use alloy::primitives::{Address, Bytes, FixedBytes, U256};
use alloy::rlp::{Decodable, Encodable, Header};
use serde::{ser::SerializeSeq, Deserialize, Serialize};
use super::utils::{hash_bytecode, BytecodeHashError};
fn serialize_bytes<S: serde::Serializer>(
bytes: &Vec<Bytes>,
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(bytes.len()))?;
for e in bytes {
seq.serialize_element(&e.0)?;
}
seq.end()
}
fn serialize_bytes_custom<S: serde::Serializer>(
bytes: &Bytes,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&bytes.0)
}
fn serialize_bytes_opt<S: serde::Serializer>(
value: &Option<Bytes>,
serializer: S,
) -> Result<S::Ok, S::Error> {
match value {
Some(bytes) => serializer.serialize_bytes(&bytes.0),
None => serializer.serialize_none(),
}
}
#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Debug, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct Eip712Meta {
pub gas_per_pubdata: U256,
#[serde(default)]
#[serde(serialize_with = "serialize_bytes")]
pub factory_deps: Vec<Bytes>,
#[serde(serialize_with = "serialize_bytes_opt")]
pub custom_signature: Option<Bytes>,
pub paymaster_params: Option<PaymasterParams>,
}
impl Eip712Meta {
pub fn factory_deps_hashes(&self) -> Result<Vec<FixedBytes<32>>, BytecodeHashError> {
let mut hashes = Vec::with_capacity(self.factory_deps.len() * 32);
for dep in &self.factory_deps {
let hash = hash_bytecode(dep)?;
hashes.push(hash.into());
}
Ok(hashes)
}
}
impl Decodable for Eip712Meta {
fn decode(buf: &mut &[u8]) -> alloy::rlp::Result<Self> {
fn opt_decode<T: Decodable>(buf: &mut &[u8]) -> alloy::rlp::Result<Option<T>> {
Ok(Decodable::decode(buf).ok()) }
let gas_per_pubdata = Decodable::decode(buf)?;
let factory_deps = Decodable::decode(buf)?;
let custom_signature = opt_decode(buf)?;
let paymaster_params = opt_decode(buf)?;
Ok(Self {
gas_per_pubdata,
factory_deps,
custom_signature,
paymaster_params,
})
}
}
impl Encodable for Eip712Meta {
fn encode(&self, out: &mut dyn alloy::rlp::BufMut) {
fn opt_encode<T>(stream: &mut dyn alloy::rlp::BufMut, value: Option<T>)
where
T: Encodable,
{
if let Some(v) = value {
v.encode(stream);
} else {
"".encode(stream);
}
}
self.gas_per_pubdata.encode(out);
self.factory_deps.encode(out);
opt_encode(out, self.custom_signature.clone());
opt_encode(out, self.paymaster_params.clone());
}
}
#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Debug, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct PaymasterParams {
pub paymaster: Address,
#[serde(serialize_with = "serialize_bytes_custom")]
pub paymaster_input: Bytes,
}
impl Decodable for PaymasterParams {
fn decode(buf: &mut &[u8]) -> alloy::rlp::Result<Self> {
let mut bytes = Header::decode_bytes(buf, true)?;
let payload_view = &mut bytes;
Ok(Self {
paymaster: dbg!(Decodable::decode(payload_view))?,
paymaster_input: dbg!(Decodable::decode(payload_view))?,
})
}
}
impl Encodable for PaymasterParams {
fn encode(&self, out: &mut dyn alloy::rlp::BufMut) {
let h = Header {
list: true,
payload_length: self.paymaster.length() + self.paymaster_input.length(),
};
h.encode(out);
self.paymaster.encode(out);
self.paymaster_input.encode(out);
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy::primitives::address;
use serde_json::json;
#[test]
fn test_bytes_get_serialized_into_vec() {
let meta = Eip712Meta {
gas_per_pubdata: U256::from(4),
factory_deps: vec![vec![1, 2].into()],
custom_signature: Some(vec![3, 4].into()),
paymaster_params: Some(PaymasterParams {
paymaster: address!("99E12239CBf8112fBB3f7Fd473d0558031abcbb5"),
paymaster_input: vec![5, 6].into(),
}),
};
let expected_json = json!({
"gasPerPubdata": "0x4",
"factoryDeps": [[1,2]],
"customSignature": [3, 4],
"paymasterParams": {
"paymaster": "0x99e12239cbf8112fbb3f7fd473d0558031abcbb5",
"paymasterInput": [5, 6],
}
});
let actual_json = serde_json::to_value(&meta).unwrap();
assert_eq!(expected_json, actual_json);
}
}