use crate::address::Address;
use crate::error::Error;
use num256::{Int256, Uint256};
use sha3::{Digest, Keccak256};
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum AbiToken {
Uint(Uint256),
Int(Int256),
Address(Address),
Bool(bool),
String(String),
FixedString(String),
Bytes(Vec<u8>),
UnboundedBytes(Vec<u8>),
Dynamic(Vec<AbiToken>),
Struct(Vec<AbiToken>),
}
pub enum SerializedToken {
Static([u8; 32]),
Dynamic(Vec<u8>),
}
impl SerializedToken {
fn as_static_ref(&self) -> Option<&[u8; 32]> {
match *self {
SerializedToken::Static(ref data) => Some(data),
_ => None,
}
}
}
impl AbiToken {
pub fn serialize(&self) -> SerializedToken {
match *self {
AbiToken::Uint(ref value) => {
assert!(value.bits() <= 256);
let bytes = value.to_be_bytes();
let mut res: [u8; 32] = Default::default();
res[32 - bytes.len()..].copy_from_slice(&bytes);
SerializedToken::Static(res)
}
AbiToken::Int(ref value) => {
let bytes = value.to_be_bytes();
let mut res: [u8; 32] = Default::default();
res.copy_from_slice(&bytes);
SerializedToken::Static(res)
}
AbiToken::Bool(value) => {
let mut res: [u8; 32] = Default::default();
res[31] = value as u8;
SerializedToken::Static(res)
}
AbiToken::Dynamic(ref tokens) => {
let mut wtr = vec![];
let prefix: AbiToken = (tokens.len() as u64).into();
wtr.extend(prefix.serialize().as_static_ref().unwrap());
wtr.extend(encode_tokens(tokens));
SerializedToken::Dynamic(wtr)
}
AbiToken::Struct(ref tokens) => SerializedToken::Dynamic(encode_tokens(tokens)),
AbiToken::UnboundedBytes(ref v) => {
let mut wtr = vec![];
let prefix: AbiToken = (v.len() as u64).into();
wtr.extend(prefix.serialize().as_static_ref().unwrap());
wtr.extend(v);
let pad_right = (((v.len() - 1) / 32) + 1) * 32;
wtr.extend(vec![0x00u8; pad_right - v.len()]);
SerializedToken::Dynamic(wtr)
}
AbiToken::String(ref s) => {
let mut wtr = vec![];
let prefix: AbiToken = (s.len() as u64).into();
wtr.extend(prefix.serialize().as_static_ref().unwrap());
wtr.extend(s.as_bytes());
let pad_right = (((s.len() - 1) / 32) + 1) * 32;
wtr.extend(vec![0x00u8; pad_right - s.len()]);
SerializedToken::Dynamic(wtr)
}
AbiToken::FixedString(ref s) => {
let value = s.to_string().as_bytes().to_vec();
assert!(value.len() <= 32);
let mut wtr: [u8; 32] = Default::default();
wtr[0..value.len()].copy_from_slice(&value[..]);
SerializedToken::Static(wtr)
}
AbiToken::Bytes(ref value) => {
assert!(value.len() <= 32);
let mut wtr: [u8; 32] = Default::default();
wtr[0..value.len()].copy_from_slice(&value[..]);
SerializedToken::Static(wtr)
}
AbiToken::Address(ref address) => {
let mut wtr: [u8; 32] = Default::default();
let bytes = address.as_bytes();
wtr[32 - bytes.len()..].copy_from_slice(bytes);
SerializedToken::Static(wtr)
}
}
}
}
impl From<u8> for AbiToken {
fn from(v: u8) -> AbiToken {
AbiToken::Uint(Uint256::from(v))
}
}
impl From<u16> for AbiToken {
fn from(v: u16) -> AbiToken {
AbiToken::Uint(Uint256::from(v))
}
}
impl From<u32> for AbiToken {
fn from(v: u32) -> AbiToken {
AbiToken::Uint(Uint256::from(v))
}
}
impl From<u64> for AbiToken {
fn from(v: u64) -> AbiToken {
AbiToken::Uint(Uint256::from(v))
}
}
impl From<u128> for AbiToken {
fn from(v: u128) -> AbiToken {
AbiToken::Uint(Uint256::from(v))
}
}
impl From<i8> for AbiToken {
fn from(v: i8) -> AbiToken {
AbiToken::Int(Int256::from(v))
}
}
impl From<i16> for AbiToken {
fn from(v: i16) -> AbiToken {
AbiToken::Int(Int256::from(v))
}
}
impl From<i32> for AbiToken {
fn from(v: i32) -> AbiToken {
AbiToken::Int(Int256::from(v))
}
}
impl From<i64> for AbiToken {
fn from(v: i64) -> AbiToken {
AbiToken::Int(Int256::from(v))
}
}
impl From<i128> for AbiToken {
fn from(v: i128) -> AbiToken {
AbiToken::Int(Int256::from(v))
}
}
impl From<bool> for AbiToken {
fn from(v: bool) -> AbiToken {
AbiToken::Bool(v)
}
}
impl From<Vec<u8>> for AbiToken {
fn from(v: Vec<u8>) -> AbiToken {
AbiToken::UnboundedBytes(v)
}
}
impl From<Vec<u16>> for AbiToken {
fn from(v: Vec<u16>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<u32>> for AbiToken {
fn from(v: Vec<u32>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<u64>> for AbiToken {
fn from(v: Vec<u64>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<u128>> for AbiToken {
fn from(v: Vec<u128>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<i8>> for AbiToken {
fn from(v: Vec<i8>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<i16>> for AbiToken {
fn from(v: Vec<i16>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<i32>> for AbiToken {
fn from(v: Vec<i32>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<i64>> for AbiToken {
fn from(v: Vec<i64>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<i128>> for AbiToken {
fn from(v: Vec<i128>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Address> for AbiToken {
fn from(v: Address) -> AbiToken {
AbiToken::Address(v)
}
}
impl From<&Address> for AbiToken {
fn from(v: &Address) -> AbiToken {
AbiToken::Address(*v)
}
}
impl<'a> From<&'a str> for AbiToken {
fn from(v: &'a str) -> AbiToken {
AbiToken::String(v.into())
}
}
impl From<Vec<Address>> for AbiToken {
fn from(v: Vec<Address>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<Vec<AbiToken>> for AbiToken {
fn from(v: Vec<AbiToken>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<&[Address]> for AbiToken {
fn from(v: &[Address]) -> AbiToken {
AbiToken::Dynamic(v.iter().map(Into::into).collect())
}
}
impl From<Uint256> for AbiToken {
fn from(v: Uint256) -> AbiToken {
AbiToken::Uint(v)
}
}
impl From<&Uint256> for AbiToken {
fn from(v: &Uint256) -> AbiToken {
AbiToken::Uint(*v)
}
}
impl From<Vec<Uint256>> for AbiToken {
fn from(v: Vec<Uint256>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<&[Uint256]> for AbiToken {
fn from(v: &[Uint256]) -> AbiToken {
AbiToken::Dynamic(v.iter().map(Into::into).collect())
}
}
impl From<Int256> for AbiToken {
fn from(v: Int256) -> AbiToken {
AbiToken::Int(v)
}
}
impl From<&Int256> for AbiToken {
fn from(v: &Int256) -> AbiToken {
AbiToken::Int(*v)
}
}
impl From<Vec<Int256>> for AbiToken {
fn from(v: Vec<Int256>) -> AbiToken {
AbiToken::Dynamic(v.into_iter().map(Into::into).collect())
}
}
impl From<&[Int256]> for AbiToken {
fn from(v: &[Int256]) -> AbiToken {
AbiToken::Dynamic(v.iter().map(Into::into).collect())
}
}
pub fn derive_signature(data: &str) -> Result<[u8; 32], Error> {
if data.contains(' ') {
return Err(Error::InvalidCallError(
"No spaces are allowed in call names".to_string(),
));
} else if !(data.contains('(') && data.contains(')')) {
return Err(Error::InvalidCallError(
"Mismatched call braces".to_string(),
));
}
let digest = Keccak256::digest(data.as_bytes());
let mut result: [u8; 32] = Default::default();
result.copy_from_slice(&digest);
Ok(result)
}
pub fn derive_method_id(signature: &str) -> Result<[u8; 4], Error> {
let digest = derive_signature(signature)?;
let mut result: [u8; 4] = Default::default();
result.copy_from_slice(&digest[0..4]);
Ok(result)
}
pub fn encode_tokens(tokens: &[AbiToken]) -> Vec<u8> {
let mut res = Vec::new();
let mut dynamic_data: Vec<Vec<u8>> = Vec::new();
for token in tokens.iter() {
match token.serialize() {
SerializedToken::Static(data) => res.extend(data),
SerializedToken::Dynamic(data) => {
let dynamic_offset = dynamic_data
.iter()
.map(|data| data.len() as u64)
.fold(tokens.len() as u64 * 32, |r, v| r + v);
dynamic_data.push(data);
if !is_static_struct_array(tokens) {
let offset: AbiToken = dynamic_offset.into();
match offset.serialize() {
SerializedToken::Static(bytes) => res.extend(bytes),
_ => panic!("Offset token is expected to be static"),
}
}
}
}
}
for data in dynamic_data.iter() {
res.extend(&data[..]);
}
res
}
pub fn get_hash(bytes: &[u8]) -> [u8; 32] {
Keccak256::digest(bytes).into()
}
pub fn encode_call(sig: &str, tokens: &[AbiToken]) -> Result<Vec<u8>, Error> {
let mut wtr = vec![];
wtr.extend(derive_method_id(sig)?);
let args_count = get_args_count(sig)?;
let token_count = get_tokens_count(tokens);
if args_count != token_count {
return Err(Error::InvalidCallError(format!(
"Function call contains {args_count} arguments, but {token_count} provided"
)));
}
wtr.extend(encode_tokens(tokens));
Ok(wtr)
}
fn get_tokens_count(tokens: &[AbiToken]) -> usize {
let mut count = 0;
for token in tokens {
match token {
AbiToken::Struct(v) => count += get_tokens_count(v),
AbiToken::Dynamic(d) => {
if is_struct_array(d) && !d.is_empty() {
count += get_tokens_count(&[d[0].clone()])
} else {
count += 1
}
}
_ => count += 1,
}
}
count
}
fn is_struct_array(input: &[AbiToken]) -> bool {
if input.is_empty() {
return false;
}
for t in input {
match t {
AbiToken::Struct(_) => {}
_ => return false,
}
}
true
}
fn is_static_struct_array(input: &[AbiToken]) -> bool {
if input.is_empty() {
return false;
}
for t in input {
match t {
AbiToken::Struct(v) => {
for t in v {
if let SerializedToken::Dynamic(_) = t.serialize() {
return false;
}
}
}
_ => return false,
}
}
true
}
fn get_args_count(sig: &str) -> Result<usize, Error> {
if sig.matches('(').count() != sig.matches(')').count() {
return Err(Error::InvalidCallError(
"Mismatched call braces".to_string(),
));
}
let args = sig.split(|ch| ch == '(' || ch == ')');
let mut num_args = 0;
for substring in args {
let substring = substring.trim_matches(|c| c == ']' || c == '[');
let substring = substring.trim_matches(',');
let substring = substring.trim();
if !substring.is_empty() {
num_args += substring.split(',').count();
}
}
Ok(num_args - 1)
}
#[cfg(test)]
mod tests {
use num_traits::Bounded;
use super::*;
use crate::utils::hex_str_to_bytes;
#[test]
fn derive_event_signature() {
use crate::utils::bytes_to_hex_str;
let derived = derive_signature("HelloWorld(string)").unwrap();
assert_eq!(
bytes_to_hex_str(&derived),
"86066750c0fd4457fd16f79750914fbd72db952f2ff0a7b5c6a2a531bc15ce2c"
);
let derived = derive_signature("Transfer(address,address,uint256)").unwrap();
assert_eq!(
bytes_to_hex_str(&derived),
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
);
let derived = derive_signature("Approval(address,address,uint256)").unwrap();
assert_eq!(
bytes_to_hex_str(&derived),
"8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
);
}
#[test]
fn derive_baz() {
use crate::utils::bytes_to_hex_str;
assert_eq!(
bytes_to_hex_str(&derive_method_id("baz(uint32,bool)").unwrap()),
"cdcd77c0"
);
}
#[test]
fn derive_bar() {
use crate::utils::bytes_to_hex_str;
assert_eq!(
bytes_to_hex_str(&derive_method_id("bar(bytes3[2])").unwrap()),
"fce353f6"
);
}
#[test]
fn derive_sam() {
use crate::utils::bytes_to_hex_str;
assert_eq!(
bytes_to_hex_str(&derive_method_id("sam(bytes,bool,uint256[])").unwrap()),
"a5643bf2"
);
}
#[test]
fn derive_complex_signatures() {
use crate::utils::bytes_to_hex_str;
assert_eq!(
bytes_to_hex_str(&derive_method_id("dummyUpdateValset(address[])").unwrap()),
"fd9b9103"
);
assert_eq!(
bytes_to_hex_str(&derive_method_id("dummyUpdateValset(address[],uint256[])").unwrap()),
"711ca6ac"
);
assert_eq!(bytes_to_hex_str(&derive_method_id("updateValset((address[],uint256[],uint256,uint256,address),(address[],uint256[],uint256,uint256,address),(uint8,bytes32,bytes32)[])").unwrap()), "aca6b1c1");
assert_eq!(bytes_to_hex_str(&derive_method_id("submitLogicCall((address[],uint256[],uint256,uint256,address),(uint8,bytes32,bytes32)[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))").unwrap()), "6941db93");
}
#[test]
fn derive_f() {
use crate::utils::bytes_to_hex_str;
assert_eq!(
bytes_to_hex_str(&derive_method_id("f(uint256,uint32[],bytes10,bytes)").unwrap()),
"8be65246"
);
}
#[test]
fn derive_function_with_args() {
encode_call("f()", &[]).unwrap();
encode_call("f(uint256)", &["66u64".into()]).unwrap();
encode_call("f(uint256,uint256)", &["66u64".into(), "66u64".into()]).unwrap();
encode_call(
"f(uint256,uint256,uint256)",
&["66u64".into(), "66u64".into(), "66u64".into()],
)
.unwrap();
}
#[test]
fn attempt_to_derive_invalid_function_signatures() {
assert!(derive_method_id("dummyUpdateValset( address[])").is_err());
assert!(derive_method_id("dummyUpdateValsetaddress[],uint256[])").is_err());
assert!(encode_call("dummyUpdateValset(address[],uint256[])", &["66u64".into()]).is_err());
}
#[test]
fn encode_simple() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&[69u32.into(), true.into()]);
assert_eq!(
bytes_to_hex_str(&result),
concat!(
"0000000000000000000000000000000000000000000000000000000000000045",
"0000000000000000000000000000000000000000000000000000000000000001"
)
);
}
#[test]
fn encode_sam() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&["dave".into(), true.into(), vec![1u32, 2u32, 3u32].into()]);
assert!(result.len() % 8 == 0);
assert_eq!(
bytes_to_hex_str(&result),
concat![
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000001",
"00000000000000000000000000000000000000000000000000000000000000a0",
"0000000000000000000000000000000000000000000000000000000000000004",
"6461766500000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000003",
]
);
}
#[test]
fn encode_f() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&[
0x123u32.into(),
vec![0x456u32, 0x789u32].into(),
AbiToken::Bytes(b"1234567890".to_vec()),
"Hello, world!".into(),
]);
assert!(result.len() % 8 == 0);
assert_eq!(
result[..]
.chunks(32)
.map(bytes_to_hex_str)
.collect::<Vec<String>>(),
vec![
"0000000000000000000000000000000000000000000000000000000000000123".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000080".to_owned(),
"3132333435363738393000000000000000000000000000000000000000000000".to_owned(),
"00000000000000000000000000000000000000000000000000000000000000e0".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000002".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000456".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000789".to_owned(),
"000000000000000000000000000000000000000000000000000000000000000d".to_owned(),
"48656c6c6f2c20776f726c642100000000000000000000000000000000000000".to_owned(),
]
);
}
#[test]
fn encode_f_with_real_unbounded_bytes() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&[
0x123u32.into(),
vec![0x456u32, 0x789u32].into(),
AbiToken::Bytes(b"1234567890".to_vec()),
b"Hello, world!".to_vec().into(),
]);
assert!(result.len() % 8 == 0);
assert_eq!(
result[..]
.chunks(32)
.map(bytes_to_hex_str)
.collect::<Vec<String>>(),
vec![
"0000000000000000000000000000000000000000000000000000000000000123".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000080".to_owned(),
"3132333435363738393000000000000000000000000000000000000000000000".to_owned(),
"00000000000000000000000000000000000000000000000000000000000000e0".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000002".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000456".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000789".to_owned(),
"000000000000000000000000000000000000000000000000000000000000000d".to_owned(),
"48656c6c6f2c20776f726c642100000000000000000000000000000000000000".to_owned(),
]
);
}
#[test]
fn encode_address() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&["0x00000000000000000000000000000000deadbeef"
.parse::<Address>()
.expect("Unable to parse address")
.into()]);
assert!(result.len() % 8 == 0);
assert_eq!(
result[..]
.chunks(32)
.map(bytes_to_hex_str)
.collect::<Vec<String>>(),
vec!["00000000000000000000000000000000000000000000000000000000deadbeef".to_owned(),]
);
}
#[test]
fn encode_dynamic_only() {
use crate::utils::bytes_to_hex_str;
let result = encode_tokens(&["foo".into(), "bar".into()]);
assert!(result.len() % 8 == 0);
assert_eq!(
result[..]
.chunks(32)
.map(bytes_to_hex_str)
.collect::<Vec<String>>(),
vec![
"0000000000000000000000000000000000000000000000000000000000000040".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000080".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000003".to_owned(),
"666f6f0000000000000000000000000000000000000000000000000000000000".to_owned(),
"0000000000000000000000000000000000000000000000000000000000000003".to_owned(),
"6261720000000000000000000000000000000000000000000000000000000000".to_owned(),
]
);
}
#[test]
fn encode_peggy_checkpoint_hash() {
use crate::utils::bytes_to_hex_str;
let nonce: Uint256 = 0u32.into();
let validators: AbiToken = vec![
"0xc783df8a850f42e7F7e57013759C285caa701eB6"
.parse::<Address>()
.unwrap(),
"0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4"
.parse()
.unwrap(),
"0xE5904695748fe4A84b40b3fc79De2277660BD1D3"
.parse()
.unwrap(),
]
.into();
let powers: AbiToken = vec![3333u32, 3333, 3333].into();
let encoded = "666f6f0000000000000000000000000000000000000000000000000000000000636865636b706f696e7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c783df8a850f42e7f7e57013759c285caa701eb6000000000000000000000000ead9c93b79ae7c1591b1fb5323bd777e86e150d4000000000000000000000000e5904695748fe4a84b40b3fc79de2277660bd1d300000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000d050000000000000000000000000000000000000000000000000000000000000d050000000000000000000000000000000000000000000000000000000000000d05";
let encoded_hash = "88165860d955aee7dc3e83d9d1156a5864b708841965585d206dbef6e9e1a499";
let result = encode_tokens(&[
AbiToken::FixedString("foo".to_string()),
AbiToken::FixedString("checkpoint".to_string()),
nonce.into(),
validators,
powers,
]);
assert_eq!(encoded, bytes_to_hex_str(&result));
assert_eq!(encoded_hash, bytes_to_hex_str(&get_hash(&result)))
}
#[test]
fn encode_function_with_only_struct_arg() {
let correct = hex_str_to_bytes(
"0x414bf389000000000000000000000000c783df8a850f42e7f7e57013759c285caa701eb6000000000000000000000000c783df8a850f42e7f7e57013759c285caa701eb600000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000c783df8a850f42e7f7e57013759c285caa701eb600000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap();
let address: Address = "0xc783df8a850f42e7F7e57013759C285caa701eB6"
.parse()
.unwrap();
let tokens: Vec<AbiToken> = vec![
address.into(),
address.into(),
500u16.into(),
address.into(),
100_000u32.into(),
100_000u32.into(),
0u8.into(),
0u8.into(),
];
let tokens = [AbiToken::Struct(tokens)];
let sig =
"exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))";
let payload = encode_call(sig, &tokens).unwrap();
assert_eq!(correct, payload);
}
#[test]
fn encode_abiv2_function_header() {
use crate::utils::bytes_to_hex_str;
let signature = "submitLogicCall(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))";
let encoded_method_id = "0x0c246c82";
let res = derive_method_id(signature).unwrap();
assert_eq!(encoded_method_id, format!("0x{}", bytes_to_hex_str(&res)));
}
#[test]
fn encode_uniswap_header() {
use crate::utils::bytes_to_hex_str;
let signature =
"exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))";
let encoded_method_id = "0x414bf389";
let res = derive_method_id(signature).unwrap();
assert_eq!(encoded_method_id, format!("0x{}", bytes_to_hex_str(&res)));
}
#[test]
fn test_args_count() {
let test_signatures = [
("testCall()", 0),
("testCall(uint256,uint256,uint256)", 3),
("updateValset((address[],uint256[],uint256,uint256,address),(address[],uint256[],uint256,uint256,address),uint8[],bytes32[],bytes32[])", 13),
("submitLogicCall(address[],uint256[],uint256,uint8[],bytes32[],bytes32[],(uint256[],address[],uint256[],address[],address,bytes,uint256,bytes32,uint256))", 15),
("updateValset((address[],uint256[],uint256,uint256,address),(address[],uint256[],uint256,uint256,address),(uint8[],bytes32[],bytes32[]))", 13),
("updateValset((address[],uint256[],uint256,uint256,address),(address[],uint256[],uint256,uint256,address),(uint8,bytes32,bytes32)[])", 13),
("submitBatch((address[],uint256[],uint256,uint256,address),(uint8,bytes32,bytes32)[],uint256[],address[],uint256[],uint256,address,uint256)", 14)
];
for (sig, count) in test_signatures.iter() {
assert_eq!(get_args_count(sig).unwrap(), *count);
}
}
#[test]
fn test_encode_int_uint() {
use crate::utils::hex_str_to_bytes;
use std::ops::Neg;
let test_cases: Vec<(String, Vec<AbiToken>)> = vec![
(
"0x0000000000000000000000000000000000000000000000000000000000000000".to_string(),
vec![Uint256::default().into()],
),
(
"0x0000000000000000000000000000000000000000000000000000000000000000".to_string(),
vec![Int256::default().into()],
),
(
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".to_string(),
vec![Int256::from(-1i8).into()],
),
(
"0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF".to_string(),
vec![Uint256::from(std::u64::MAX).into()],
),
(
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000001".to_string(),
vec![Int256::from(std::u64::MAX).neg().into()],
),
(
"0x8000000000000000000000000000000000000000000000000000000000000000".to_string(),
vec![Int256::min_value().into()],
),
(
"0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".to_string(),
vec![Int256::max_value().into()],
),
];
for (byte_string, tokens) in test_cases {
let encoded = hex_str_to_bytes(&byte_string).unwrap();
let result = encode_tokens(&tokens);
assert_eq!(encoded, result);
}
}
}