-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
740 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[package] | ||
name = "alloy-json-abi" | ||
description = "Ethereum ABI JSON file (de)serialization" | ||
keywords = ["ethereum", "abi", "serialization"] | ||
categories = ["encoding", "cryptography::cryptocurrencies"] | ||
homepage = "https://github.com/alloy-rs/core/tree/main/crates/json-abi" | ||
|
||
version.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
authors.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
exclude.workspace = true | ||
|
||
[dependencies] | ||
serde = { workspace = true, features = ["derive"] } | ||
alloy-primitives.workspace = true | ||
|
||
[dev-dependencies] | ||
serde_json.workspace = true | ||
|
||
[features] | ||
default = ["std"] | ||
std = ["serde/std", "alloy-primitives/std"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
use crate::{AbiItem, Constructor, Error, Event, Fallback, Function, Receive}; | ||
use alloc::{collections::BTreeMap, string::String, vec::Vec}; | ||
use serde::{ | ||
de::{SeqAccess, Visitor}, | ||
ser::SerializeSeq, | ||
Deserialize, Serialize, | ||
}; | ||
|
||
/// The JSON contract ABI, as specified in the [Solidity documentation][ref]. | ||
/// | ||
/// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#json | ||
#[derive(Debug, Clone, PartialEq, Eq, Default)] | ||
pub struct AbiJson { | ||
/// The constructor function. | ||
pub constructor: Option<Constructor>, | ||
/// The fallback function. | ||
pub fallback: Option<Fallback>, | ||
/// The receive function. | ||
pub receive: Option<Receive>, | ||
/// The functions, indexed by the function name | ||
pub functions: BTreeMap<String, Vec<Function>>, | ||
/// The events, indexed by the event name | ||
pub events: BTreeMap<String, Vec<Event>>, | ||
/// The errors, indexed by the error name | ||
pub errors: BTreeMap<String, Vec<Error>>, | ||
} | ||
|
||
impl AbiJson { | ||
/// The total number of items (of any type) | ||
pub fn len(&self) -> usize { | ||
self.constructor.is_some() as usize | ||
+ self.fallback.is_some() as usize | ||
+ self.receive.is_some() as usize | ||
+ self.functions.values().map(Vec::len).sum::<usize>() | ||
+ self.events.values().map(Vec::len).sum::<usize>() | ||
+ self.errors.values().map(Vec::len).sum::<usize>() | ||
} | ||
|
||
/// True if the ABI contains no items | ||
pub fn is_empty(&self) -> bool { | ||
self.len() == 0 | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for AbiJson { | ||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<AbiJson, D::Error> { | ||
deserializer.deserialize_seq(AbiJsonVisitor) | ||
} | ||
} | ||
|
||
impl Serialize for AbiJson { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: serde::Serializer, | ||
{ | ||
let mut seq = serializer.serialize_seq(Some(self.len()))?; | ||
|
||
if let Some(constructor) = &self.constructor { | ||
seq.serialize_element(constructor)?; | ||
} | ||
if let Some(fallback) = &self.fallback { | ||
seq.serialize_element(fallback)?; | ||
} | ||
if let Some(receive) = &self.receive { | ||
seq.serialize_element(receive)?; | ||
} | ||
|
||
self.functions | ||
.values() | ||
.flatten() | ||
.try_for_each(|f| seq.serialize_element(f))?; | ||
|
||
self.events | ||
.values() | ||
.flatten() | ||
.try_for_each(|e| seq.serialize_element(&e))?; | ||
|
||
self.errors | ||
.values() | ||
.flatten() | ||
.try_for_each(|e| seq.serialize_element(&e))?; | ||
|
||
seq.end() | ||
} | ||
} | ||
|
||
struct AbiJsonVisitor; | ||
|
||
impl<'de> Visitor<'de> for AbiJsonVisitor { | ||
type Value = AbiJson; | ||
|
||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
write!(formatter, "a valid ABI JSON file") | ||
} | ||
|
||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> | ||
where | ||
A: SeqAccess<'de>, | ||
{ | ||
let mut json_file = AbiJson::default(); | ||
|
||
while let Some(item) = seq.next_element()? { | ||
match item { | ||
AbiItem::Constructor(c) => { | ||
if json_file.constructor.is_some() { | ||
return Err(serde::de::Error::duplicate_field("constructor")) | ||
} | ||
json_file.constructor = Some(c.into_owned()); | ||
} | ||
AbiItem::Fallback(f) => { | ||
if json_file.fallback.is_some() { | ||
return Err(serde::de::Error::duplicate_field("fallback")) | ||
} | ||
json_file.fallback = Some(f.into_owned()); | ||
} | ||
AbiItem::Receive(r) => { | ||
if json_file.receive.is_some() { | ||
return Err(serde::de::Error::duplicate_field("receive")) | ||
} | ||
json_file.receive = Some(r.into_owned()); | ||
} | ||
AbiItem::Function(f) => { | ||
json_file | ||
.functions | ||
.entry(f.name.clone()) | ||
.or_default() | ||
.push(f.into_owned()); | ||
} | ||
AbiItem::Event(e) => { | ||
json_file | ||
.events | ||
.entry(e.name.clone()) | ||
.or_default() | ||
.push(e.into_owned()); | ||
} | ||
AbiItem::Error(e) => { | ||
json_file | ||
.errors | ||
.entry(e.name.clone()) | ||
.or_default() | ||
.push(e.into_owned()); | ||
} | ||
} | ||
} | ||
Ok(json_file) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use crate::param::Param; | ||
use alloc::{string::String, vec::Vec}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// A Solidity Event parameter. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
pub struct SimpleEventParam { | ||
/// The name of the parameter. | ||
pub name: String, | ||
/// The Solidity type of the parameter. | ||
#[serde(rename = "type")] | ||
pub ty: String, | ||
/// Whether the parameter is indexed. Indexed parameters have their | ||
///value, or the hash of their value, stored in the log topics. | ||
pub indexed: bool, | ||
/// The internal type of the parameter. This type represents the type that | ||
/// the author of the solidity contract specified. E.g. for a contract, this | ||
/// will be `contract MyContract` while the `type` field will be `address`. | ||
#[serde(rename = "internalType")] | ||
pub internal_type: String, | ||
} | ||
|
||
/// JSON representation of a complex event parameter. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
pub struct ComplexEventParam { | ||
/// The name of the parameter. | ||
pub name: String, | ||
/// The Solidity type of the parameter. | ||
#[serde(rename = "type")] | ||
pub ty: String, | ||
/// Whether the parameter is indexed. Indexed parameters have their | ||
/// value, or the hash of their value, stored in the log topics. | ||
pub indexed: bool, | ||
/// A list of the parameter's components, in order. This is a tuple | ||
/// definition, and sub-components will NOT have an `indexed` field. | ||
pub components: Vec<Param>, | ||
/// The internal type of the parameter. This type represents the type that | ||
/// the author of the solidity contract specified. E.g. for a contract, this | ||
/// will be `contract MyContract` while the `type` field will be `address`. | ||
#[serde(rename = "internalType")] | ||
pub internal_type: String, | ||
} | ||
|
||
/// A Solidity Event parameter. Event parameters are distinct from function | ||
/// parameters in that they have an `indexed` field. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
#[serde(untagged)] | ||
pub enum EventParam { | ||
/// [`ComplexEventParam`] variant | ||
Complex(ComplexEventParam), | ||
/// [`SimpleEventParam`] variant | ||
Simple(SimpleEventParam), | ||
} |
Oops, something went wrong.