Skip to content

Commit

Permalink
feat(7702): bincode compatibility (#8)
Browse files Browse the repository at this point in the history
* feat(7702): bincode compatibility

* improve comments

* make it pub
  • Loading branch information
shekhirin authored Sep 30, 2024
1 parent 2ca16e0 commit efff991
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/eip7702/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ alloy-rlp = { workspace = true, features = ["derive"] }

# serde
serde = { workspace = true, optional = true }
serde_with = { version = "3", optional = true }

# arbitrary
arbitrary = { workspace = true, features = ["derive"], optional = true }
Expand All @@ -32,12 +33,15 @@ k256 = { workspace = true, optional = true }
rand = { workspace = true, optional = true }

[dev-dependencies]
bincode = "1.3"
rand = "0.8"
serde_json.workspace = true

[features]
default = ["std"]
std = ["alloy-primitives/std", "alloy-rlp/std", "serde?/std"]
serde = ["dep:serde", "alloy-primitives/serde"]
serde-bincode-compat = ["serde_with"]
arbitrary = [
"std",
"dep:arbitrary",
Expand Down
98 changes: 98 additions & 0 deletions crates/eip7702/src/auth_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,101 @@ mod tests {
let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
}
}

/// Bincode-compatible [`SignedAuthorization`] serde implementation.
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub(super) mod serde_bincode_compat {
use alloc::borrow::Cow;
use alloy_primitives::Signature;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{DeserializeAs, SerializeAs};

use crate::Authorization;

/// Bincode-compatible [`super::SignedAuthorization`] serde implementation.
///
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
/// ```rust
/// use alloy_eip7702::{serde_bincode_compat, SignedAuthorization};
/// use serde::{Deserialize, Serialize};
/// use serde_with::serde_as;
///
/// #[serde_as]
/// #[derive(Serialize, Deserialize)]
/// struct Data {
/// #[serde_as(as = "serde_bincode_compat::SignedAuthorization")]
/// authorization: SignedAuthorization,
/// }
/// ```
#[derive(Debug, Serialize, Deserialize)]
pub struct SignedAuthorization<'a> {
inner: Cow<'a, Authorization>,
signature: Cow<'a, Signature>,
}

impl<'a> From<&'a super::SignedAuthorization> for SignedAuthorization<'a> {
fn from(value: &'a super::SignedAuthorization) -> Self {
Self { inner: Cow::Borrowed(&value.inner), signature: Cow::Borrowed(&value.signature) }
}
}

impl<'a> From<SignedAuthorization<'a>> for super::SignedAuthorization {
fn from(value: SignedAuthorization<'a>) -> Self {
Self { inner: value.inner.into_owned(), signature: value.signature.into_owned() }
}
}

impl<'a> SerializeAs<super::SignedAuthorization> for SignedAuthorization<'a> {
fn serialize_as<S>(
source: &super::SignedAuthorization,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SignedAuthorization::from(source).serialize(serializer)
}
}

impl<'de> DeserializeAs<'de, super::SignedAuthorization> for SignedAuthorization<'de> {
fn deserialize_as<D>(deserializer: D) -> Result<super::SignedAuthorization, D::Error>
where
D: Deserializer<'de>,
{
SignedAuthorization::deserialize(deserializer).map(Into::into)
}
}

#[cfg(all(test, feature = "k256"))]
mod tests {
use arbitrary::Arbitrary;
use rand::Rng;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use super::super::{serde_bincode_compat, SignedAuthorization};

#[test]
fn test_signed_authorization_bincode_roundtrip() {
#[serde_as]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Data {
#[serde_as(as = "serde_bincode_compat::SignedAuthorization")]
authorization: SignedAuthorization,
}

let mut bytes = [0u8; 1024];
rand::thread_rng().fill(bytes.as_mut_slice());
let data = Data {
authorization: SignedAuthorization::arbitrary(&mut arbitrary::Unstructured::new(
&bytes,
))
.unwrap(),
};

let encoded = bincode::serialize(&data).unwrap();
let decoded: Data = bincode::deserialize(&encoded).unwrap();
assert_eq!(decoded, data);
}
}
}
12 changes: 12 additions & 0 deletions crates/eip7702/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ mod auth_list;
pub use auth_list::*;

pub mod constants;

/// Bincode-compatible serde implementations for EIP-7702 types.
///
/// `bincode` crate doesn't work with `#[serde(flatten)]` attribute, but some of the EIP-7702 types
/// require flattenning for RPC compatibility. This module makes so that all fields are
/// non-flattenned.
///
/// Read more: <https://github.com/bincode-org/bincode/issues/167#issuecomment-897629039>
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub mod serde_bincode_compat {
pub use super::auth_list::serde_bincode_compat::*;
}

0 comments on commit efff991

Please sign in to comment.