diff --git a/Cargo.lock b/Cargo.lock index a4939664e7c..e6bf039d6c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1680,6 +1680,16 @@ dependencies = [ "uint", ] +[[package]] +name = "ethaddr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecbcc1770d1b2e3fb83915c80c47a10efbe1964544a40e0211fe40274f8363c8" +dependencies = [ + "serde", + "sha3", +] + [[package]] name = "ethbloom" version = "0.13.0" @@ -1695,6 +1705,16 @@ dependencies = [ "tiny-keccak 2.0.2", ] +[[package]] +name = "ethdigest" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cadad23be1ff9cf970f84a87e8e9666c19b4a4393c1acf89a1790d727ae11cc" +dependencies = [ + "serde", + "sha3", +] + [[package]] name = "ethereum" version = "0.14.0" @@ -1747,6 +1767,26 @@ dependencies = [ "uint", ] +[[package]] +name = "ethnum" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +dependencies = [ + "serde", +] + +[[package]] +name = "ethprim" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d5d08a4d724b4d10c7f5db832f45298c7418629acf0b17cc265f3f35a00bd79" +dependencies = [ + "ethaddr", + "ethdigest", + "ethnum", +] + [[package]] name = "evm" version = "0.39.1" @@ -3832,6 +3872,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "sha3", + "solabi", "substrate-bn", "thiserror 1.0.69", "uint", @@ -3857,12 +3898,12 @@ version = "0.1.0" dependencies = [ "anyhow", "base64 0.22.1", - "ethabi", "oasis-cbor", "oasis-runtime-sdk", "oasis-runtime-sdk-evm", "once_cell", "rustc-hex", + "solabi", "thiserror 1.0.69", "zeroize", ] @@ -5924,6 +5965,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "solabi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005d001dd520582887445fb55b8d392fe3d3fca556d10f14b30522d8c25efb29" +dependencies = [ + "ethprim", + "serde", +] + [[package]] name = "sp800-185" version = "0.2.0" diff --git a/examples/runtime-sdk/rofl-oracle-sgx/Cargo.toml b/examples/runtime-sdk/rofl-oracle-sgx/Cargo.toml index 693ede60647..8aa67730f85 100644 --- a/examples/runtime-sdk/rofl-oracle-sgx/Cargo.toml +++ b/examples/runtime-sdk/rofl-oracle-sgx/Cargo.toml @@ -13,7 +13,7 @@ rofl-utils = { path = "../../../rofl-utils" } # Third-party dependencies. anyhow = "1.0" async-trait = "0.1.77" -ethabi = { version = "18.0.0", default-features = false, features = ["std"] } +solabi = "0.3.0" tokio = { version = "1.38", features = ["rt", "rt-multi-thread"] } serde_json = "1.0" diff --git a/examples/runtime-sdk/rofl-oracle-sgx/src/main.rs b/examples/runtime-sdk/rofl-oracle-sgx/src/main.rs index 70e511fbb22..4806d2afc11 100644 --- a/examples/runtime-sdk/rofl-oracle-sgx/src/main.rs +++ b/examples/runtime-sdk/rofl-oracle-sgx/src/main.rs @@ -3,6 +3,10 @@ use rofl_app_core::prelude::*; /// Address where the oracle contract is deployed. const ORACLE_CONTRACT_ADDRESS: &str = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; // TODO: Replace with your contract address. +/// Type of the submitObservation function inside the contract. +const SUBMIT_OBSERVATION: solabi::FunctionEncoder<(u128,), (bool,)> = + solabi::FunctionEncoder::new(solabi::selector!("submitObservation(uint128)")); + struct OracleApp; #[async_trait] @@ -54,12 +58,7 @@ impl OracleApp { module_evm::types::Call { address: ORACLE_CONTRACT_ADDRESS.parse().unwrap(), value: 0.into(), - data: [ - ethabi::short_signature("submitObservation", &[ethabi::ParamType::Uint(128)]) - .to_vec(), - ethabi::encode(&[ethabi::Token::Uint(observation.into())]), - ] - .concat(), + data: SUBMIT_OBSERVATION.encode_params(&(observation,)), }, ); tx.set_fee_gas(200_000); diff --git a/examples/runtime-sdk/rofl-oracle-tdx-raw/Cargo.toml b/examples/runtime-sdk/rofl-oracle-tdx-raw/Cargo.toml index d1c9b895c3f..1fb75e1dab4 100644 --- a/examples/runtime-sdk/rofl-oracle-tdx-raw/Cargo.toml +++ b/examples/runtime-sdk/rofl-oracle-tdx-raw/Cargo.toml @@ -12,7 +12,7 @@ module-evm = { path = "../../../runtime-sdk/modules/evm", package = "oasis-runti # Third-party dependencies. anyhow = "1.0" async-trait = "0.1.77" -ethabi = { version = "18.0.0", default-features = false, features = ["std"] } +solabi = "0.3.0" tokio = { version = "1.38", features = ["full"] } reqwest = { version = "0.12", features = ["json"] } serde_json = "1.0" diff --git a/examples/runtime-sdk/rofl-oracle-tdx-raw/src/main.rs b/examples/runtime-sdk/rofl-oracle-tdx-raw/src/main.rs index 41499d451b0..ef3f21f603c 100644 --- a/examples/runtime-sdk/rofl-oracle-tdx-raw/src/main.rs +++ b/examples/runtime-sdk/rofl-oracle-tdx-raw/src/main.rs @@ -3,6 +3,10 @@ use rofl_app_core::prelude::*; /// Address where the oracle contract is deployed. const ORACLE_CONTRACT_ADDRESS: &str = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; // TODO: Replace with your contract address. +/// Type of the submitObservation function inside the contract. +const SUBMIT_OBSERVATION: solabi::FunctionEncoder<(u128,), (bool,)> = + solabi::FunctionEncoder::new(solabi::selector!("submitObservation(uint128)")); + struct OracleApp; #[async_trait] @@ -49,12 +53,7 @@ impl OracleApp { module_evm::types::Call { address: ORACLE_CONTRACT_ADDRESS.parse().unwrap(), value: 0.into(), - data: [ - ethabi::short_signature("submitObservation", &[ethabi::ParamType::Uint(128)]) - .to_vec(), - ethabi::encode(&[ethabi::Token::Uint(observation.into())]), - ] - .concat(), + data: SUBMIT_OBSERVATION.encode_params(&(observation,)), }, ); tx.set_fee_gas(200_000); diff --git a/runtime-sdk-macros/src/evm_derive.rs b/runtime-sdk-macros/src/evm_derive.rs index ac853472e4f..ae78e47f992 100644 --- a/runtime-sdk-macros/src/evm_derive.rs +++ b/runtime-sdk-macros/src/evm_derive.rs @@ -121,20 +121,21 @@ fn generate_method_call(attr: &EvmMethod, sig: &Signature) -> Option = Vec::new(); for (i, evm_ty) in sig.args.iter().enumerate() { let arg_name = format_ident!("arg_{}", i); + let field_index = syn::Index::from(i); call_args.push(arg_name.clone()); match evm_ty.as_str() { "address" => { - decoder_list.push(quote!(::ethabi::ParamType::Address)); + decoder_list.push(quote!(::solabi::Address)); arg_decls.push(quote! { - let #arg_name = decoded_args[#i].clone().into_address().unwrap(); + let #arg_name = ::primitive_types::H160(decoded_args.#field_index.0); }); } "uint256" => { - decoder_list.push(quote!(::ethabi::ParamType::Uint(256))); + decoder_list.push(quote!(::solabi::U256)); let temp_name = format_ident!("{}_uint", arg_name); arg_decls.push(quote! { - let #temp_name = decoded_args[#i].clone().into_uint().unwrap(); - if #temp_name.bits() > (u128::BITS as usize) { + let #temp_name = decoded_args.#field_index.clone(); + if *#temp_name.high() != 0 { return Some(Err(::evm::executor::stack::PrecompileFailure::Error { exit_status: ::evm::ExitError::Other("integer overflow".into()), })); @@ -152,8 +153,17 @@ fn generate_method_call(attr: &EvmMethod, sig: &Signature) -> Option return Some(Err(::evm::executor::stack::PrecompileFailure::Error { exit_status: ::evm::ExitError::Other("invalid argument".into()), })), @@ -329,7 +339,7 @@ pub fn derive_evm_event(input: DeriveInput) -> TokenStream { let topics: Vec = fields.iter().filter_map(|f| { if f.indexed.is_present() { let name = f.ident.as_ref().unwrap(); - Some(quote! { ::primitive_types::H256::from_slice(::ethabi::encode(&[self.#name.clone()]).as_slice()) }) + Some(quote! { ::primitive_types::H256::from_slice(::solabi::encode(&[self.#name.clone()]).as_slice()) }) } else { None } @@ -350,7 +360,7 @@ pub fn derive_evm_event(input: DeriveInput) -> TokenStream { quote! { vec![] } } else { quote! { - ::ethabi::encode(&[ + ::solabi::encode(&[ #(#data ,)* ]) } @@ -408,19 +418,19 @@ pub fn derive_evm_error(input: DeriveInput) -> TokenStream { "address" => { // The tuple field for this should be a primitive_types::H160. abi_tokens.push(quote! { - ::ethabi::Token::Address(*#field_ident) + solabi::Address((*#field_ident).into()) }); } "uint256" => { // The tuple field for this should be a u128. abi_tokens.push(quote! { - ::ethabi::Token::Uint((*#field_ident).into()) + solabi::U256::new(*#field_ident as u128) }); } "string" => { // The tuple field for this should be anything that has a to_string() method. abi_tokens.push(quote! { - ::ethabi::Token::String(#field_ident.to_string()) + #field_ident.to_string() }); } ty => { @@ -439,7 +449,7 @@ pub fn derive_evm_error(input: DeriveInput) -> TokenStream { } else { ( quote! { Self::#ident(#(#match_fields,)*) }, - quote! { output.extend(::ethabi::encode(&[#(#abi_tokens,)*]).as_slice()); }, + quote! { output.extend(::solabi::encode(&(#(#abi_tokens,)*)).as_slice()); }, ) }; variant_encoders.push(quote! { diff --git a/runtime-sdk/modules/evm/Cargo.toml b/runtime-sdk/modules/evm/Cargo.toml index 7eb14f7d9f3..1e7cf89096b 100644 --- a/runtime-sdk/modules/evm/Cargo.toml +++ b/runtime-sdk/modules/evm/Cargo.toml @@ -29,6 +29,7 @@ hmac = "0.12.1" rand_core = { version = "0.6.4", default-features = false } # Ethereum. +solabi = "0.3.0" ethabi = { version = "18.0.0", default-features = false, features = ["std"] } ethereum = "0.15" evm = { git = "https://github.com/oasisprotocol/evm", tag = "v0.39.1-oasis" } diff --git a/runtime-sdk/modules/evm/fuzz/precompile_contract_dispatch_corpus.rs b/runtime-sdk/modules/evm/fuzz/precompile_contract_dispatch_corpus.rs index 4b869a66cf8..3757fb68d09 100644 --- a/runtime-sdk/modules/evm/fuzz/precompile_contract_dispatch_corpus.rs +++ b/runtime-sdk/modules/evm/fuzz/precompile_contract_dispatch_corpus.rs @@ -4,7 +4,6 @@ use std::{fs, path}; #[cfg(fuzzing)] use honggfuzz::fuzz; -use ethabi::Token; use primitive_types::H160; fn gen_calldata() -> Box>> { @@ -13,10 +12,10 @@ fn gen_calldata() -> Box>> { let arg = &[0xd5, 0x2b, 0xce, 0x59]; let mut arg_input = arg.to_vec(); - arg_input.append(&mut ethabi::encode(&[ - Token::Address(H160::zero()), - Token::Uint(42.into()), - ])); + arg_input.append(&mut solabi::encode(&( + solabi::Address(H160::zero().into()), + solabi::U256::from(42_u128), + ))); Box::new(vec![direct.to_vec(), arg_input].into_iter()) } diff --git a/runtime-sdk/modules/evm/fuzz/precompile_corpus.rs b/runtime-sdk/modules/evm/fuzz/precompile_corpus.rs index 32c6f221c5b..4c8a3a8804e 100644 --- a/runtime-sdk/modules/evm/fuzz/precompile_corpus.rs +++ b/runtime-sdk/modules/evm/fuzz/precompile_corpus.rs @@ -4,8 +4,6 @@ use std::{fs, path}; #[cfg(fuzzing)] use honggfuzz::fuzz; -use ethabi::Token; - use oasis_runtime_sdk_evm::precompile::testing::read_test_cases; fn gen_test_vectors(fixture: &str) -> Box>> { @@ -16,61 +14,53 @@ fn gen_test_vectors(fixture: &str) -> Box>> { ) } -fn gen_ethabi(tokens: &[Token]) -> Box>> { - Box::new(vec![ethabi::encode(tokens)].into_iter()) -} - fn gen_x25519() -> Box>> { let key = b"this must be the excelentest key"; let nonce = b"complete noncence, and too long."; let plaintext = b"0123456789"; let ad = b"additional data"; - gen_ethabi(&[ - Token::FixedBytes(key.to_vec()), - Token::FixedBytes(nonce.to_vec()), - Token::Bytes(plaintext.to_vec()), - Token::Bytes(ad.to_vec()), - ]) + Box::new( + vec![solabi::encode(&( + key.to_vec(), + nonce.to_vec(), + plaintext.to_vec(), + ad.to_vec(), + ))] + .into_iter(), + ) } fn gen_random_bytes() -> Box>> { - Box::new( - (0..32).map(|bytes| { - ethabi::encode(&[Token::Uint(bytes.into()), Token::Bytes(vec![0xbe, 0xef])]) - }), - ) + Box::new((0..32).map(|bytes| solabi::encode(&(bytes, vec![0xbe, 0xef])))) } fn gen_keygen() -> Box>> { Box::new((0..10).map(|signature_type| { - ethabi::encode(&[ - Token::Uint(signature_type.into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - ]) + solabi::encode(&(signature_type, b"01234567890123456789012345678901".to_vec())) })) } fn gen_sign() -> Box>> { Box::new((0..10).map(|signature_type| { - ethabi::encode(&[ - Token::Uint(signature_type.into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - Token::Bytes(b"test context".to_vec()), - Token::Bytes(b"test message".to_vec()), - ]) + solabi::encode(&( + signature_type, + b"01234567890123456789012345678901", + b"test context", + b"test message", + )) })) } fn gen_verify() -> Box>> { Box::new((0..10).map(|signature_type| { - ethabi::encode(&[ - Token::Uint(signature_type.into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - Token::Bytes(b"test context".to_vec()), - Token::Bytes(b"test message".to_vec()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - ]) + solabi::encode(&( + signature_type, + b"01234567890123456789012345678901", + b"test context", + b"test message", + b"01234567890123456789012345678901", + )) })) } diff --git a/runtime-sdk/modules/evm/src/mock.rs b/runtime-sdk/modules/evm/src/mock.rs index a882490475f..1b180397182 100644 --- a/runtime-sdk/modules/evm/src/mock.rs +++ b/runtime-sdk/modules/evm/src/mock.rs @@ -28,38 +28,32 @@ impl EvmSigner { } /// Dispatch a call to the given EVM contract method. - pub fn call_evm( + pub fn call_evm( &mut self, ctx: &C, address: H160, - name: &str, - param_types: &[ethabi::ParamType], - params: &[ethabi::Token], + selector: solabi::Selector, + params: &T, ) -> dispatcher::DispatchResult where C: Context, { - self.call_evm_opts(ctx, address, name, param_types, params, Default::default()) + self.call_evm_opts(ctx, address, selector, params, Default::default()) } /// Dispatch a call to the given EVM contract method with the given options. - pub fn call_evm_opts( + pub fn call_evm_opts( &mut self, ctx: &C, address: H160, - name: &str, - param_types: &[ethabi::ParamType], - params: &[ethabi::Token], + selector: solabi::Selector, + params: &T, opts: CallOptions, ) -> dispatcher::DispatchResult where C: Context, { - let data = [ - ethabi::short_signature(name, param_types).to_vec(), - ethabi::encode(params), - ] - .concat(); + let data = solabi::encode_with_selector(selector, params); self.call_opts( ctx, @@ -79,38 +73,32 @@ impl EvmSigner { } /// Dispatch a query to the given EVM contract method. - pub fn query_evm_call( + pub fn query_evm_call( &self, ctx: &C, address: H160, - name: &str, - param_types: &[ethabi::ParamType], - params: &[ethabi::Token], + selector: solabi::Selector, + params: &T, ) -> Result, RuntimeError> where C: Context, { - self.query_evm_call_opts(ctx, address, name, param_types, params, Default::default()) + self.query_evm_call_opts(ctx, address, selector, params, Default::default()) } /// Dispatch a query to the given EVM contract method. - pub fn query_evm_call_opts( + pub fn query_evm_call_opts( &self, ctx: &C, address: H160, - name: &str, - param_types: &[ethabi::ParamType], - params: &[ethabi::Token], + selector: solabi::Selector, + params: &T, opts: QueryOptions, ) -> Result, RuntimeError> where C: Context, { - let data = [ - ethabi::short_signature(name, param_types).to_vec(), - ethabi::encode(params), - ] - .concat(); + let data = solabi::encode_with_selector(selector, params); self.query_evm_opts(ctx, Some(address), data, opts) } @@ -249,31 +237,10 @@ pub fn load_contract_bytecode(raw: &str) -> Vec { /// Decode a basic revert reason. pub fn decode_reverted(msg: &str) -> Option { - decode_reverted_abi( - msg, - ethabi::AbiError { - name: "Error".to_string(), - inputs: vec![ethabi::Param { - name: "message".to_string(), - kind: ethabi::ParamType::String, - internal_type: None, - }], - }, - )? - .pop() - .unwrap() - .into_string() -} - -/// Decode a revert reason accoording to the given API. -pub fn decode_reverted_abi(msg: &str, abi: ethabi::AbiError) -> Option> { + let error_encoder = solabi::ErrorEncoder::<(String,)>::new(solabi::selector!("Error(string)")); let raw = decode_reverted_raw(msg)?; - - // Strip (and validate) error signature. - let signature = abi.signature(); - let raw = raw.strip_prefix(&signature.as_bytes()[..4])?; - - Some(abi.decode(raw).unwrap()) + let (message,) = error_encoder.decode(&raw).ok()?; + Some(message) } /// Decode a base64-encoded revert reason. diff --git a/runtime-sdk/modules/evm/src/precompile/confidential.rs b/runtime-sdk/modules/evm/src/precompile/confidential.rs index e2aca907ee0..e11be065993 100644 --- a/runtime-sdk/modules/evm/src/precompile/confidential.rs +++ b/runtime-sdk/modules/evm/src/precompile/confidential.rs @@ -1,7 +1,6 @@ //! Implements the confidential precompiles. use std::{collections::HashMap, convert::TryInto}; -use ethabi::{ParamType, Token}; use evm::{ executor::stack::{PrecompileFailure, PrecompileHandle, PrecompileOutput}, ExitError, ExitRevert, ExitSucceed, @@ -82,16 +81,17 @@ pub(super) fn call_random_bytes( handle: &mut impl PrecompileHandle, backend: &B, ) -> PrecompileResult { - let mut call_args = ethabi::decode(&[ParamType::Uint(256), ParamType::Bytes], handle.input()) - .map_err(|e| PrecompileFailure::Error { - exit_status: ExitError::Other(e.to_string().into()), - })?; - let pers_str = call_args.pop().unwrap().into_bytes().unwrap(); - let num_bytes_big = call_args.pop().unwrap().into_uint().unwrap(); - let num_bytes = num_bytes_big + let (num_bytes_big, pers_str): (solabi::U256, solabi::Bytes>) = + solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { + exit_status: ExitError::Other(e.to_string().into()), + })?; + + let pers_str = pers_str.as_bytes(); + let num_bytes: u64 = num_bytes_big .try_into() .unwrap_or(u64::MAX) .min(RNG_MAX_BYTES); + // This operation shouldn't be too cheap to start since it invokes a key manager. // Each byte is generated using hashing, so it's neither expensive nor cheap. // Thus: @@ -104,7 +104,7 @@ pub(super) fn call_random_bytes( record_multilinear_cost(handle, num_bytes, pers_str.len() as u64, 240, 60, 10_000)?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: backend.random_bytes(num_bytes, &pers_str), + output: backend.random_bytes(num_bytes, pers_str), }) } @@ -171,27 +171,24 @@ pub(super) fn call_x25519_derive(handle: &mut impl PrecompileHandle) -> Precompi fn decode_deoxysii_call_args( input: &[u8], ) -> Result<([u8; KEY_SIZE], [u8; NONCE_SIZE], Vec, Vec), PrecompileFailure> { - let mut call_args = ethabi::decode( - &[ - ParamType::FixedBytes(32), // key - ParamType::FixedBytes(32), // nonce - ParamType::Bytes, // plain or ciphertext - ParamType::Bytes, // associated data - ], - input, - ) - .map_err(|e| PrecompileFailure::Error { + let (key, nonce, text, ad): ( + solabi::Bytes<[u8; 32]>, // key + solabi::Bytes<[u8; 32]>, // nonce + solabi::Bytes>, // plain or ciphertext + solabi::Bytes>, // associated data + ) = solabi::decode(input).map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(e.to_string().into()), })?; - let ad = call_args.pop().unwrap().into_bytes().unwrap(); - let text = call_args.pop().unwrap().into_bytes().unwrap(); - let nonce_bytes = call_args.pop().unwrap().into_fixed_bytes().unwrap(); - let key_bytes = call_args.pop().unwrap().into_fixed_bytes().unwrap(); + + let key_bytes = key.as_bytes(); + let nonce_bytes = nonce.as_bytes(); + let text = text.to_vec(); + let ad = ad.to_vec(); let mut nonce = [0u8; NONCE_SIZE]; nonce.copy_from_slice(&nonce_bytes[..NONCE_SIZE]); let mut key = [0u8; KEY_SIZE]; - key.copy_from_slice(&key_bytes); + key.copy_from_slice(key_bytes); Ok((key, nonce, text, ad)) } @@ -239,27 +236,16 @@ pub(super) fn call_deoxysii_open(handle: &mut impl PrecompileHandle) -> Precompi } pub(super) fn call_keypair_generate(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let mut call_args = ethabi::decode( - &[ - ParamType::Uint(256), // method - ParamType::Bytes, // seed - ], - handle.input(), - ) - .map_err(|e| PrecompileFailure::Error { + let (method, seed): ( + solabi::U256, // method + solabi::Bytes>, // seed + ) = solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(e.to_string().into()), })?; - let seed = call_args.pop().unwrap().into_bytes().unwrap(); - let method: usize = call_args - .pop() - .unwrap() - .into_uint() - .unwrap() - .try_into() - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("method identifier out of bounds".into()), - })?; + let method: usize = method.try_into().map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("method identifier out of bounds".into()), + })?; let sig_type: SignatureType = >::try_into(method) .map_err(|_| PrecompileFailure::Error { @@ -281,46 +267,38 @@ pub(super) fn call_keypair_generate(handle: &mut impl PrecompileHandle) -> Preco 0, )?; - let signer = signature::MemorySigner::new_from_seed(sig_type, &seed).map_err(|err| { - PrecompileFailure::Error { - exit_status: ExitError::Other(format!("error creating signer: {err}").into()), - } - })?; + let signer = + signature::MemorySigner::new_from_seed(sig_type, seed.as_bytes()).map_err(|err| { + PrecompileFailure::Error { + exit_status: ExitError::Other(format!("error creating signer: {err}").into()), + } + })?; let public = signer.public_key().as_bytes().to_vec(); let private = signer.to_bytes(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Bytes(public), Token::Bytes(private)]), + output: solabi::encode(&(solabi::Bytes(public), solabi::Bytes(private))), }) } pub(super) fn call_sign(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let mut call_args = ethabi::decode( - &[ - ParamType::Uint(256), // signature type - ParamType::Bytes, // private key - ParamType::Bytes, // context or precomputed hash bytes - ParamType::Bytes, // message; should be zero-length if precomputed hash given - ], - handle.input(), - ) - .map_err(|e| PrecompileFailure::Error { + #[allow(clippy::type_complexity)] + let (typ, pk, ctx, msg): ( + solabi::U256, // signature type + solabi::Bytes>, // private key + solabi::Bytes>, // context or precomputed hash bytes + solabi::Bytes>, // message; should be zero-length if precomputed hash given + ) = solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(e.to_string().into()), })?; - let message = call_args.pop().unwrap().into_bytes().unwrap(); - let ctx_or_hash = call_args.pop().unwrap().into_bytes().unwrap(); - let pk = call_args.pop().unwrap().into_bytes().unwrap(); - let method = call_args - .pop() - .unwrap() - .into_uint() - .unwrap() - .try_into() - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("signature type identifier out of bounds".into()), - })?; + let message = msg.as_bytes(); + let ctx_or_hash = ctx.as_bytes(); + let pk = pk.as_bytes(); + let method: usize = typ.try_into().map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("signature type identifier out of bounds".into()), + })?; let sig_type: SignatureType = >::try_into(method) .map_err(|_| PrecompileFailure::Error { @@ -338,13 +316,13 @@ pub(super) fn call_sign(handle: &mut impl PrecompileHandle) -> PrecompileResult })?; record_linear_cost(handle, handle.input().len() as u64, costs.0, costs.1)?; - let signer = signature::MemorySigner::from_bytes(sig_type, &pk).map_err(|e| { + let signer = signature::MemorySigner::from_bytes(sig_type, pk).map_err(|e| { PrecompileFailure::Error { exit_status: ExitError::Other(format!("error creating signer: {e}").into()), } })?; - let result = signer.sign_by_type(sig_type, &ctx_or_hash, &message); + let result = signer.sign_by_type(sig_type, ctx_or_hash, message); let result = result.map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(format!("error signing message: {e}").into()), })?; @@ -356,33 +334,24 @@ pub(super) fn call_sign(handle: &mut impl PrecompileHandle) -> PrecompileResult } pub(super) fn call_verify(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let mut call_args = ethabi::decode( - &[ - ParamType::Uint(256), // signature type - ParamType::Bytes, // public key - ParamType::Bytes, // context or precomputed hash bytes - ParamType::Bytes, // message; should be zero-length if precomputed hash given - ParamType::Bytes, // signature - ], - handle.input(), - ) - .map_err(|e| PrecompileFailure::Error { + #[allow(clippy::type_complexity)] + let (typ, pk, ctx, msg, sig): ( + solabi::U256, // signature type + solabi::Bytes>, // public key + solabi::Bytes>, // context or precomputed hash bytes + solabi::Bytes>, // message; should be zero-length if precomputed hash given + solabi::Bytes>, // signature + ) = solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(e.to_string().into()), })?; - let signature = call_args.pop().unwrap().into_bytes().unwrap(); - let message = call_args.pop().unwrap().into_bytes().unwrap(); - let ctx_or_hash = call_args.pop().unwrap().into_bytes().unwrap(); - let pk = call_args.pop().unwrap().into_bytes().unwrap(); - let method = call_args - .pop() - .unwrap() - .into_uint() - .unwrap() - .try_into() - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("signature type identifier out of bounds".into()), - })?; + let signature = sig.to_vec(); + let message = msg.as_bytes(); + let ctx_or_hash = ctx.as_bytes(); + let pk = pk.as_bytes(); + let method: usize = typ.try_into().map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("signature type identifier out of bounds".into()), + })?; let sig_type: SignatureType = >::try_into(method) .map_err(|_| PrecompileFailure::Error { @@ -402,15 +371,15 @@ pub(super) fn call_verify(handle: &mut impl PrecompileHandle) -> PrecompileResul let signature: signature::Signature = signature.into(); let public_key = - signature::PublicKey::from_bytes(sig_type, &pk).map_err(|_| PrecompileFailure::Error { + signature::PublicKey::from_bytes(sig_type, pk).map_err(|_| PrecompileFailure::Error { exit_status: ExitError::Other("error reading public key".into()), })?; - let result = public_key.verify_by_type(sig_type, &ctx_or_hash, &message, &signature); + let result = public_key.verify_by_type(sig_type, ctx_or_hash, message, &signature); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Bool(result.is_ok())]), + output: solabi::encode(&(result.is_ok(),)), }) } @@ -418,7 +387,6 @@ pub(super) fn call_verify(handle: &mut impl PrecompileHandle) -> PrecompileResul mod test { extern crate test; - use ethabi::{ParamType, Token}; use rand::rngs::OsRng; use test::Bencher; @@ -541,12 +509,12 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, ]), - ðabi::encode(&[ - Token::FixedBytes(key.to_vec()), - Token::FixedBytes(nonce.to_vec()), - Token::Bytes(plaintext.to_vec()), - Token::Bytes(ad.to_vec()), - ]), + &solabi::encode(&( + solabi::Bytes(key.to_vec()), + solabi::Bytes(nonce.to_vec()), + solabi::Bytes(plaintext.to_vec()), + solabi::Bytes(ad.to_vec()), + )), 10_000_000, ) .expect("call should return something") @@ -557,12 +525,12 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, ]), - ðabi::encode(&[ - Token::FixedBytes(key.to_vec()), - Token::FixedBytes(nonce.to_vec()), - Token::Bytes(ret_ct.output), - Token::Bytes(ad.to_vec()), - ]), + &solabi::encode(&( + solabi::Bytes(key.to_vec()), + solabi::Bytes(nonce.to_vec()), + solabi::Bytes(ret_ct.output.to_vec()), + solabi::Bytes(ad.to_vec()), + )), 10_000_000, ) .expect("call should return something") @@ -576,7 +544,7 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]), - ðabi::encode(&[Token::Uint(4.into()), Token::Bytes(vec![0xbe, 0xef])]), + &solabi::encode(&(solabi::U256::new(4_u128), solabi::Bytes(vec![0xbe, 0xef]))), 10_560, ) .unwrap(); @@ -594,12 +562,12 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, ]), - ðabi::encode(&[ - Token::FixedBytes(key.to_vec()), - Token::FixedBytes(nonce.to_vec()), - Token::Bytes(plaintext.to_vec()), - Token::Bytes(ad.to_vec()), - ]), + &solabi::encode(&( + solabi::Bytes(key.to_vec()), + solabi::Bytes(nonce.to_vec()), + solabi::Bytes(plaintext.to_vec()), + solabi::Bytes(ad.to_vec()), + )), 10_000_000, ) .expect("call should return something") @@ -618,12 +586,12 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, ]), - ðabi::encode(&[ - Token::FixedBytes(key.to_vec()), - Token::FixedBytes(nonce.to_vec()), - Token::Bytes(plaintext.to_vec()), - Token::Bytes(ad.to_vec()), - ]), + &solabi::encode(&( + solabi::Bytes(key.to_vec()), + solabi::Bytes(nonce.to_vec()), + solabi::Bytes(plaintext.to_vec()), + solabi::Bytes(ad.to_vec()), + )), 10_000_000, ) .expect("call should return something") @@ -669,10 +637,10 @@ mod test { #[test] fn test_keypair_generate() { // Invalid method. - let params = ethabi::encode(&[ - Token::Uint(50.into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(50_u128), + solabi::Bytes(b"01234567890123456789012345678901".to_vec()), + )); call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, @@ -684,10 +652,10 @@ mod test { .expect_err("call should fail"); // Working test. - let params = ethabi::encode(&[ - Token::Uint(SignatureType::Ed25519_Oasis.as_int().into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(SignatureType::Ed25519_Oasis.as_int().into()), + solabi::Bytes(b"01234567890123456789012345678901".to_vec()), + )); let output1 = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, @@ -700,10 +668,10 @@ mod test { .output; // Again, should be repeatable. - let params = ethabi::encode(&[ - Token::Uint(SignatureType::Ed25519_Oasis.as_int().into()), - Token::Bytes(b"01234567890123456789012345678901".to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(SignatureType::Ed25519_Oasis.as_int().into()), + solabi::Bytes(b"01234567890123456789012345678901".to_vec()), + )); let output2 = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, @@ -724,10 +692,10 @@ mod test { } else { 4 }); - let params = ethabi::encode(&[ - Token::Uint(signature_type.as_int().into()), - Token::Bytes(seed), - ]); + let params = solabi::encode(&( + solabi::U256::new(signature_type.as_int().into()), + solabi::Bytes(seed), + )); b.iter(|| { call_contract( H160([ @@ -780,7 +748,10 @@ mod test { } // Generate key pair from a fixed seed. - let params = ethabi::encode(&[Token::Uint(method.into()), Token::Bytes(seed.to_vec())]); + let params = solabi::encode(&( + solabi::U256::new(method.into()), + solabi::Bytes(seed.to_vec()), + )); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, @@ -792,18 +763,18 @@ mod test { .unwrap() .output; - let mut call_output = - ethabi::decode(&[ParamType::Bytes, ParamType::Bytes], &output).unwrap(); - let private_key = call_output.pop().unwrap().into_bytes().unwrap().to_vec(); - let public_key = call_output.pop().unwrap().into_bytes().unwrap().to_vec(); + let (public_key, private_key): (solabi::Bytes>, solabi::Bytes>) = + solabi::decode(&output).expect("decode should succeed"); + let public_key = public_key.as_bytes(); + let private_key = private_key.as_bytes(); // Sign message. - let params = ethabi::encode(&[ - Token::Uint(method.into()), - Token::Bytes(private_key), - Token::Bytes(context.to_vec()), - Token::Bytes(message.to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(method.into()), + solabi::Bytes(private_key), + solabi::Bytes(context.to_vec()), + solabi::Bytes(message.to_vec()), + )); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, @@ -818,13 +789,13 @@ mod test { let signature = output.to_vec(); // Verify signature. - let params = ethabi::encode(&[ - Token::Uint(method.into()), - Token::Bytes(public_key), - Token::Bytes(context.to_vec()), - Token::Bytes(message.to_vec()), - Token::Bytes(signature), - ]); + let params = solabi::encode(&( + solabi::U256::new(method.into()), + solabi::Bytes(public_key), + solabi::Bytes(context.to_vec()), + solabi::Bytes(message.to_vec()), + solabi::Bytes(signature), + )); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, @@ -835,12 +806,7 @@ mod test { .unwrap() .unwrap() .output; - let status = ethabi::decode(&[ParamType::Bool], &output) - .unwrap() - .pop() - .unwrap() - .into_bool() - .unwrap(); + let status: bool = solabi::decode(&output).expect("decode should succeed"); assert_eq!(status, true); } } @@ -884,7 +850,7 @@ mod test { }); // Generate key pair from a fixed seed. - let params = ethabi::encode(&[Token::Uint(method.into()), Token::Bytes(seed)]); + let params = solabi::encode(&(solabi::U256::new(method.into()), solabi::Bytes(seed))); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, @@ -896,18 +862,18 @@ mod test { .unwrap() .output; - let mut call_output = - ethabi::decode(&[ParamType::Bytes, ParamType::Bytes], &output).unwrap(); - let private_key = call_output.pop().unwrap().into_bytes().unwrap().to_vec(); - let public_key = call_output.pop().unwrap().into_bytes().unwrap().to_vec(); + let (public_key, private_key): (solabi::Bytes>, solabi::Bytes>) = + solabi::decode(&output).expect("decode should succeed"); + let public_key = public_key.as_bytes(); + let private_key = private_key.as_bytes(); // Sign message. - let params = ethabi::encode(&[ - Token::Uint(method.into()), - Token::Bytes(private_key), - Token::Bytes(hasher(message)), - Token::Bytes(Vec::new()), - ]); + let params = solabi::encode(&( + solabi::U256::new(method.into()), + solabi::Bytes(private_key), + solabi::Bytes(hasher(message)), + solabi::Bytes(Vec::new()), + )); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, @@ -922,13 +888,13 @@ mod test { let signature = output.to_vec(); // Verify signature. - let params = ethabi::encode(&[ - Token::Uint(method.into()), - Token::Bytes(public_key), - Token::Bytes(hasher(message)), - Token::Bytes(Vec::new()), - Token::Bytes(signature), - ]); + let params = solabi::encode(&( + solabi::U256::new(method.into()), + solabi::Bytes(public_key), + solabi::Bytes(hasher(message)), + solabi::Bytes(Vec::new()), + solabi::Bytes(signature), + )); let output = call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, @@ -939,12 +905,7 @@ mod test { .unwrap() .unwrap() .output; - let status = ethabi::decode(&[ParamType::Bool], &output) - .unwrap() - .pop() - .unwrap() - .into_bool() - .unwrap(); + let status: bool = solabi::decode(&output).expect("decode should succeed"); assert_eq!(status, true); } } @@ -972,12 +933,12 @@ mod test { SignatureType::Ed25519_Oasis.as_int() }; - let params = ethabi::encode(&[ - Token::Uint(method.unwrap_or(ctx_method).into()), - Token::Bytes(pk.map(|o| o.to_vec()).unwrap_or(def_pk)), - Token::Bytes(context.unwrap_or(def_ctx).to_vec()), - Token::Bytes(message.unwrap_or(def_msg).to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(method.unwrap_or(ctx_method).into()), + solabi::Bytes(pk.map(|o| o.to_vec()).unwrap_or(def_pk)), + solabi::Bytes(context.unwrap_or(def_ctx).to_vec()), + solabi::Bytes(message.unwrap_or(def_msg).to_vec()), + )); call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, @@ -1033,12 +994,12 @@ mod test { ) }; - let params = ethabi::encode(&[ - Token::Uint(signature_type.as_int().into()), - Token::Bytes(signer.to_bytes()), - Token::Bytes(context), - Token::Bytes(message), - ]); + let params = solabi::encode(&( + solabi::U256::new(signature_type.as_int().into()), + solabi::Bytes(signer.to_bytes()), + solabi::Bytes(context), + solabi::Bytes(message), + )); b.iter(|| { call_contract( @@ -1139,17 +1100,17 @@ mod test { let def_ctx = b"default context"; let def_msg = b"default message"; - let params = ethabi::encode(&[ - Token::Uint( + let params = solabi::encode(&( + solabi::U256::new( method .unwrap_or(SignatureType::Ed25519_Oasis.as_int()) .into(), ), - Token::Bytes(pk.map(|o| o.to_vec()).unwrap_or(def_pk)), - Token::Bytes(context.unwrap_or(def_ctx).to_vec()), - Token::Bytes(message.unwrap_or(def_msg).to_vec()), - Token::Bytes(signature.unwrap_or(def_sig.as_ref()).to_vec()), - ]); + solabi::Bytes(pk.map(|o| o.to_vec()).unwrap_or(def_pk)), + solabi::Bytes(context.unwrap_or(def_ctx).to_vec()), + solabi::Bytes(message.unwrap_or(def_msg).to_vec()), + solabi::Bytes(signature.unwrap_or(def_sig.as_ref()).to_vec()), + )); call_contract( H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, @@ -1171,15 +1132,8 @@ mod test { .expect("call should succeed") .output; // Verification should have failed. - assert_eq!( - ethabi::decode(&[ParamType::Bool], &output) - .unwrap() - .pop() - .unwrap() - .into_bool() - .unwrap(), - false - ); + let status: bool = solabi::decode(&output).expect("decode should succeed"); + assert_eq!(status, false); // Invalid signature. let long_zeroes: Vec = vec![0; 64]; @@ -1188,30 +1142,16 @@ mod test { .expect("call should succeed") .output; // Verification should have failed. - assert_eq!( - ethabi::decode(&[ParamType::Bool], &output) - .unwrap() - .pop() - .unwrap() - .into_bool() - .unwrap(), - false - ); + let status: bool = solabi::decode(&output).expect("decode should succeed"); + assert_eq!(status, false); // All ok. output = push_all_and_test(None, None, None, None, None) .expect("call should return something") .expect("call should succeed") .output; - assert_eq!( - ethabi::decode(&[ParamType::Bool], &output) - .unwrap() - .pop() - .unwrap() - .into_bool() - .unwrap(), - true - ); + let status: bool = solabi::decode(&output).expect("decode should succeed"); + assert_eq!(status, true); } fn bench_verification( @@ -1245,13 +1185,13 @@ mod test { }; let signature = signer.sign(&context, &message).unwrap(); - let params = ethabi::encode(&[ - Token::Uint(signature_type.as_int().into()), - Token::Bytes(signer.public_key().as_bytes().to_vec()), - Token::Bytes(context), - Token::Bytes(message), - Token::Bytes(signature.as_ref().to_vec()), - ]); + let params = solabi::encode(&( + solabi::U256::new(signature_type.as_int().into()), + solabi::Bytes(signer.public_key().as_bytes().to_vec()), + solabi::Bytes(context), + solabi::Bytes(message), + solabi::Bytes(signature.as_ref().to_vec()), + )); b.iter(|| { call_contract( diff --git a/runtime-sdk/modules/evm/src/precompile/erc20.rs b/runtime-sdk/modules/evm/src/precompile/erc20.rs index cdfee9fca46..570c2ebfdf9 100644 --- a/runtime-sdk/modules/evm/src/precompile/erc20.rs +++ b/runtime-sdk/modules/evm/src/precompile/erc20.rs @@ -1,6 +1,5 @@ use std::marker::PhantomData; -use ethabi::Token; use evm::{ executor::stack::{PrecompileHandle, PrecompileOutput}, ExitSucceed, @@ -304,22 +303,22 @@ impl Erc20Token for T { #[evm_event(name = "Transfer")] struct TransferEvent { #[evm_event(arg_type = "address", indexed)] - from: Token, + from: solabi::Address, #[evm_event(arg_type = "address", indexed)] - to: Token, + to: solabi::Address, #[evm_event(arg_type = "uint256")] - value: Token, + value: solabi::U256, } #[derive(EvmEvent)] #[evm_event(name = "Approval")] struct ApprovalEvent { #[evm_event(arg_type = "address", indexed)] - owner: Token, + owner: solabi::Address, #[evm_event(arg_type = "address", indexed)] - spender: Token, + spender: solabi::Address, #[evm_event(arg_type = "uint256")] - value: Token, + value: solabi::U256, } #[derive(Default)] @@ -339,7 +338,7 @@ impl Erc20Contract { handle.record_cost(T::GAS_COSTS.name)?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::String(T::NAME.to_string())]), + output: solabi::encode(&[T::NAME.to_string()]), }) } @@ -348,7 +347,7 @@ impl Erc20Contract { handle.record_cost(T::GAS_COSTS.symbol)?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::String(T::SYMBOL.to_string())]), + output: solabi::encode(&[T::SYMBOL.to_string()]), }) } @@ -357,7 +356,7 @@ impl Erc20Contract { handle.record_cost(T::GAS_COSTS.decimals)?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Uint(T::DECIMALS.into())]), + output: solabi::encode(&[T::DECIMALS as u128]), }) } @@ -367,7 +366,7 @@ impl Erc20Contract { match T::total_supply() { Ok(amount) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Uint(amount.into())]), + output: solabi::encode(&amount), }), Err(e) => Err(e.encode()), } @@ -379,7 +378,7 @@ impl Erc20Contract { match T::balance_of(&address) { Ok(amount) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Uint(amount.into())]), + output: solabi::encode(&amount), }), Err(e) => Err(e.encode()), } @@ -396,15 +395,15 @@ impl Erc20Contract { match T::transfer(&sender, &recipient, amount) { Ok(done) => { TransferEvent { - from: Token::Address(handle.context().caller), - to: Token::Address(recipient), - value: Token::Uint(amount.into()), + from: solabi::Address(handle.context().caller.into()), + to: solabi::Address(recipient.into()), + value: solabi::U256::from(amount), } .emit::(handle) .unwrap(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Bool(done)]), + output: solabi::encode(&done), }) } Err(e) => Err(e.encode()), @@ -423,15 +422,15 @@ impl Erc20Contract { match T::transfer_from(&owner, &caller, &recipient, amount) { Ok(done) => { TransferEvent { - from: Token::Address(owner), - to: Token::Address(recipient), - value: Token::Uint(amount.into()), + from: solabi::Address(owner.into()), + to: solabi::Address(recipient.into()), + value: solabi::U256::from(amount), } .emit::(handle) .unwrap(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Bool(done)]), + output: solabi::encode(&[done]), }) } Err(e) => Err(e.encode()), @@ -449,15 +448,15 @@ impl Erc20Contract { match T::approve(&owner, &spender, amount) { Ok(done) => { ApprovalEvent { - owner: Token::Address(owner), - spender: Token::Address(spender), - value: Token::Uint(amount.into()), + owner: solabi::Address(owner.into()), + spender: solabi::Address(spender.into()), + value: solabi::U256::from(amount), } .emit::(handle) .unwrap(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Bool(done)]), + output: solabi::encode(&[done]), }) } Err(e) => Err(e.encode()), @@ -474,7 +473,7 @@ impl Erc20Contract { match T::allowance(&owner, &spender) { Ok(amount) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Uint(amount.into())]), + output: solabi::encode(&amount), }), Err(e) => Err(e.encode()), } @@ -487,9 +486,9 @@ impl Erc20Contract { match T::mint(&caller, &to, amount) { Ok(_) => { TransferEvent { - from: Token::Address(H160::zero()), - to: Token::Address(to), - value: Token::Uint(amount.into()), + from: solabi::Address(H160::zero().into()), + to: solabi::Address(to.into()), + value: solabi::U256::from(amount), } .emit::(handle) .unwrap(); @@ -509,9 +508,9 @@ impl Erc20Contract { match T::burn(&caller, &from, amount) { Ok(_) => { TransferEvent { - from: Token::Address(from), - to: Token::Address(H160::zero()), - value: Token::Uint(amount.into()), + from: solabi::Address(from.into()), + to: solabi::Address(H160::zero().into()), + value: solabi::U256::from(amount), } .emit::(handle) .unwrap(); diff --git a/runtime-sdk/modules/evm/src/precompile/gas.rs b/runtime-sdk/modules/evm/src/precompile/gas.rs index d5e0c3d2d4c..aa7ad22f460 100644 --- a/runtime-sdk/modules/evm/src/precompile/gas.rs +++ b/runtime-sdk/modules/evm/src/precompile/gas.rs @@ -1,4 +1,3 @@ -use ethabi::{ParamType, Token}; use evm::{ executor::stack::{PrecompileFailure, PrecompileHandle, PrecompileOutput}, ExitError, ExitSucceed, @@ -12,11 +11,11 @@ const PAD_GAS_COST: u64 = 10; pub(super) fn call_gas_used(handle: &mut impl PrecompileHandle) -> PrecompileResult { handle.record_cost(GAS_USED_COST)?; - let used_gas = handle.used_gas(); + let used_gas: u64 = handle.used_gas(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[Token::Uint(used_gas.into())]), + output: solabi::encode(&(used_gas,)), }) } @@ -24,12 +23,10 @@ pub(super) fn call_pad_gas(handle: &mut impl PrecompileHandle) -> PrecompileResu handle.record_cost(PAD_GAS_COST)?; // Decode args. - let mut call_args = ethabi::decode(&[ParamType::Uint(128)], handle.input()).map_err(|e| { - PrecompileFailure::Error { + let gas_amount_big: u128 = + solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { exit_status: ExitError::Other(e.to_string().into()), - } - })?; - let gas_amount_big = call_args.pop().unwrap().into_uint().unwrap(); + })?; let gas_amount = gas_amount_big.try_into().unwrap_or(u64::MAX); // Obtain total used gas so far. @@ -62,7 +59,6 @@ mod test { mock::EvmSigner, precompile::testing::{init_and_deploy_contract, TestRuntime}, }; - use ethabi::{ParamType, Token}; use oasis_runtime_sdk::{ modules::core::Event, testing::{keys, mock::Mock}, @@ -79,19 +75,14 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x09, ]), - ðabi::encode(&[Token::Bytes(Vec::new())]), + &solabi::encode(&(solabi::Bytes(Vec::new()),)), 10_560, ) .unwrap(); - let gas_usage = ethabi::decode(&[ParamType::Uint(128)], &ret.unwrap().output) - .expect("call should return gas usage") - .pop() - .unwrap() - .into_uint() - .expect("call should return uint") - .try_into() - .unwrap_or(u64::max_value()); + let gas_usage_big: u128 = + solabi::decode(&ret.unwrap().output).expect("call should return gas usage"); + let gas_usage: u64 = gas_usage_big.try_into().unwrap_or(u64::max_value()); assert_eq!(gas_usage, 10, "call should return gas usage"); // Test use gas in contract. @@ -105,8 +96,12 @@ mod test { let expected_gas_used = 22_659; // Call into the test contract. - let dispatch_result = - signer.call_evm(&ctx, contract_address.into(), "test_gas_used", &[], &[]); + let dispatch_result = signer.call_evm( + &ctx, + contract_address.into(), + solabi::selector!("test_gas_used()"), + &(), + ); assert!( dispatch_result.result.is_success(), "test gas used should succeed" @@ -130,7 +125,7 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, ]), - ðabi::encode(&[Token::Uint(1.into())]), + &solabi::encode(&(1_u64,)), 10_560, ) .expect("call should return something") @@ -140,7 +135,7 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, ]), - ðabi::encode(&[Token::Uint(20.into())]), + &solabi::encode(&(20_u64,)), 10_560, ) .unwrap(); @@ -151,7 +146,7 @@ mod test { H160([ 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, ]), - ðabi::encode(&[Token::Uint(20_000.into())]), + &solabi::encode(&(20_000_u64,)), 10_560, ) .expect("call should return something") @@ -171,9 +166,8 @@ mod test { let dispatch_result = signer.call_evm( &ctx, contract_address.into(), - "test_pad_gas", - &[ParamType::Uint(128)], - &[Token::Uint(100.into())], + solabi::selector!("test_pad_gas(uint128)"), + &(100_u128,), ); assert!( dispatch_result.result.is_success(), @@ -193,9 +187,8 @@ mod test { let dispatch_result = signer.call_evm( &ctx, contract_address.into(), - "test_pad_gas", - &[ParamType::Uint(128)], - &[Token::Uint(1.into())], + solabi::selector!("test_pad_gas(uint128)"), + &(1_u128,), ); assert!( dispatch_result.result.is_success(), diff --git a/runtime-sdk/modules/evm/src/precompile/subcall.rs b/runtime-sdk/modules/evm/src/precompile/subcall.rs index 1fa93385f04..f61370a7192 100644 --- a/runtime-sdk/modules/evm/src/precompile/subcall.rs +++ b/runtime-sdk/modules/evm/src/precompile/subcall.rs @@ -1,4 +1,3 @@ -use ethabi::{ParamType, Token}; use evm::{ executor::stack::{PrecompileFailure, PrecompileHandle, PrecompileOutput}, ExitError, ExitSucceed, @@ -45,28 +44,19 @@ pub(super) fn call_subcall( }); } - let mut call_args = ethabi::decode( - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body (CBOR) - ], - handle.input(), - ) - .map_err(|e| PrecompileFailure::Error { - exit_status: ExitError::Other(e.to_string().into()), - })?; - - // Parse raw arguments. - let body = call_args.pop().unwrap().into_bytes().unwrap(); - let method = call_args.pop().unwrap().into_bytes().unwrap(); + // Decode arguments. + let (method, body): (solabi::Bytes>, solabi::Bytes>) = + solabi::decode(handle.input()).map_err(|e| PrecompileFailure::Error { + exit_status: ExitError::Other(e.to_string().into()), + })?; // Parse body as CBOR. - let body = cbor::from_slice(&body).map_err(|_| PrecompileFailure::Error { + let body = cbor::from_slice(body.as_bytes()).map_err(|_| PrecompileFailure::Error { exit_status: ExitError::Other("body is malformed".into()), })?; // Parse method. - let method = String::from_utf8(method).map_err(|_| PrecompileFailure::Error { + let method = String::from_utf8(method.to_vec()).map_err(|_| PrecompileFailure::Error { exit_status: ExitError::Other("method is malformed".into()), })?; @@ -94,17 +84,17 @@ pub(super) fn call_subcall( match result.call_result { CallResult::Ok(value) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[ - Token::Uint(0.into()), // status_code - Token::Bytes(cbor::to_vec(value)), // response - ]), + output: solabi::encode(&( + 0_u64, // status_code + solabi::Bytes(cbor::to_vec(value)), // response + )), }), CallResult::Failed { code, module, .. } => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - output: ethabi::encode(&[ - Token::Uint(code.into()), // status_code - Token::Bytes(module.into()), // response - ]), + output: solabi::encode(&( + code, // status_code + solabi::Bytes(module.as_bytes()), // response + )), }), CallResult::Aborted(_) => { // TODO: Should propagate abort. @@ -118,7 +108,6 @@ pub(super) fn call_subcall( #[cfg(test)] mod test { use base64::prelude::*; - use ethabi::{ParamType, Token}; use oasis_runtime_sdk::{ module::{self, Module as _}, @@ -161,18 +150,14 @@ mod test { let dispatch_result = signer.call_evm( &ctx, contract_address.into(), - "test", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(1_000, Denomination::NATIVE), })), - ], + ), ); assert!( !dispatch_result.result.is_success(), @@ -197,18 +182,14 @@ mod test { let dispatch_result = signer.call_evm_opts( &ctx, contract_address.into(), - "test", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(1_000, Denomination::NATIVE), })), - ], + ), CallOptions { fee: Fee { amount: BaseUnits::new(100, Denomination::NATIVE), @@ -268,18 +249,14 @@ mod test { let dispatch_result = signer.call_evm( &ctx, contract_address.into(), - "test_delegatecall", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test_delegatecall(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(0, Denomination::NATIVE), })), - ], + ), ); if let module::CallResult::Failed { module, @@ -308,30 +285,24 @@ mod test { let dispatch_result = signer.call_evm( &ctx, contract_address.into(), - "test", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("evm.Call".into()), - Token::Bytes(cbor::to_vec(evm::types::Call { + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("evm.Call".as_bytes()), + solabi::Bytes(cbor::to_vec(evm::types::Call { address: contract_address.into(), value: 0.into(), - data: [ - ethabi::short_signature("test", &[ParamType::Bytes, ParamType::Bytes]) - .to_vec(), - ethabi::encode(&[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + data: solabi::encode_with_selector( + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(0, Denomination::NATIVE), })), - ]), - ] - .concat(), + ), + ), })), - ], + ), ); if let module::CallResult::Failed { module, @@ -368,18 +339,14 @@ mod test { let dispatch_result = signer.call_evm_opts( &ctx, contract_address.into(), - "test", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(0, Denomination::NATIVE), })), - ], + ), CallOptions { fee: Fee { gas: 130_000, @@ -398,18 +365,14 @@ mod test { let dispatch_result = signer.call_evm_opts( &ctx, contract_address.into(), - "test", - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test(bytes,bytes)"), + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(0, Denomination::NATIVE), })), - ], + ), CallOptions { fee: Fee { gas: 120_000, @@ -450,18 +413,14 @@ mod test { let dispatch_result = signer.call_evm_opts( &ctx, contract_address.into(), - "test_spin", // Version that spins, wasting gas, after the subcall. - &[ - ParamType::Bytes, // method - ParamType::Bytes, // body - ], - &[ - Token::Bytes("accounts.Transfer".into()), - Token::Bytes(cbor::to_vec(accounts::types::Transfer { + solabi::selector!("test_spin(bytes,bytes)"), // Version that spins, wasting gas, after the subcall. + &( + solabi::Bytes("accounts.Transfer".as_bytes()), + solabi::Bytes(cbor::to_vec(accounts::types::Transfer { to: keys::alice::address(), amount: BaseUnits::new(0, Denomination::NATIVE), })), - ], + ), CallOptions { fee: Fee { gas: 127_710, diff --git a/runtime-sdk/modules/evm/src/signed_call.rs b/runtime-sdk/modules/evm/src/signed_call.rs index 40447973a1b..55844024c91 100644 --- a/runtime-sdk/modules/evm/src/signed_call.rs +++ b/runtime-sdk/modules/evm/src/signed_call.rs @@ -1,6 +1,5 @@ use std::convert::TryFrom as _; -use ethabi::Token; use once_cell::sync::OnceCell; use sha3::{Digest as _, Keccak256}; @@ -15,6 +14,22 @@ use crate::{ Config, Error, Runtime, }; +/// Convert U256 representation from [u64; 4] to [u8; 32] (the u64s seem to be little endian). +fn u64x4_to_u8x32(arr: [u64; 4]) -> [u8; 32] { + let mut out = [0u8; 32]; + for (i, v) in arr.iter().rev().enumerate() { + out[i * 8..(i + 1) * 8].copy_from_slice(&v.to_be_bytes()); + } + out +} + +/// Pad a [u8; 20] to [u8; 32] (left pad with zeros). +fn pad_address_to_u8x32(addr: &[u8; 20]) -> [u8; 32] { + let mut out = [0u8; 32]; + out[12..].copy_from_slice(addr); + out +} + /// Verifies the signature on signed query and whether it is appropriately leashed. /// /// See [`crate::types::SignedSimulateCallEnvelope`] for details on the signature format. @@ -111,47 +126,46 @@ fn hash_call(query: &SimulateCallQuery, leash: &Leash) -> [u8; 32] { ")", leash_type_str!() ); - hash_encoded(&[ - encode_bytes(CALL_TYPE_STR), - Token::Address(query.caller.0.into()), - Token::Address(query.address.unwrap_or_default().0.into()), - Token::Uint(query.gas_limit.into()), - Token::Uint(ethabi::ethereum_types::U256(query.gas_price.0)), - Token::Uint(ethabi::ethereum_types::U256(query.value.0)), - encode_bytes(&query.data), - Token::Uint(hash_leash(leash).into()), - ]) + let encoded = solabi::encode_packed(&( + hash_bytes(CALL_TYPE_STR), + solabi::U256::from_be_bytes(pad_address_to_u8x32(&query.caller.0)), + solabi::U256::from_be_bytes(pad_address_to_u8x32(&query.address.unwrap_or_default().0)), + solabi::U256::new(query.gas_limit.into()), + solabi::U256::from_be_bytes(u64x4_to_u8x32(query.gas_price.0)), + solabi::U256::from_be_bytes(u64x4_to_u8x32(query.value.0)), + hash_bytes(&query.data), + solabi::U256::from_be_bytes(hash_leash(leash)), + )); + Keccak256::digest(encoded).into() } fn hash_leash(leash: &Leash) -> [u8; 32] { - hash_encoded(&[ - encode_bytes(leash_type_str!()), - Token::Uint(leash.nonce.into()), - Token::Uint(leash.block_number.into()), - Token::Uint(leash.block_hash.0.into()), - Token::Uint(leash.block_range.into()), - ]) + let encoded = solabi::encode_packed(&( + hash_bytes(leash_type_str!()), + solabi::U256::new(leash.nonce.into()), + solabi::U256::new(leash.block_number.into()), + solabi::U256::from_be_bytes(leash.block_hash.0), + solabi::U256::new(leash.block_range.into()), + )); + Keccak256::digest(encoded).into() } fn hash_domain() -> &'static [u8; 32] { static DOMAIN_SEPARATOR: OnceCell<[u8; 32]> = OnceCell::new(); // Not `Lazy` because of generic. DOMAIN_SEPARATOR.get_or_init(|| { const DOMAIN_TYPE_STR: &str = "EIP712Domain(string name,string version,uint256 chainId)"; - hash_encoded(&[ - encode_bytes(DOMAIN_TYPE_STR), - encode_bytes("oasis-runtime-sdk/evm: signed query"), - encode_bytes("1.0.0"), - Token::Uint(Cfg::CHAIN_ID.into()), - ]) + let encoded = solabi::encode_packed(&( + hash_bytes(DOMAIN_TYPE_STR), + hash_bytes("oasis-runtime-sdk/evm: signed query"), + hash_bytes("1.0.0"), + solabi::U256::new(Cfg::CHAIN_ID.into()), + )); + Keccak256::digest(encoded).into() }) } -fn encode_bytes(s: impl AsRef<[u8]>) -> Token { - Token::FixedBytes(Keccak256::digest(s.as_ref()).to_vec()) -} - -fn hash_encoded(tokens: &[Token]) -> [u8; 32] { - Keccak256::digest(ethabi::encode(tokens)).into() +fn hash_bytes(s: impl AsRef<[u8]>) -> Vec { + Keccak256::digest(s.as_ref()).to_vec() } #[cfg(test)] @@ -168,6 +182,24 @@ mod test { type Accounts = accounts::Module; + #[test] + fn test_u64x4_to_u8x32() { + let input = [1u64, 2, 3, 4]; + let expected = [ + 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 1, + ]; + assert_eq!(u64x4_to_u8x32(input), expected); + } + + #[test] + fn test_pad_address_to_u8x32() { + let input = [1u8; 20]; + let mut expected = [0u8; 32]; + expected[12..].copy_from_slice(&input); + assert_eq!(pad_address_to_u8x32(&input), expected); + } + /// This was generated using the `@oasislabs/sapphire-paratime` JS lib. const SIGNED_CALL_DATA_PACK: &str = "a36464617461a164626f64794401020304656c65617368a4656e6f6e63651903e76a626c6f636b5f686173685820c92b675c7013e33aa88feaae520eb0ede155e7cacb3c4587e0923cba9953f8bb6b626c6f636b5f72616e6765036c626c6f636b5f6e756d626572182a697369676e6174757265584148bca100e84d13a80b131c62b9b87caf07e4da6542a9e1ea16d8042ba08cc1e31f10ae924d8c137882204e9217423194014ce04fa2130c14f27b148858733c7b1c"; diff --git a/runtime-sdk/modules/evm/src/test.rs b/runtime-sdk/modules/evm/src/test.rs index b85da23f600..0bb6820d72c 100644 --- a/runtime-sdk/modules/evm/src/test.rs +++ b/runtime-sdk/modules/evm/src/test.rs @@ -2,8 +2,8 @@ use std::{collections::BTreeMap, str::FromStr as _}; use base64::prelude::*; -use ethabi::{ParamType, Token}; use sha3::Digest as _; +use solabi; use uint::hex::FromHex; use oasis_runtime_sdk::{ @@ -331,7 +331,7 @@ fn test_c10l_evm_balance_transfer() { parameters: Default::default(), }); - let recipient = ethabi::Address::repeat_byte(42); + let recipient = solabi::Address::from_slice(&[42; 20]); let transfer_tx = transaction::Transaction { version: 1, call: transaction::Call { @@ -858,13 +858,10 @@ fn test_c10l_queries() { // Call the `test` method on the contract via a query. let result = signer - .query_evm_call(&ctx, contract_address, "test", &[], &[]) + .query_evm_call(&ctx, contract_address, solabi::selector!("test()"), &()) .expect("query should succeed"); - let mut result = - ethabi::decode(&[ParamType::Address], &result).expect("output should be correct"); - - let test = result.pop().unwrap().into_address().unwrap(); + let test: H160 = solabi::decode(&result).expect("output should be correct"); assert_eq!(test, Default::default(), "msg.signer should be zeroized"); // Test call with confidential envelope. @@ -872,9 +869,8 @@ fn test_c10l_queries() { .query_evm_call_opts( &ctx, contract_address, - "test", - &[], - &[], + solabi::selector!("test()"), + &(), QueryOptions { encrypt: true, ..Default::default() @@ -882,10 +878,7 @@ fn test_c10l_queries() { ) .expect("query should succeed"); - let mut result = - ethabi::decode(&[ParamType::Address], &result).expect("output should be correct"); - - let test = result.pop().unwrap().into_address().unwrap(); + let test: H160 = solabi::decode(&result).expect("output should be correct"); assert_eq!(test, Default::default(), "msg.signer should be zeroized"); } @@ -921,9 +914,8 @@ fn test_fee_refunds() { let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "name", - &[], - &[], + solabi::selector!("name()"), + &(), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -971,12 +963,11 @@ fn test_fee_refunds() { let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "transfer", - &[ParamType::Address, ParamType::Uint(256)], - &[ - Token::Address(contract_address.into()), - Token::Uint(u128::MAX.into()), // Too much so it reverts. - ], + solabi::selector!("transfer(address,uint256)"), + &( + solabi::Address(contract_address.into()), + solabi::U256::new(u128::MAX.into()), // Too much so it reverts. + ), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1056,9 +1047,8 @@ fn test_transfer_event() { let dispatch_result = signer.call_evm_opts( &mut ctx, contract_address, - "withdraw", - &[ParamType::Uint(256)], - &[Token::Uint(1_000_000_000.into())], + solabi::selector!("withdraw(uint256)"), + &(solabi::U256::new(1_000_000_000_u128),), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1147,17 +1137,17 @@ fn test_whitelisted_magic_slots() { let non_whitelisted_slot = &<[u8; 32]>::from_hex("0000000000000000000000000000000000000000000000000000000000000123") .unwrap(); + let slot_value: &[u8; 32] = b"Hello, world!\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // Call the `setSlot` method on the contract. let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "setSlot", - &[ParamType::FixedBytes(32), ParamType::FixedBytes(32)], - &[ - Token::FixedBytes(eip_1967_implementation_slot.to_vec()), - Token::FixedBytes(b"Hello, world!".to_vec()), - ], + solabi::selector!("setSlot(bytes32,bytes32)"), + &( + solabi::Bytes(*eip_1967_implementation_slot), + solabi::Bytes(*slot_value), + ), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1174,12 +1164,11 @@ fn test_whitelisted_magic_slots() { let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "setSlot", - &[ParamType::FixedBytes(32), ParamType::FixedBytes(32)], - &[ - Token::FixedBytes(non_whitelisted_slot.to_vec()), - Token::FixedBytes(b"Hello, world!".to_vec()), - ], + solabi::selector!("setSlot(bytes32,bytes32)"), + &( + solabi::Bytes(*non_whitelisted_slot), + solabi::Bytes(*slot_value), + ), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1206,6 +1195,10 @@ fn test_whitelisted_magic_slots() { let result: Vec = cbor::from_value(result).unwrap(); let mut expected = b"Hello, world!".to_vec(); expected.extend(vec![0; 32 - expected.len()]); + assert_eq!( + expected, slot_value, + "expected result should be equal to slot_value" + ); assert_eq!(result, expected, "result should be correct"); // Query the storage slot for the non-whitelisted slot. @@ -1259,9 +1252,8 @@ fn test_return_value_limits() { let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "testSuccess", - &[], - &[], + solabi::selector!("testSuccess()"), + &(), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1281,9 +1273,8 @@ fn test_return_value_limits() { let dispatch_result = signer.call_evm_opts( &ctx, contract_address, - "testRevert", - &[], - &[], + solabi::selector!("testRevert()"), + &(), CallOptions { fee: Fee { amount: token::BaseUnits::new(1_000_000, Denomination::NATIVE), @@ -1316,9 +1307,8 @@ fn test_return_value_limits() { .query_evm_call_opts( &ctx, contract_address, - "testSuccess", - &[], - &[], + solabi::selector!("testSuccess()"), + &(), Default::default(), ) .expect("query should succeed"); @@ -1373,13 +1363,6 @@ impl AccountToken for TestErcToken { } } -fn downcast_uint(uint: ðabi::Uint) -> u128 { - if uint.bits() > 128 { - panic!("ethabi::uint too large"); - } - uint.as_u128() -} - #[test] fn test_erc20_dispatch() { let mut mock = mock::Mock::default(); @@ -1399,79 +1382,71 @@ fn test_erc20_dispatch() { // Test dispatch. let result = signer - .query_evm_call(&ctx, contract_address, "name", &[], &[]) + .query_evm_call(&ctx, contract_address, solabi::selector!("name()"), &()) .expect("query should succeed"); - let result = ethabi::decode(&[ParamType::String], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_string().unwrap(); - assert_eq!(test, "Test Token", "token name should be correct"); + let (result,) = solabi::decode::<(String,)>(&result).expect("output should be correct"); + assert_eq!(result, "Test Token", "token name should be correct"); let result = signer - .query_evm_call(&ctx, contract_address, "symbol", &[], &[]) + .query_evm_call(&ctx, contract_address, solabi::selector!("symbol()"), &()) .expect("query should succeed"); - let result = ethabi::decode(&[ParamType::String], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_string().unwrap(); - assert_eq!(test, "TTOK", "token symbol should be correct"); + let (result,) = solabi::decode::<(String,)>(&result).expect("output should be correct"); + assert_eq!(result, "TTOK", "token symbol should be correct"); let result = signer - .query_evm_call(&ctx, contract_address, "decimals", &[], &[]) + .query_evm_call(&ctx, contract_address, solabi::selector!("decimals()"), &()) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - assert_eq!(downcast_uint(&test), 18, "decimals should be correct"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result.as_u128(), 18, "decimals should be correct"); let result = signer - .query_evm_call(&ctx, contract_address, "totalSupply", &[], &[]) + .query_evm_call( + &ctx, + contract_address, + solabi::selector!("totalSupply()"), + &(), + ) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - assert_eq!(downcast_uint(&test), 0, "total supply should be correct"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result.as_u128(), 0, "total supply should be correct"); let result = signer .query_evm_call( &ctx, contract_address, - "balanceOf", - &[ParamType::Address], - &[Token::Address(dave)], + solabi::selector!("balanceOf(address)"), + &(solabi::Address(dave.into()),), ) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - assert_eq!(downcast_uint(&test), 0, "dave's balance should be correct"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result.as_u128(), 0, "dave's balance should be correct"); let result = signer .query_evm_call( &ctx, contract_address, - "transfer", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(erin), Token::Uint(0.into())], + solabi::selector!("transfer(address,uint256)"), + &(solabi::Address(erin.into()), solabi::U256::new(0_u128)), ) .expect("query should succeed"); - let result = ethabi::decode(&[ParamType::Bool], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_bool().unwrap(); - assert_eq!(test, true, "transfer from dave to erin should happen"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result, true, "transfer from dave to erin should happen"); let result = signer .query_evm_call( &ctx, contract_address, - "transferFrom", - &[ParamType::Address, ParamType::Address, ParamType::Uint(256)], - &[ - Token::Address(erin), - Token::Address(dave), - Token::Uint(0.into()), - ], + solabi::selector!("transferFrom(address,address,uint256)"), + &( + solabi::Address(erin.into()), + solabi::Address(dave.into()), + solabi::U256::new(0_u128), + ), ) .expect("query should succeed"); - let result = ethabi::decode(&[ParamType::Bool], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_bool().unwrap(); + let result = solabi::decode::(&result).expect("output should be correct"); assert_eq!( - test, true, + result, true, "transfer from erin to dave by dave should happen" ); @@ -1479,36 +1454,30 @@ fn test_erc20_dispatch() { .query_evm_call( &ctx, contract_address, - "approve", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(erin), Token::Uint(0.into())], + solabi::selector!("approve(address,uint256)"), + &(solabi::Address(erin.into()), solabi::U256::new(0_u128)), ) .expect("query should succeed"); - let result = ethabi::decode(&[ParamType::Bool], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_bool().unwrap(); - assert_eq!(test, true, "dave's approval for erin should succeed"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result, true, "dave's approval for erin should succeed"); let result = signer .query_evm_call( &ctx, contract_address, - "allowance", - &[ParamType::Address, ParamType::Address], - &[Token::Address(dave), Token::Address(erin)], + solabi::selector!("allowance(address,address)"), + &(solabi::Address(dave.into()), solabi::Address(erin.into())), ) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - assert_eq!(downcast_uint(&test), 0, "allowance should be correct"); + let result = solabi::decode::(&result).expect("output should be correct"); + assert_eq!(result.as_u128(), 0, "allowance should be correct"); signer .query_evm_call( &ctx, contract_address, - "mint", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(dave), Token::Uint(0.into())], + solabi::selector!("mint(address,uint256)"), + &(solabi::Address(dave.into()), solabi::U256::new(0_u128)), ) .expect("query should succeed"); @@ -1516,9 +1485,8 @@ fn test_erc20_dispatch() { .query_evm_call( &ctx, contract_address, - "burn", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(dave), Token::Uint(0.into())], + solabi::selector!("burn(address,uint256)"), + &(solabi::Address(dave.into()), solabi::U256::new(0_u128)), ) .expect("query should succeed"); } @@ -1549,15 +1517,12 @@ fn get_balance(ctx: &C, signer: &EvmSigner, address: &primitive_type .query_evm_call( ctx, contract_address, - "balanceOf", - &[ParamType::Address], - &[Token::Address(*address)], + solabi::selector!("balanceOf(address)"), + &(solabi::Address((*address).into()),), ) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - downcast_uint(&test) + let result = solabi::decode::(&result).expect("output should be correct"); + result.as_u128() } fn get_allowance( @@ -1571,15 +1536,15 @@ fn get_allowance( .query_evm_call( ctx, contract_address, - "allowance", - &[ParamType::Address, ParamType::Address], - &[Token::Address(*owner), Token::Address(*spender)], + solabi::selector!("allowance(address,address)"), + &( + solabi::Address((*owner).into()), + solabi::Address((*spender).into()), + ), ) .expect("query should succeed"); - let result = - ethabi::decode(&[ParamType::Uint(256)], &result).expect("output should be correct"); - let test = result.first().unwrap().clone().into_uint().unwrap(); - downcast_uint(&test) + let result = solabi::decode::(&result).expect("output should be correct"); + result.as_u128() } #[test] @@ -1614,9 +1579,8 @@ fn test_erc20_minting_burning() { let result = signer_dave.call_evm( &ctx, contract_address, - "mint", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(dave), Token::Uint(10.into())], + solabi::selector!("mint(address,uint256)"), + &(solabi::Address(dave.into()), solabi::U256::new(10_u128)), ); let logs = decode_evm_logs(&result.tags); assert_eq!(logs.len(), 1, "1 evm log should be emitted"); @@ -1635,7 +1599,7 @@ fn test_erc20_minting_burning() { // Minting is a transfer from address 0. H256::zero(), // The recipient was dave. - H256::from_slice(ðabi::encode(&[Token::Address(dave)])), + H256::from_slice(&solabi::encode(&[solabi::Address(dave.into())])), ], "transfer event topics should be correct" ); @@ -1648,9 +1612,8 @@ fn test_erc20_minting_burning() { let result = signer_dave.call_evm( &ctx, contract_address, - "burn", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(dave), Token::Uint(5.into())], + solabi::selector!("burn(address,uint256)"), + &(solabi::Address(dave.into()), solabi::U256::new(5_u128)), ); let logs = decode_evm_logs(&result.tags); assert_eq!(logs.len(), 1, "1 evm log should be emitted"); @@ -1667,7 +1630,7 @@ fn test_erc20_minting_burning() { .unwrap() ), // The sender was dave. - H256::from_slice(ðabi::encode(&[Token::Address(dave)])), + H256::from_slice(&solabi::encode(&[solabi::Address(dave.into())])), // Burning is a transfer to address 0. H256::zero(), ], @@ -1686,13 +1649,18 @@ fn test_erc20_minting_burning() { if s == 0 && r == 0 { continue; } - for method in &["mint", "burn"] { + for method in [ + solabi::selector!("mint(address,uint256)"), + solabi::selector!("burn(address,uint256)"), + ] { let result = signer.call_evm( &ctx, contract_address, method, - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(*recipient), Token::Uint(3.into())], + &( + solabi::Address((*recipient).into()), + solabi::U256::new(3_u128), + ), ); assert_eq!(result.result.is_success(), false, "minting should fail"); if let module::CallResult::Failed { @@ -1753,9 +1721,8 @@ fn test_erc20_allowances() { let result = signer_dave.call_evm( &ctx, contract_address, - "approve", - &[ParamType::Address, ParamType::Uint(256)], - &[Token::Address(erin), Token::Uint(5.into())], + solabi::selector!("approve(address,uint256)"), + &(solabi::Address(erin.into()), solabi::U256::new(5_u128)), ); assert_eq!(result.result.is_success(), true, "approve should succeed"); let logs = decode_evm_logs(&result.tags); @@ -1773,9 +1740,9 @@ fn test_erc20_allowances() { .unwrap() ), // Owner is dave. - H256::from_slice(ðabi::encode(&[Token::Address(dave)])), + H256::from_slice(&solabi::encode(&[solabi::Address(dave.into())])), // Happy spender is erin. - H256::from_slice(ðabi::encode(&[Token::Address(erin)])), + H256::from_slice(&solabi::encode(&[solabi::Address(erin.into())])), ], "approval event topics should be correct" ); @@ -1789,13 +1756,12 @@ fn test_erc20_allowances() { let result = signer_erin.call_evm( &ctx, contract_address, - "transferFrom", - &[ParamType::Address, ParamType::Address, ParamType::Uint(256)], - &[ - Token::Address(dave), - Token::Address(erin), - Token::Uint(7.into()), - ], + solabi::selector!("transferFrom(address,address,uint256)"), + &( + solabi::Address(dave.into()), + solabi::Address(erin.into()), + solabi::U256::new(7_u128), + ), ); assert_eq!( result.result.is_success(), @@ -1817,13 +1783,12 @@ fn test_erc20_allowances() { let result = signer_erin.call_evm( &ctx, contract_address, - "transferFrom", - &[ParamType::Address, ParamType::Address, ParamType::Uint(256)], - &[ - Token::Address(dave), - Token::Address(erin), - Token::Uint(3.into()), - ], + solabi::selector!("transferFrom(address,address,uint256)"), + &( + solabi::Address(dave.into()), + solabi::Address(erin.into()), + solabi::U256::new(3_u128), + ), ); assert_eq!( result.result.is_success(), @@ -1845,9 +1810,9 @@ fn test_erc20_allowances() { .unwrap() ), // The sender was dave. - H256::from_slice(ðabi::encode(&[Token::Address(dave)])), + H256::from_slice(&solabi::encode(&[solabi::Address(dave.into())])), // The sender was dave. - H256::from_slice(ðabi::encode(&[Token::Address(erin)])), + H256::from_slice(&solabi::encode(&[solabi::Address(erin.into())])), ], "transfer event topics should be correct" ); diff --git a/runtime-sdk/modules/evm/src/types.rs b/runtime-sdk/modules/evm/src/types.rs index b16c8eda081..6dd87c9504d 100644 --- a/runtime-sdk/modules/evm/src/types.rs +++ b/runtime-sdk/modules/evm/src/types.rs @@ -204,7 +204,7 @@ mod eth { construct_fixed_hash!(H256(32)); construct_uint!(U256(4)); - macro_rules! impl_upstream_conversions { + macro_rules! impl_upstream_conversions_primitive_types { ($($ty:ident),* $(,)?) => { $( impl From<$ty> for primitive_types::$ty { @@ -222,6 +222,68 @@ mod eth { } } - impl_upstream_conversions!(H160, H256, U256); + impl_upstream_conversions_primitive_types!(H160, H256, U256); + + /// Add solabi::Decode implementation for H160. + impl solabi::decode::Decode for H160 { + fn is_dynamic() -> bool { + false // Addresses are fixed-size. + } + + fn decode( + decoder: &mut solabi::decode::Decoder, + ) -> Result { + // Decode as solabi::Address, then convert to H160. + let solabi_addr = solabi::Address::decode(decoder)?; + Ok(H160::from(solabi_addr)) + } + } + + // Add solabi::Encode implementation for H160. + impl solabi::encode::Encode for H160 { + fn size(&self) -> solabi::encode::Size { + // Delegate to solabi::Address's size implementation. + solabi::Address::from(*self).size() + } + + fn encode(&self, encoder: &mut solabi::encode::Encoder) { + // Convert to solabi::Address and encode. + solabi::Address::from(*self).encode(encoder); + } + } + + impl From for H160 { + fn from(addr: solabi::Address) -> Self { + H160(addr.0) + } + } + + impl From for solabi::Address { + fn from(addr: H160) -> Self { + solabi::Address(addr.0) + } + } + + impl From for U256 { + fn from(u: solabi::U256) -> Self { + let u64_array: [u64; 4] = [ + (u.0[0] >> 64) as u64, // High 64 bits of first u128. + u.0[0] as u64, // Low 64 bits of first u128. + (u.0[1] >> 64) as u64, // High 64 bits of second u128. + u.0[1] as u64, // Low 64 bits of second u128. + ]; + U256(u64_array) + } + } + + impl From for solabi::U256 { + fn from(u: U256) -> Self { + let u128_array: [u128; 2] = [ + ((u.0[0] as u128) << 64) | (u.0[1] as u128), // Combine first two u64s into first u128. + ((u.0[2] as u128) << 64) | (u.0[3] as u128), // Combine last two u64s into second u128. + ]; + solabi::U256(u128_array) + } + } } pub use eth::{H160, H256, U256}; diff --git a/runtime-sdk/modules/rofl-market/Cargo.toml b/runtime-sdk/modules/rofl-market/Cargo.toml index 19e6c07c9e5..b3dcf2238e4 100644 --- a/runtime-sdk/modules/rofl-market/Cargo.toml +++ b/runtime-sdk/modules/rofl-market/Cargo.toml @@ -14,7 +14,7 @@ oasis-runtime-sdk-evm = { path = "../evm" } # Third party. anyhow = "1.0" base64 = "0.22.1" -ethabi = { version = "18.0.0", default-features = false, features = ["std"] } +solabi = "0.3.0" once_cell = "1.8.0" rustc-hex = "2.0.1" thiserror = "1.0" diff --git a/runtime-sdk/modules/rofl-market/src/payment.rs b/runtime-sdk/modules/rofl-market/src/payment.rs index 32742e5d492..2aff3f4cc85 100644 --- a/runtime-sdk/modules/rofl-market/src/payment.rs +++ b/runtime-sdk/modules/rofl-market/src/payment.rs @@ -43,12 +43,29 @@ pub trait PaymentMethod { ) -> Result<(), Error>; } -/// Name of the method invoked for the `pay` action for `Payment::EvmContract`. -const EVM_CONTRACT_PAY: &str = "rmpPay"; -/// Name of the method invoked for the `refund` action for `Payment::EvmContract`. -const EVM_CONTRACT_REFUND: &str = "rmpRefund"; -/// Name of the method invoked for the `claim` action for `Payment::EvmContract`. -const EVM_CONTRACT_CLAIM: &str = "rmpClaim"; +/// Type of the method invoked for the `pay` action for `Payment::EvmContract`. +/// rmpPay(term, termCount, from, data) +#[allow(clippy::type_complexity)] +const EVM_CONTRACT_PAY: solabi::FunctionEncoder< + (u8, u64, solabi::Address, solabi::Bytes>), + (bool,), +> = solabi::FunctionEncoder::new(solabi::selector!("rmpPay(uint8,uint64,address,bytes)")); + +/// Type of the method invoked for the `refund` action for `Payment::EvmContract`. +/// rmpRefund(to, data) +#[allow(clippy::type_complexity)] +const EVM_CONTRACT_REFUND: solabi::FunctionEncoder< + (solabi::Address, solabi::Bytes>), + (bool,), +> = solabi::FunctionEncoder::new(solabi::selector!("rmpRefund(address,bytes)")); + +/// Type of the method invoked for the `claim` action for `Payment::EvmContract`. +/// rmpClaim(claimableTime, paidTime, to, data) +#[allow(clippy::type_complexity)] +const EVM_CONTRACT_CLAIM: solabi::FunctionEncoder< + (u64, u64, solabi::Address, solabi::Bytes>), + (bool,), +> = solabi::FunctionEncoder::new(solabi::selector!("rmpClaim(uint64,uint64,address,bytes)")); impl PaymentMethod for Payment { fn pay( @@ -96,25 +113,12 @@ impl PaymentMethod for Payment { body: cbor::to_value(oasis_runtime_sdk_evm::types::Call { address: *address, value: 0.into(), - data: [ - ethabi::short_signature( - EVM_CONTRACT_PAY, - &[ - ethabi::ParamType::Uint(8), // term - ethabi::ParamType::Uint(64), // termCount - ethabi::ParamType::Address, // from - ethabi::ParamType::Bytes, // data - ], - ) - .to_vec(), - ethabi::encode(&[ - ethabi::Token::Uint(term.as_u8().into()), - ethabi::Token::Uint(term_count.into()), - ethabi::Token::Address(from.into()), - ethabi::Token::Bytes(data.clone()), - ]), - ] - .concat(), + data: EVM_CONTRACT_PAY.encode_params(&( + term.as_u8(), + term_count, + solabi::Address(from.into()), + solabi::Bytes(data.clone()), + )), }), max_depth: 8, max_gas: remaining_gas, @@ -179,7 +183,7 @@ impl PaymentMethod for Payment { Self::EvmContract { address, data } => { // EVM contract call that handles the refund. This requires that the caller is a // compatible address. - use ethabi::ethereum_types::H160; + use oasis_runtime_sdk_evm::types::H160; let remaining_gas = ::Core::remaining_tx_gas(); if instance.refund_data.len() != H160::len_bytes() { @@ -195,21 +199,10 @@ impl PaymentMethod for Payment { body: cbor::to_value(oasis_runtime_sdk_evm::types::Call { address: *address, value: 0.into(), - data: [ - ethabi::short_signature( - EVM_CONTRACT_REFUND, - &[ - ethabi::ParamType::Address, // to - ethabi::ParamType::Bytes, // data - ], - ) - .to_vec(), - ethabi::encode(&[ - ethabi::Token::Address(refund_address), - ethabi::Token::Bytes(data.clone()), - ]), - ] - .concat(), + data: EVM_CONTRACT_REFUND.encode_params(&( + solabi::Address(refund_address.into()), + solabi::Bytes(data.clone()), + )), }), max_depth: 8, max_gas: remaining_gas, @@ -282,8 +275,8 @@ impl PaymentMethod for Payment { )?; } Self::EvmContract { address, data } => { - let provider_address = match provider.payment_address { - PaymentAddress::Eth(address) => address.into(), + let provider_address: solabi::Address = match provider.payment_address { + PaymentAddress::Eth(address) => solabi::Address(address), _ => { return Err(Error::PaymentFailed( "incompatible payment address".to_string(), @@ -300,25 +293,12 @@ impl PaymentMethod for Payment { body: cbor::to_value(oasis_runtime_sdk_evm::types::Call { address: *address, value: 0.into(), - data: [ - ethabi::short_signature( - EVM_CONTRACT_CLAIM, - &[ - ethabi::ParamType::Uint(64), // claimableTime - ethabi::ParamType::Uint(64), // paidTime - ethabi::ParamType::Address, // to - ethabi::ParamType::Bytes, // data - ], - ) - .to_vec(), - ethabi::encode(&[ - ethabi::Token::Uint(claimable_time.into()), - ethabi::Token::Uint(paid_time.into()), - ethabi::Token::Address(provider_address), - ethabi::Token::Bytes(data.clone()), - ]), - ] - .concat(), + data: EVM_CONTRACT_CLAIM.encode_params(&( + u64::try_from(claimable_time).unwrap(), + paid_time, + provider_address, + solabi::Bytes(data.clone()), + )), }), max_depth: 8, max_gas: remaining_gas,