From ab6fa78511acef0b354b2c53a1f00b59efbea05d Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Tue, 12 Sep 2023 16:50:45 +0100 Subject: [PATCH 1/3] feat: possibility to set metadata for erc20 contracts --- CHANGES.md | 5 + engine-standalone-storage/src/sync/mod.rs | 19 ++- engine-standalone-storage/src/sync/types.rs | 12 ++ engine-tests/src/tests/erc20.rs | 51 +++++++ engine-types/src/parameters/connector.rs | 30 ++++ engine-types/src/types/mod.rs | 18 ++- engine/src/contract_methods/connector.rs | 56 +++++++- engine/src/contract_methods/mod.rs | 8 +- engine/src/engine.rs | 144 +++++++++++++++++++- engine/src/errors.rs | 4 + engine/src/lib.rs | 29 +++- 11 files changed, 353 insertions(+), 23 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 27b763b35..5f44c5899 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Added functions for setting and getting metadata of ERC-20 contracts deployed with `deploy_erc20_token` transaction + by [@aleksuss]. ([#837]) + +[#837]: https://github.com/aurora-is-near/aurora-engine/pull/837 + ## [3.0.0] 2023-08-28 ### Fixes diff --git a/engine-standalone-storage/src/sync/mod.rs b/engine-standalone-storage/src/sync/mod.rs index c4cc8c14a..06995af47 100644 --- a/engine-standalone-storage/src/sync/mod.rs +++ b/engine-standalone-storage/src/sync/mod.rs @@ -205,20 +205,21 @@ pub fn parse_transaction_kind( TransactionKind::SetKeyManager(args) } TransactionKindTag::AddRelayerKey => { - let args = - aurora_engine::parameters::RelayerKeyArgs::try_from_slice(&bytes).map_err(f)?; + let args = parameters::RelayerKeyArgs::try_from_slice(&bytes).map_err(f)?; TransactionKind::AddRelayerKey(args) } TransactionKindTag::RemoveRelayerKey => { - let args = - aurora_engine::parameters::RelayerKeyArgs::try_from_slice(&bytes).map_err(f)?; + let args = parameters::RelayerKeyArgs::try_from_slice(&bytes).map_err(f)?; TransactionKind::RemoveRelayerKey(args) } TransactionKindTag::StartHashchain => { - let args = - aurora_engine::parameters::StartHashchainArgs::try_from_slice(&bytes).map_err(f)?; + let args = parameters::StartHashchainArgs::try_from_slice(&bytes).map_err(f)?; TransactionKind::StartHashchain(args) } + TransactionKindTag::SetErc20Metadata => { + let args = parameters::SetErc20MetadataArgs::try_from_slice(&bytes).map_err(f)?; + TransactionKind::SetErc20Metadata(args) + } TransactionKindTag::Unknown => { return Err(ParseTransactionKindError::UnknownMethodName { name: method_name.into(), @@ -609,6 +610,12 @@ fn non_submit_execute( TransactionKind::StartHashchain(_) => { contract_methods::admin::start_hashchain(io, env)?; + None + } + TransactionKind::SetErc20Metadata(_) => { + let mut handler = crate::promise::NoScheduler { promise_data }; + contract_methods::connector::set_erc20_metadata(io, env, &mut handler)?; + None } }; diff --git a/engine-standalone-storage/src/sync/types.rs b/engine-standalone-storage/src/sync/types.rs index 081a4e24d..bafac0aea 100644 --- a/engine-standalone-storage/src/sync/types.rs +++ b/engine-standalone-storage/src/sync/types.rs @@ -145,6 +145,8 @@ pub enum TransactionKind { /// Remove the relayer public function call access key RemoveRelayerKey(parameters::RelayerKeyArgs), StartHashchain(parameters::StartHashchainArgs), + /// Set metadata of ERC-20 contract. + SetErc20Metadata(parameters::SetErc20MetadataArgs), /// Sentinel kind for cases where a NEAR receipt caused a /// change in Aurora state, but we failed to parse the Action. Unknown, @@ -374,6 +376,7 @@ impl TransactionKind { Self::AddRelayerKey(_) => Self::no_evm_execution("add_relayer_key"), Self::RemoveRelayerKey(_) => Self::no_evm_execution("remove_relayer_key"), Self::StartHashchain(_) => Self::no_evm_execution("start_hashchain"), + Self::SetErc20Metadata(_) => Self::no_evm_execution("set_erc20_metadata"), } } @@ -484,6 +487,8 @@ pub enum TransactionKindTag { RemoveRelayerKey, #[strum(serialize = "start_hashchain")] StartHashchain, + #[strum(serialize = "set_erc20_metadata")] + SetErc20Metadata, Unknown, } @@ -532,6 +537,7 @@ impl TransactionKind { args.try_to_vec().unwrap_or_default() } Self::StartHashchain(args) => args.try_to_vec().unwrap_or_default(), + Self::SetErc20Metadata(args) => args.try_to_vec().unwrap_or_default(), } } } @@ -575,6 +581,7 @@ impl From<&TransactionKind> for TransactionKindTag { TransactionKind::AddRelayerKey(_) => Self::AddRelayerKey, TransactionKind::RemoveRelayerKey(_) => Self::RemoveRelayerKey, TransactionKind::StartHashchain(_) => Self::StartHashchain, + TransactionKind::SetErc20Metadata(_) => Self::SetErc20Metadata, TransactionKind::Unknown => Self::Unknown, } } @@ -755,6 +762,7 @@ enum BorshableTransactionKind<'a> { AddRelayerKey(Cow<'a, parameters::RelayerKeyArgs>), RemoveRelayerKey(Cow<'a, parameters::RelayerKeyArgs>), StartHashchain(Cow<'a, parameters::StartHashchainArgs>), + SetErc20Metadata(Cow<'a, parameters::SetErc20MetadataArgs>), } impl<'a> From<&'a TransactionKind> for BorshableTransactionKind<'a> { @@ -807,6 +815,7 @@ impl<'a> From<&'a TransactionKind> for BorshableTransactionKind<'a> { TransactionKind::AddRelayerKey(x) => Self::AddRelayerKey(Cow::Borrowed(x)), TransactionKind::RemoveRelayerKey(x) => Self::RemoveRelayerKey(Cow::Borrowed(x)), TransactionKind::StartHashchain(x) => Self::StartHashchain(Cow::Borrowed(x)), + TransactionKind::SetErc20Metadata(x) => Self::SetErc20Metadata(Cow::Borrowed(x)), } } } @@ -880,6 +889,9 @@ impl<'a> TryFrom> for TransactionKind { Ok(Self::RemoveRelayerKey(x.into_owned())) } BorshableTransactionKind::StartHashchain(x) => Ok(Self::StartHashchain(x.into_owned())), + BorshableTransactionKind::SetErc20Metadata(x) => { + Ok(Self::SetErc20Metadata(x.into_owned())) + } } } } diff --git a/engine-tests/src/tests/erc20.rs b/engine-tests/src/tests/erc20.rs index dabeba064..8bc765886 100644 --- a/engine-tests/src/tests/erc20.rs +++ b/engine-tests/src/tests/erc20.rs @@ -8,6 +8,8 @@ use crate::utils::{ use aurora_engine::engine::EngineErrorKind; use aurora_engine::parameters::TransactionStatus; use aurora_engine_sdk as sdk; +use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; +use aurora_engine_types::parameters::connector::{Erc20Metadata, SetErc20MetadataArgs}; use bstr::ByteSlice; use libsecp256k1::SecretKey; @@ -237,6 +239,55 @@ fn deploy_erc_20_out_of_gas() { ); } +#[test] +fn test_erc20_get_and_set_metadata() { + let mut runner = utils::deploy_runner(); + let erc20_address = runner.deploy_erc20_token("token"); + let caller = runner.aurora_account_id.clone(); + let result = runner.one_shot().call( + "get_erc20_metadata", + &caller, + erc20_address.as_bytes().to_vec(), + ); + + assert!(result.is_ok()); + + let metadata = + Erc20Metadata::try_from_slice(result.unwrap().return_data.as_value().unwrap().as_slice()) + .unwrap(); + assert_eq!(metadata, Erc20Metadata::default()); + + let new_metadata = Erc20Metadata { + name: "USD Token".to_string(), + symbol: "USDT".to_string(), + decimals: 20, + }; + + let result = runner.call( + "set_erc20_metadata", + &caller, + SetErc20MetadataArgs { + erc20_address, + erc20_metadata: new_metadata.clone(), + } + .try_to_vec() + .unwrap(), + ); + assert!(result.is_ok()); + + let result = runner.one_shot().call( + "get_erc20_metadata", + &caller, + erc20_address.as_bytes().to_vec(), + ); + assert!(result.is_ok()); + + let metadata = + Erc20Metadata::try_from_slice(result.unwrap().return_data.as_value().unwrap().as_slice()) + .unwrap(); + assert_eq!(metadata, new_metadata); +} + fn get_address_erc20_balance( runner: &utils::AuroraRunner, signer: &Signer, diff --git a/engine-types/src/parameters/connector.rs b/engine-types/src/parameters/connector.rs index 5fa2483ce..b2e1474f4 100644 --- a/engine-types/src/parameters/connector.rs +++ b/engine-types/src/parameters/connector.rs @@ -220,6 +220,36 @@ impl rlp::Encodable for LogEntry { } } +/// Borsh-encoded parameters for `set_erc20_metadata` function. +#[derive(BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, Clone)] +pub struct SetErc20MetadataArgs { + /// Address of the ERC-20 contract. + pub erc20_address: Address, + /// Metadata of the ERC-20 contract. + pub erc20_metadata: Erc20Metadata, +} + +/// Metadata of ERC-20 contract. +#[derive(BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, Clone)] +pub struct Erc20Metadata { + /// Name of the token. + pub name: String, + /// Symbol of the token. + pub symbol: String, + /// Number of decimals. + pub decimals: u8, +} + +impl Default for Erc20Metadata { + fn default() -> Self { + Self { + name: "Token".to_string(), + symbol: "TKN".to_string(), + decimals: 18, // To be compatible with MetaMask. + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/engine-types/src/types/mod.rs b/engine-types/src/types/mod.rs index f0a875ee9..87db146e9 100644 --- a/engine-types/src/types/mod.rs +++ b/engine-types/src/types/mod.rs @@ -22,11 +22,21 @@ pub type RawH256 = [u8; 32]; // Unformatted binary data of fixed length. pub type StorageUsage = u64; -/// Selector to call mint function in ERC 20 contract -/// -/// `keccak("mint(address,uint256)".as_bytes())[..4];` -#[allow(dead_code)] +/// Selector to call `mint` function in ERC 20 contract. +/// `keccak(b"mint(address,uint256)")[..4];` pub const ERC20_MINT_SELECTOR: &[u8] = &[64, 193, 15, 25]; +/// Selector to call `setMetadata` function in ERC-20 contact. +/// `keccak(b"setMetadata(string,string,uint8)")[..4];` +pub const ERC20_SET_METADATA_SELECTOR: &[u8] = &[55, 210, 194, 244]; +/// Selector to call `name` function in ERC-20 contact. +/// `keccak(b"name()")[..4];` +pub const ERC20_NAME_SELECTOR: &[u8] = &[6, 253, 222, 3]; +/// Selector to call `symbol` function in ERC-20 contact. +/// `keccak(b"symbol()")[..4];` +pub const ERC20_SYMBOL_SELECTOR: &[u8] = &[149, 216, 155, 65]; +/// Selector to call `digits` function in ERC-20 contact. +/// `keccak(b"digits()")[..4];` +pub const ERC20_DIGITS_SELECTOR: &[u8] = &[49, 60, 229, 103]; #[derive(Debug)] pub enum AddressValidationError { diff --git a/engine/src/contract_methods/connector.rs b/engine/src/contract_methods/connector.rs index 16f64f947..9eeda0b46 100644 --- a/engine/src/contract_methods/connector.rs +++ b/engine/src/contract_methods/connector.rs @@ -17,8 +17,8 @@ use aurora_engine_types::{ parameters::{ connector::{ InitCallArgs, NEP141FtOnTransferArgs, ResolveTransferCallArgs, SetContractDataCallArgs, - StorageDepositCallArgs, StorageWithdrawCallArgs, TransferCallArgs, - TransferCallCallArgs, + SetErc20MetadataArgs, StorageDepositCallArgs, StorageWithdrawCallArgs, + TransferCallArgs, TransferCallCallArgs, }, engine::{ errors::ParseTypeFromJsonError, DeployErc20TokenArgs, PauseEthConnectorCallArgs, @@ -404,3 +404,55 @@ pub fn set_paused_flags(io: I, env: &E) -> Result<(), Cont Ok(()) }) } + +#[named] +pub fn set_erc20_metadata( + io: I, + env: &E, + handler: &mut H, +) -> Result { + with_hashchain(io, env, function_name!(), |io| { + let state = state::get_state(&io)?; + require_running(&state)?; + // TODO: Define special role for this transaction. Potentially via multisig? + let is_private = env.assert_private_call(); + if is_private.is_err() { + require_owner_only(&state, &env.predecessor_account_id())?; + } + + let args: SetErc20MetadataArgs = io.read_input_borsh()?; + let current_account_id = env.current_account_id(); + let mut engine: Engine<_, E, AuroraModExp> = Engine::new_with_state( + state, + predecessor_address(&env.predecessor_account_id()), + current_account_id, + io, + env, + ); + let result = engine.set_erc20_metadata(args.erc20_address, args.erc20_metadata, handler)?; + + Ok(result) + }) +} + +pub fn get_erc20_metadata(mut io: I, env: &E) -> Result<(), ContractError> { + let erc20_address = io.read_input_arr20().map(Address::from_array)?; + let state = state::get_state(&io)?; + let current_account_id = env.current_account_id(); + let engine: Engine<_, E, AuroraModExp> = Engine::new_with_state( + state, + predecessor_address(&env.predecessor_account_id()), + current_account_id, + io, + env, + ); + let metadata = engine.get_erc20_metadata(erc20_address)?; + + io.return_output( + metadata + .try_to_vec() + .map_err(|_| errors::ERR_SERIALIZE)? + .as_slice(), + ); + Ok(()) +} diff --git a/engine/src/contract_methods/mod.rs b/engine/src/contract_methods/mod.rs index 98332b65a..3a0c949e9 100644 --- a/engine/src/contract_methods/mod.rs +++ b/engine/src/contract_methods/mod.rs @@ -47,7 +47,7 @@ impl + Send + Sync + 'static> From for ContractError { /// This type is structurally the same as `ContractError`, but /// importantly `ContractError` implements `From>` -/// for easy usage in the this module's function implementations, while +/// for easy usage in this module's function implementations, while /// `ErrorMessage` implements `AsRef<[u8]>` for compatibility with /// `sdk_unwrap`. pub struct ErrorMessage { @@ -60,14 +60,14 @@ impl AsRef<[u8]> for ErrorMessage { } } -fn require_running(state: &crate::state::EngineState) -> Result<(), ContractError> { +fn require_running(state: &state::EngineState) -> Result<(), ContractError> { if state.is_paused { return Err(errors::ERR_PAUSED.into()); } Ok(()) } -fn require_paused(state: &crate::state::EngineState) -> Result<(), ContractError> { +fn require_paused(state: &state::EngineState) -> Result<(), ContractError> { if !state.is_paused { return Err(errors::ERR_RUNNING.into()); } @@ -75,7 +75,7 @@ fn require_paused(state: &crate::state::EngineState) -> Result<(), ContractError } fn require_owner_only( - state: &crate::state::EngineState, + state: &state::EngineState, predecessor_account_id: &AccountId, ) -> Result<(), ContractError> { if &state.owner_id != predecessor_account_id { diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 19b2edffc..abee82159 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -28,12 +28,15 @@ use crate::prelude::precompiles::Precompiles; use crate::prelude::transactions::{EthTransactionKind, NormalizedEthTransaction}; use crate::prelude::{ address_to_key, bytes_to_key, sdk, storage_to_key, u256_to_arr, vec, AccountId, Address, - BTreeMap, BorshDeserialize, KeyPrefix, PromiseArgs, PromiseCreateArgs, ToString, Vec, Wei, - Yocto, ERC20_MINT_SELECTOR, H160, H256, U256, + BTreeMap, BorshDeserialize, KeyPrefix, PromiseArgs, PromiseCreateArgs, Vec, Wei, Yocto, + ERC20_DIGITS_SELECTOR, ERC20_MINT_SELECTOR, ERC20_NAME_SELECTOR, ERC20_SET_METADATA_SELECTOR, + ERC20_SYMBOL_SELECTOR, H160, H256, U256, }; use crate::state::EngineState; use aurora_engine_modexp::{AuroraModExp, ModExpAlgorithm}; use aurora_engine_precompiles::PrecompileConstructorContext; +use aurora_engine_types::parameters::connector::Erc20Metadata; +use aurora_engine_types::parameters::engine::FunctionCallArgsV2; use core::cell::RefCell; use core::iter::once; @@ -342,6 +345,25 @@ impl AsRef<[u8]> for RegisterTokenError { } } +#[derive(Debug)] +pub enum ReadMetadataError { + DecodeError, + WrongType, + NoValue, + EngineError(EngineErrorKind), +} + +impl AsRef<[u8]> for ReadMetadataError { + fn as_ref(&self) -> &[u8] { + match self { + Self::DecodeError => errors::ERR_DECODING_TOKEN, + Self::WrongType => errors::ERR_WRONG_TOKEN_TYPE, + Self::NoValue => errors::ERR_TOKEN_NO_VALUE, + Self::EngineError(e) => e.as_ref(), + } + } +} + pub struct StackExecutorParams<'a, I, E, H> { precompiles: Precompiles<'a, I, E, H>, gas_limit: u64, @@ -784,6 +806,69 @@ impl<'env, I: IO + Copy, E: Env, M: ModExpAlgorithm> Engine<'env, I, E, M> { self.io.return_output(b"\"0\""); } + /// Read metadata of ERC-20 contract. + pub fn get_erc20_metadata( + &self, + erc20_address: Address, + ) -> Result { + let name = self + .view_with_selector( + erc20_address, + ERC20_NAME_SELECTOR, + &[ethabi::ParamType::String], + )? + .into_string() + .ok_or(ReadMetadataError::WrongType)?; + let symbol = self + .view_with_selector( + erc20_address, + ERC20_SYMBOL_SELECTOR, + &[ethabi::ParamType::String], + )? + .into_string() + .ok_or(ReadMetadataError::WrongType)?; + let decimals = self + .view_with_selector( + erc20_address, + ERC20_DIGITS_SELECTOR, + &[ethabi::ParamType::Uint(8)], + )? + .into_uint() + .ok_or(ReadMetadataError::WrongType)? + .try_into() + .map_err(|_| ReadMetadataError::WrongType)?; + + Ok(Erc20Metadata { + name, + symbol, + decimals, + }) + } + + /// Set metadata of ERC-20 contract. + pub fn set_erc20_metadata( + &mut self, + erc20_address: Address, + erc20_metadata: Erc20Metadata, + handler: &mut P, + ) -> EngineResult { + let args = ethabi::encode(&[ + ethabi::Token::String(erc20_metadata.name), + ethabi::Token::String(erc20_metadata.symbol), + ethabi::Token::Uint(erc20_metadata.decimals.into()), + ]); + let input = [ERC20_SET_METADATA_SELECTOR, &args].concat(); + + self.call_with_args( + CallArgs::V2(FunctionCallArgsV2 { + contract: erc20_address, + value: [0; 32], + input, + }), + handler, + ) + } + fn create_precompiles( &self, pause_flags: PrecompileFlags, @@ -821,6 +906,30 @@ impl<'env, I: IO + Copy, E: Env, M: ModExpAlgorithm> Engine<'env, I, E, M> { all_precompiles: precompiles.all_precompiles, } } + + fn view_with_selector( + &self, + contract_address: Address, + selector: &[u8], + output_types: &[ethabi::ParamType], + ) -> Result { + let result = self.view_with_args(ViewCallArgs { + sender: self.origin, + address: contract_address, + amount: [0; 32], + input: selector.to_vec(), + }); + + let output = match result.map_err(ReadMetadataError::EngineError)? { + TransactionStatus::Succeed(bytes) => bytes, + _ => Vec::new(), + }; + + ethabi::decode(output_types, &output) + .map_err(|_| ReadMetadataError::DecodeError)? + .pop() + .ok_or(ReadMetadataError::NoValue) + } } pub fn submit( @@ -1123,11 +1232,12 @@ pub fn setup_deploy_erc20_input(current_account_id: &AccountId) -> Vec { let erc20_contract = include_bytes!("../../etc/eth-contracts/res/EvmErc20.bin"); let erc20_admin_address = current_address(current_account_id); + let erc20_metadata = Erc20Metadata::default(); let deploy_args = ethabi::encode(&[ - ethabi::Token::String("Empty".to_string()), - ethabi::Token::String("EMPTY".to_string()), - ethabi::Token::Uint(ethabi::Uint::from(0)), + ethabi::Token::String(erc20_metadata.name), + ethabi::Token::String(erc20_metadata.symbol), + ethabi::Token::Uint(erc20_metadata.decimals.into()), ethabi::Token::Address(erc20_admin_address.raw()), ]); @@ -2028,6 +2138,30 @@ mod tests { assert_eq!(expected_address, actual_address); } + #[test] + fn test_get_erc20_metadata() { + let env = Fixed::default(); + let origin = aurora_engine_sdk::types::near_account_to_evm_address( + env.predecessor_account_id().as_bytes(), + ); + let current_account_id = AccountId::default(); + let storage = RefCell::new(Storage::default()); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let state = EngineState::default(); + state::set_state(&mut io, &state).unwrap(); + + let engine: Engine<_, _> = + Engine::new_with_state(state, origin, current_account_id, io, &env); + let nep141 = AccountId::new("testcoin").unwrap(); + let mut handler = Noop; + let args = DeployErc20TokenArgs { nep141 }; + let erc20_address = deploy_erc20_token(args, io, &env, &mut handler).unwrap(); + let metadata = engine.get_erc20_metadata(erc20_address).unwrap(); + + assert_eq!(metadata, Erc20Metadata::default()); + } + #[test] fn test_gas_charge_for_empty_transaction_is_zero() { let origin = Address::zero(); diff --git a/engine/src/errors.rs b/engine/src/errors.rs index f1e2d1138..7c6823da7 100644 --- a/engine/src/errors.rs +++ b/engine/src/errors.rs @@ -94,3 +94,7 @@ pub const ERR_SAME_KEY_MANAGER: &[u8] = b"ERR_SAME_KEY_MANAGER"; pub const ERR_FUNCTION_CALL_KEY_NOT_FOUND: &[u8] = b"ERR_FUNCTION_CALL_KEY_NOT_FOUND"; pub const ERR_KEY_MANAGER_IS_NOT_SET: &[u8] = b"ERR_KEY_MANAGER_IS_NOT_SET"; pub const ERR_ACCOUNTS_COUNTER_OVERFLOW: &str = "ERR_ACCOUNTS_COUNTER_OVERFLOW"; +pub const ERR_DECODING_TOKEN: &[u8] = b"ERR_DECODING_TOKEN"; +pub const ERR_GETTING_TOKEN: &[u8] = b"ERR_GETTING_TOKEN"; +pub const ERR_WRONG_TOKEN_TYPE: &[u8] = b"ERR_WRONG_TOKEN_TYPE"; +pub const ERR_TOKEN_NO_VALUE: &[u8] = b"ERR_TOKEN_NO_VALUE"; diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 394ba375e..5ac8c179c 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -423,6 +423,17 @@ mod contract { .sdk_unwrap(); } + /// Set metadata of ERC-20 contract. + #[no_mangle] + pub extern "C" fn set_erc20_metadata() { + let io = Runtime; + let env = Runtime; + let mut handler = Runtime; + contract_methods::connector::set_erc20_metadata(io, &env, &mut handler) + .map_err(ContractError::msg) + .sdk_unwrap(); + } + /// Callback invoked by exit to NEAR precompile to handle potential /// errors in the exit call. #[no_mangle] @@ -547,6 +558,16 @@ mod contract { .sdk_unwrap(); } + /// Return metadata of the ERC-20 contract. + #[no_mangle] + pub extern "C" fn get_erc20_metadata() { + let io = Runtime; + let env = ViewEnv; + contract_methods::connector::get_erc20_metadata(io, &env) + .map_err(ContractError::msg) + .sdk_unwrap(); + } + /// /// BENCHMARKING METHODS /// @@ -556,7 +577,9 @@ mod contract { use crate::prelude::U256; let mut io = Runtime; let mut state = state::get_state(&io).sdk_unwrap(); - require_owner_only(&state, &io.predecessor_account_id()); + if state.owner_id != io.predecessor_account_id() { + sdk::panic_utf8(errors::ERR_NOT_ALLOWED); + } let args: BeginChainArgs = io.read_input_borsh().sdk_unwrap(); state.chain_id = args.chain_id; state::set_state(&mut io, &state).sdk_unwrap(); @@ -577,7 +600,9 @@ mod contract { pub extern "C" fn begin_block() { let io = Runtime; let state = state::get_state(&io).sdk_unwrap(); - require_owner_only(&state, &io.predecessor_account_id()); + if state.owner_id != io.predecessor_account_id() { + sdk::panic_utf8(errors::ERR_NOT_ALLOWED); + } let _args: BeginBlockArgs = io.read_input_borsh().sdk_unwrap(); // TODO: https://github.com/aurora-is-near/aurora-engine/issues/2 } From 6c6eaffe827205a859cdd89fa9f054ccf3ac145e Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Wed, 13 Sep 2023 09:47:18 +0100 Subject: [PATCH 2/3] use json de/serialization --- engine-standalone-storage/src/sync/mod.rs | 5 ++++- engine-standalone-storage/src/sync/types.rs | 2 +- engine-tests/src/tests/erc20.rs | 16 ++++++---------- engine-types/src/parameters/connector.rs | 12 ++++++------ engine/src/contract_methods/connector.rs | 10 +++------- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/engine-standalone-storage/src/sync/mod.rs b/engine-standalone-storage/src/sync/mod.rs index 06995af47..604f77129 100644 --- a/engine-standalone-storage/src/sync/mod.rs +++ b/engine-standalone-storage/src/sync/mod.rs @@ -217,7 +217,10 @@ pub fn parse_transaction_kind( TransactionKind::StartHashchain(args) } TransactionKindTag::SetErc20Metadata => { - let args = parameters::SetErc20MetadataArgs::try_from_slice(&bytes).map_err(f)?; + let args: parameters::SetErc20MetadataArgs = + serde_json::from_slice(&bytes).map_err(|e| { + ParseTransactionKindError::failed_deserialization(tx_kind_tag, Some(e)) + })?; TransactionKind::SetErc20Metadata(args) } TransactionKindTag::Unknown => { diff --git a/engine-standalone-storage/src/sync/types.rs b/engine-standalone-storage/src/sync/types.rs index bafac0aea..5ea8c2d62 100644 --- a/engine-standalone-storage/src/sync/types.rs +++ b/engine-standalone-storage/src/sync/types.rs @@ -537,7 +537,7 @@ impl TransactionKind { args.try_to_vec().unwrap_or_default() } Self::StartHashchain(args) => args.try_to_vec().unwrap_or_default(), - Self::SetErc20Metadata(args) => args.try_to_vec().unwrap_or_default(), + Self::SetErc20Metadata(args) => serde_json::to_vec(args).unwrap_or_default(), } } } diff --git a/engine-tests/src/tests/erc20.rs b/engine-tests/src/tests/erc20.rs index 8bc765886..7f711a30a 100644 --- a/engine-tests/src/tests/erc20.rs +++ b/engine-tests/src/tests/erc20.rs @@ -8,7 +8,6 @@ use crate::utils::{ use aurora_engine::engine::EngineErrorKind; use aurora_engine::parameters::TransactionStatus; use aurora_engine_sdk as sdk; -use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize}; use aurora_engine_types::parameters::connector::{Erc20Metadata, SetErc20MetadataArgs}; use bstr::ByteSlice; use libsecp256k1::SecretKey; @@ -252,9 +251,8 @@ fn test_erc20_get_and_set_metadata() { assert!(result.is_ok()); - let metadata = - Erc20Metadata::try_from_slice(result.unwrap().return_data.as_value().unwrap().as_slice()) - .unwrap(); + let metadata: Erc20Metadata = + serde_json::from_slice(&result.unwrap().return_data.as_value().unwrap()).unwrap(); assert_eq!(metadata, Erc20Metadata::default()); let new_metadata = Erc20Metadata { @@ -266,11 +264,10 @@ fn test_erc20_get_and_set_metadata() { let result = runner.call( "set_erc20_metadata", &caller, - SetErc20MetadataArgs { + serde_json::to_vec(&SetErc20MetadataArgs { erc20_address, erc20_metadata: new_metadata.clone(), - } - .try_to_vec() + }) .unwrap(), ); assert!(result.is_ok()); @@ -282,9 +279,8 @@ fn test_erc20_get_and_set_metadata() { ); assert!(result.is_ok()); - let metadata = - Erc20Metadata::try_from_slice(result.unwrap().return_data.as_value().unwrap().as_slice()) - .unwrap(); + let metadata: Erc20Metadata = + serde_json::from_slice(&result.unwrap().return_data.as_value().unwrap()).unwrap(); assert_eq!(metadata, new_metadata); } diff --git a/engine-types/src/parameters/connector.rs b/engine-types/src/parameters/connector.rs index b2e1474f4..5b3449ea3 100644 --- a/engine-types/src/parameters/connector.rs +++ b/engine-types/src/parameters/connector.rs @@ -220,8 +220,8 @@ impl rlp::Encodable for LogEntry { } } -/// Borsh-encoded parameters for `set_erc20_metadata` function. -#[derive(BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, Clone)] +/// Parameters for `set_erc20_metadata` function. +#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct SetErc20MetadataArgs { /// Address of the ERC-20 contract. pub erc20_address: Address, @@ -230,7 +230,7 @@ pub struct SetErc20MetadataArgs { } /// Metadata of ERC-20 contract. -#[derive(BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, Clone)] +#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Erc20Metadata { /// Name of the token. pub name: String, @@ -243,9 +243,9 @@ pub struct Erc20Metadata { impl Default for Erc20Metadata { fn default() -> Self { Self { - name: "Token".to_string(), - symbol: "TKN".to_string(), - decimals: 18, // To be compatible with MetaMask. + name: "Empty".to_string(), + symbol: "EMPTY".to_string(), + decimals: 0, } } } diff --git a/engine/src/contract_methods/connector.rs b/engine/src/contract_methods/connector.rs index 9eeda0b46..f0f21e0eb 100644 --- a/engine/src/contract_methods/connector.rs +++ b/engine/src/contract_methods/connector.rs @@ -420,7 +420,8 @@ pub fn set_erc20_metadata( require_owner_only(&state, &env.predecessor_account_id())?; } - let args: SetErc20MetadataArgs = io.read_input_borsh()?; + let args: SetErc20MetadataArgs = serde_json::from_slice(&io.read_input().to_vec()) + .map_err(Into::::into)?; let current_account_id = env.current_account_id(); let mut engine: Engine<_, E, AuroraModExp> = Engine::new_with_state( state, @@ -448,11 +449,6 @@ pub fn get_erc20_metadata(mut io: I, env: &E) -> Result<() ); let metadata = engine.get_erc20_metadata(erc20_address)?; - io.return_output( - metadata - .try_to_vec() - .map_err(|_| errors::ERR_SERIALIZE)? - .as_slice(), - ); + io.return_output(&serde_json::to_vec(&metadata).map_err(|_| errors::ERR_SERIALIZE)?); Ok(()) } From 61dbd5a6c7d4fa9b30fbaf8f97afccc6073b786f Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Wed, 13 Sep 2023 10:10:35 +0100 Subject: [PATCH 3/3] increase number of functions --- engine-tests/src/tests/sanity.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/engine-tests/src/tests/sanity.rs b/engine-tests/src/tests/sanity.rs index 87699260f..00bb94b28 100644 --- a/engine-tests/src/tests/sanity.rs +++ b/engine-tests/src/tests/sanity.rs @@ -681,10 +681,12 @@ fn test_num_wasm_functions() { let module = walrus::ModuleConfig::default() .parse(runner.code.code()) .unwrap(); - let num_functions = module.funcs.iter().count(); + let expected_number = 1460; + let actual_number = module.funcs.iter().count(); + assert!( - num_functions <= 1445, - "{num_functions} is not less than 1445", + actual_number <= expected_number, + "{actual_number} is not less than {expected_number}", ); }