Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(json-abi): add full_signature #480

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion crates/json-abi/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,21 @@ impl Function {
/// This is the same as [`signature`](Self::signature), but also includes
/// the output types.
#[inline]
pub fn signature_full(&self) -> String {
pub fn signature_with_outputs(&self) -> String {
signature(&self.name, &self.inputs, Some(&self.outputs))
}

/// Returns this function's full signature including names of params:
/// `function $name($($inputs $names),*) state_mutability returns ($($outputs $names),*)`.
///
/// This is a full human-readable string, including all parameter names, any optional modifiers
/// (e.g. view, payable, pure) and white-space to aid in human readability. This is useful for
/// storing a string which can still fully reconstruct the original Fragment
#[inline]
pub fn full_signature(&self) -> String {
BrennerSpear marked this conversation as resolved.
Show resolved Hide resolved
full_signature(&self.name, &self.inputs, Some(&self.outputs), self.state_mutability)
}

/// Computes this error's selector: `keccak256(self.signature())[..4]`
#[inline]
pub fn selector(&self) -> Selector {
Expand Down Expand Up @@ -563,6 +574,17 @@ impl Event {
event_signature(&self.name, &self.inputs)
}

/// Returns this event's full signature
/// `event $name($($inputs indexed $names),*)`.
///
/// This is a full human-readable string, including all parameter names, any optional modifiers
/// (e.g. indexed) and white-space to aid in human readability. This is useful for
/// storing a string which can still fully reconstruct the original Fragment
#[inline]
pub fn full_signature(&self) -> String {
event_full_signature(&self.name, &self.inputs)
}

/// Computes this event's selector: `keccak256(self.signature())`
#[inline]
pub fn selector(&self) -> B256 {
Expand Down
32 changes: 32 additions & 0 deletions crates/json-abi/src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,22 @@ impl Param {
}
}

/// Formats the canonical type of this parameter into the given string including then names of
/// the params.
#[inline]
pub fn full_selector_type_raw(&self, s: &mut String) {
if self.components.is_empty() {
s.push_str(&self.ty);
} else {
s.push_str("tuple");
crate::utils::full_signature_raw(&self.components, s);
// checked during deserialization, but might be invalid from a user
if let Some(suffix) = self.ty.strip_prefix("tuple") {
s.push_str(suffix);
}
}
}

/// Returns the canonical type of this parameter.
///
/// This is used to encode the preimage of a function or error selector.
Expand Down Expand Up @@ -456,6 +472,22 @@ impl EventParam {
}
}

/// Formats the canonical type of this parameter into the given string including then names of
/// the params.
#[inline]
pub fn full_selector_type_raw(&self, s: &mut String) {
if self.components.is_empty() {
s.push_str(&self.ty);
} else {
s.push_str("tuple");
crate::utils::full_signature_raw(&self.components, s);
// checked during deserialization, but might be invalid from a user
if let Some(suffix) = self.ty.strip_prefix("tuple") {
s.push_str(suffix);
}
}
}

/// Returns the canonical type of this parameter.
///
/// This is used to encode the preimage of the event selector.
Expand Down
237 changes: 233 additions & 4 deletions crates/json-abi/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{EventParam, Param};
use alloc::{string::String, vec::Vec};
use crate::{EventParam, Param, StateMutability};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use alloy_primitives::Selector;
use core::{fmt::Write, num::NonZeroUsize};
use parser::{ParameterSpecifier, TypeSpecifier, TypeStem};
Expand Down Expand Up @@ -33,6 +36,45 @@ macro_rules! signature {
};
}

macro_rules! event_full_signature {
($inputs:expr, $preimage:expr) => {
$preimage.push('(');
for (i, input) in $inputs.iter().enumerate() {
if i > 0 {
$preimage.push(',');
$preimage.push(' ');
}
input.full_selector_type_raw($preimage);
if input.indexed {
$preimage.push_str(" indexed");
}
if !input.name.is_empty() {
$preimage.push(' ');
$preimage.push_str(&input.name);
}
}
$preimage.push(')');
};
}

macro_rules! full_signature {
($inputs:expr, $preimage:expr) => {
$preimage.push('(');
for (i, input) in $inputs.iter().enumerate() {
if i > 0 {
$preimage.push(',');
$preimage.push(' ');
}
input.full_selector_type_raw($preimage);
if !input.name.is_empty() {
$preimage.push(' ');
$preimage.push_str(&input.name);
}
}
$preimage.push(')');
};
}

/// `$name($($inputs),*)($($outputs),*)`
pub(crate) fn signature(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
let parens = 2 + outputs.is_some() as usize * 2;
Expand All @@ -47,11 +89,47 @@ pub(crate) fn signature(name: &str, inputs: &[Param], outputs: Option<&[Param]>)
preimage
}

pub(crate) fn full_signature(
name: &str,
inputs: &[Param],
outputs: Option<&[Param]>,
state_mutability: StateMutability,
) -> String {
let parens = 2 + outputs.is_some() as usize * 2;
let n_outputs = outputs.map(<[_]>::len).unwrap_or(0);
let mut state_mutability_str = format!(" {}", state_mutability.as_str().unwrap_or_default());
if state_mutability_str.trim().is_empty() {
state_mutability_str = "".to_string();
}
let cap = "function ".len()
+ name.len()
+ parens
+ (inputs.len() + n_outputs) * PARAM
+ state_mutability_str.len();
let mut preimage = String::with_capacity(cap);

preimage.push_str("function ");
preimage.push_str(name);
full_signature_raw(inputs, &mut preimage);
preimage.push_str(&state_mutability_str);
if let Some(outputs) = outputs {
if !outputs.is_empty() {
preimage.push_str(" returns ");
full_signature_raw(outputs, &mut preimage);
}
}
preimage
}

/// `($($params),*)`
pub(crate) fn signature_raw(params: &[Param], preimage: &mut String) {
signature!(params, preimage);
}

pub(crate) fn full_signature_raw(params: &[Param], preimage: &mut String) {
full_signature!(params, preimage);
}

/// `$name($($inputs),*)`
pub(crate) fn event_signature(name: &str, inputs: &[EventParam]) -> String {
let mut preimage = String::with_capacity(name.len() + 2 + inputs.len() * PARAM);
Expand All @@ -60,6 +138,16 @@ pub(crate) fn event_signature(name: &str, inputs: &[EventParam]) -> String {
preimage
}

/// `$name($($inputs indexed names),*)`
pub(crate) fn event_full_signature(name: &str, inputs: &[EventParam]) -> String {
let mut preimage =
String::with_capacity("event ".len() + name.len() + 2 + inputs.len() * PARAM);
preimage.push_str("event ");
preimage.push_str(name);
event_full_signature!(inputs, &mut preimage);
preimage
}

/// `keccak256(preimage)[..4]`
pub(crate) fn selector(preimage: &str) -> Selector {
// SAFETY: splitting an array
Expand Down Expand Up @@ -152,12 +240,20 @@ mod tests {
}

fn eparam(kind: &str) -> EventParam {
eparam_with_indexed(kind, "param", false)
}

fn eparam2(kind: &str, name: &str, indexed: bool) -> EventParam {
eparam_with_indexed(kind, name, indexed)
}

fn eparam_with_indexed(kind: &str, name: &str, indexed: bool) -> EventParam {
EventParam {
name: "param".into(),
name: name.into(),
ty: kind.into(),
internal_type: None,
components: vec![],
indexed: false,
indexed,
}
}

Expand All @@ -166,6 +262,28 @@ mod tests {
crate::Param { name: "param".into(), ty: "tuple".into(), internal_type: None, components }
}

fn full_signature_raw(
name: &str,
inputs: &[Param],
outputs: Option<&[Param]>,
state_mutability: StateMutability,
) -> String {
full_signature(name, inputs, outputs, state_mutability)
}

fn full_signature_np(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
full_signature_raw(name, inputs, outputs, StateMutability::NonPayable)
}

fn full_signature_with_sm(
name: &str,
inputs: &[Param],
outputs: Option<&[Param]>,
state_mutability: StateMutability,
) -> String {
full_signature_raw(name, inputs, outputs, state_mutability)
}

#[test]
fn test_signature() {
assert_eq!(signature("foo", &[], None), "foo()");
Expand All @@ -187,13 +305,124 @@ mod tests {
);
}

#[test]
fn test_full_signature() {
assert_eq!(full_signature_np("foo", &[], None), "function foo()");
assert_eq!(full_signature_np("foo", &[], Some(&[])), "function foo()");
assert_eq!(full_signature_np("bar", &[param2("bool", "")], None), "function bar(bool)");
assert_eq!(
full_signature_np("bar", &[param2("bool", "")], Some(&[param2("bool", "")])),
"function bar(bool) returns (bool)"
);
assert_eq!(
full_signature_np(
"foo",
&[param2("address", "asset"), param2("uint256", "amount")],
None
),
"function foo(address asset, uint256 amount)"
);
assert_eq!(
full_signature_np(
"foo",
&[param2("address", "asset")],
Some(&[param2("uint256", "amount")])
),
"function foo(address asset) returns (uint256 amount)"
);

let components = vec![
param2("address", "pool"),
param2("uint256", "tokenInParam"),
param2("uint256", "tokenOutParam"),
param2("uint256", "maxPrice"),
];
let swaps =
Param { name: "swaps".into(), ty: "tuple[]".into(), internal_type: None, components };

assert_eq!(
full_signature_with_sm(
"batchSwapExactIn",
&[
swaps,
param2("address", "tokenIn"),
param2("address", "tokenOut"),
param2("uint256", "totalAmountIn"),
param2("uint256", "minTotalAmountOut"),
],
Some(&[param2("uint256", "totalAmountOut")]),
StateMutability::Payable,
),
"function batchSwapExactIn(tuple(address pool, uint256 tokenInParam, uint256 tokenOutParam, uint256 maxPrice)[] swaps, address tokenIn, address tokenOut, uint256 totalAmountIn, uint256 minTotalAmountOut) payable returns (uint256 totalAmountOut)"
);

assert_eq!(
full_signature_with_sm(
"name",
&[],
Some(&[param2("string", "")]),
StateMutability::View
),
"function name() view returns (string)"
);

assert_eq!(
full_signature_with_sm(
"calculateHash",
&[param2("address[]", "_addresses")],
Some(&[param2("bytes32", "")]),
StateMutability::Pure,
),
"function calculateHash(address[] _addresses) pure returns (bytes32)"
);
}

#[test]
fn test_event_signature() {
assert_eq!(event_signature("foo", &[]), "foo()");
assert_eq!(event_signature("foo", &[eparam("bool")]), "foo(bool)");
assert_eq!(event_signature("foo", &[eparam("bool"), eparam("string")]), "foo(bool,string)");
}

#[test]
fn test_event_full_signature() {
assert_eq!(event_full_signature("foo", &[]), "event foo()");
assert_eq!(
event_full_signature("foo", &[eparam2("bool", "confirmed", true)]),
"event foo(bool indexed confirmed)"
);
assert_eq!(
event_full_signature(
"foo",
&[eparam2("bool", "confirmed", true), eparam2("string", "message", false)]
),
"event foo(bool indexed confirmed, string message)"
);

let components = vec![
param2("uint256", "amount"),
param2("uint256", "startTime"),
param2("uint256", "interval"),
];
let info = EventParam {
name: "info".into(),
ty: "tuple".into(),
internal_type: None,
components,
indexed: false,
};
assert_eq!(
event_full_signature(
"SetupDirectDebit",
&[
eparam2("address", "debtor", true),
eparam2("address", "receiver", true),
info,
] ),
"event SetupDirectDebit(address indexed debtor, address indexed receiver, tuple(uint256 amount, uint256 startTime, uint256 interval) info)"
);
}

#[test]
fn test_item_parse() {
assert_eq!(parse_sig::<true>("foo()"), Ok(("foo".into(), vec![], vec![], false)));
Expand Down