diff --git a/integration-tests/src/evm_permit.rs b/integration-tests/src/evm_permit.rs index 7e3f496c5..2f1d7cb60 100644 --- a/integration-tests/src/evm_permit.rs +++ b/integration-tests/src/evm_permit.rs @@ -9,9 +9,7 @@ use frame_support::traits::Contains; use frame_support::{assert_noop, assert_ok, sp_runtime::codec::Encode}; use frame_system::RawOrigin; use hydradx_runtime::evm::precompiles::{CALLPERMIT, DISPATCH_ADDR}; -use hydradx_runtime::{ - Balances, Currencies, EVMAccounts, MultiTransactionPayment, Omnipool, RuntimeCall, RuntimeOrigin, Tokens, -}; +use hydradx_runtime::{Balances, Currencies, MultiTransactionPayment, Omnipool, RuntimeCall, RuntimeOrigin, Tokens}; use libsecp256k1::{sign, Message, SecretKey}; use orml_traits::MultiCurrency; use pallet_evm_accounts::EvmNonceProvider; @@ -234,110 +232,6 @@ fn dispatch_permit_fee_should_be_paid_in_hdx_when_no_currency_is_set() { }) } -#[test] -fn fee_should_be_paid_in_hdx_when_permit_is_dispatched_and_address_is_bounded() { - TestNet::reset(); - let user_evm_address = alith_evm_address(); - let user_secret_key = alith_secret_key(); - let user_acc = MockAccount::new(alith_evm_account()); - let treasury_acc = MockAccount::new(Treasury::account_id()); - - Hydra::execute_with(|| { - init_omnipool_with_oracle_for_block_10(); - pallet_transaction_payment::pallet::NextFeeMultiplier::::put( - hydradx_runtime::MinimumMultiplier::get(), - ); - - // Prepare user evm account - bind and fund - assert_ok!(EVMAccounts::bind_evm_address(hydradx_runtime::RuntimeOrigin::signed( - user_acc.address() - ))); - assert_ok!(hydradx_runtime::Currencies::update_balance( - hydradx_runtime::RuntimeOrigin::root(), - user_acc.address(), - HDX, - 100_000_000_000_000i128, - )); - //Fund some DOT to sell in omnipool - assert_ok!(hydradx_runtime::Currencies::update_balance( - hydradx_runtime::RuntimeOrigin::root(), - user_acc.address(), - DOT, - 100_000_000i128, - )); - - let initial_treasury_hdx_balance = treasury_acc.balance(HDX); - let initial_user_hdx_balance = user_acc.balance(HDX); - let initial_user_weth_balance = user_acc.balance(WETH); - let initial_user_dot_balance = user_acc.balance(DOT); - - // just reset the weth balance to 0 - to make sure we dont have enough WETH - assert_ok!(hydradx_runtime::Currencies::update_balance( - hydradx_runtime::RuntimeOrigin::root(), - user_acc.address(), - WETH, - -(initial_user_weth_balance as i128), - )); - let initial_user_weth_balance = user_acc.balance(WETH); - assert_eq!(initial_user_weth_balance, 0); - - let omni_sell = - hydradx_runtime::RuntimeCall::Omnipool(pallet_omnipool::Call::::sell { - asset_in: DOT, - asset_out: WETH, - amount: 10_000_000, - min_buy_amount: 0, - }); - - let gas_limit = 1000000; - let deadline = U256::from(1000000000000u128); - - let permit = - pallet_evm_precompile_call_permit::CallPermitPrecompile::::generate_permit( - CALLPERMIT, - user_evm_address, - DISPATCH_ADDR, - U256::from(0), - omni_sell.encode(), - gas_limit, - U256::zero(), - deadline, - ); - let secret_key = SecretKey::parse(&user_secret_key).unwrap(); - let message = Message::parse(&permit); - let (rs, v) = sign(&message, &secret_key); - - //Execute omnipool via EVM - assert_ok!(MultiTransactionPayment::dispatch_permit( - hydradx_runtime::RuntimeOrigin::none(), - user_evm_address, - DISPATCH_ADDR, - U256::from(0), - omni_sell.encode(), - gas_limit, - deadline, - v.serialize(), - H256::from(rs.r.b32()), - H256::from(rs.s.b32()), - )); - // Verify evm fee amount - let user_hdx_balance = user_acc.balance(HDX); - let fee_amount = initial_user_hdx_balance - user_hdx_balance; - assert!(fee_amount > 0); - let new_treasury_hdx_balance = treasury_acc.balance(HDX); - let treasury_hdx_diff = new_treasury_hdx_balance - initial_treasury_hdx_balance; - assert_eq!(fee_amount, treasury_hdx_diff); - - // Verify omnipool sell - let user_weth_balance = user_acc.balance(WETH); - assert_eq!(user_weth_balance, 3_565_408_466_680); - let user_dot_balance = user_acc.balance(DOT); - assert!(user_dot_balance < initial_user_dot_balance); - let dot_diff = initial_user_dot_balance - user_dot_balance; - assert_eq!(dot_diff, 10_000_000); - }) -} - #[test] fn fee_should_be_paid_in_hdx_when_permit_is_dispatched_and_address_is_not_bounded() { TestNet::reset(); diff --git a/pallets/evm-accounts/src/lib.rs b/pallets/evm-accounts/src/lib.rs index efae74a0f..5a73237f0 100644 --- a/pallets/evm-accounts/src/lib.rs +++ b/pallets/evm-accounts/src/lib.rs @@ -142,7 +142,7 @@ pub mod pallet { #[pallet::error] #[cfg_attr(test, derive(PartialEq, Eq))] pub enum Error { - /// EVM Account's nonce is not zero + /// Active EVM account cannot be bound TruncatedAccountAlreadyUsed, /// Address is already bound AddressAlreadyBound, @@ -201,6 +201,10 @@ pub mod pallet { Error::::AddressAlreadyBound ); + ensure!( + !Self::is_evm_account(who.clone()), + Error::::TruncatedAccountAlreadyUsed + ); let nonce = T::EvmNonceProvider::get_nonce(evm_address); ensure!(nonce.is_zero(), Error::::TruncatedAccountAlreadyUsed); @@ -311,6 +315,12 @@ pub mod pallet { } } +impl Pallet { + fn _is_evm_account(account_id: &[u8; 32]) -> bool { + &account_id[0..4] == b"ETH\0" && account_id[24..32] == [0u8; 8] + } +} + impl InspectEvmAccounts for Pallet where T::AccountId: AsRef<[u8; 32]> + frame_support::traits::IsType, @@ -318,13 +328,17 @@ where /// Returns `True` if the account is EVM truncated account. fn is_evm_account(account_id: T::AccountId) -> bool { let account_ref = account_id.as_ref(); - &account_ref[0..4] == b"ETH\0" && account_ref[24..32] == [0u8; 8] + Self::_is_evm_account(account_ref) } /// Get the EVM address from the substrate address. fn evm_address(account_id: &impl AsRef<[u8; 32]>) -> EvmAddress { let acc = account_id.as_ref(); - EvmAddress::from_slice(&acc[..20]) + if Self::_is_evm_account(acc) { + EvmAddress::from_slice(&acc[4..24]) + } else { + EvmAddress::from_slice(&acc[..20]) + } } /// Get the truncated address from the EVM address. diff --git a/pallets/evm-accounts/src/tests.rs b/pallets/evm-accounts/src/tests.rs index 0ae79fbb2..dea69a38e 100644 --- a/pallets/evm-accounts/src/tests.rs +++ b/pallets/evm-accounts/src/tests.rs @@ -58,6 +58,47 @@ fn eth_address_should_convert_to_full_address_when_bound() { }); } +#[test] +fn evm_address_is_reversible_from_account_id() { + ExtBuilder::default().build().execute_with(|| { + let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]); + assert_eq!( + EVMAccounts::evm_address(&EVMAccounts::account_id(evm_address)), + evm_address + ); + }); +} + +#[test] +fn account_id_is_reversible_from_evm_address() { + ExtBuilder::default().build().execute_with(|| { + let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]); + assert_eq!( + EVMAccounts::account_id(EVMAccounts::evm_address(&EVMAccounts::account_id(evm_address))), + EVMAccounts::account_id(evm_address) + ); + }); +} + +#[test] +fn account_id_is_reversible_from_bound_evm_address() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EVMAccounts::bind_evm_address(RuntimeOrigin::signed(ALICE))); + assert_eq!(EVMAccounts::account_id(EVMAccounts::evm_address(&ALICE)), ALICE); + }); +} + +#[test] +fn bound_evm_address_is_reversible_from_account_id() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(EVMAccounts::bind_evm_address(RuntimeOrigin::signed(ALICE))); + assert_eq!( + EVMAccounts::evm_address(&EVMAccounts::account_id(EVMAccounts::evm_address(&ALICE))), + EVMAccounts::evm_address(&ALICE) + ); + }); +} + #[test] fn bind_address_should_fail_when_nonce_is_not_zero() { ExtBuilder::default() @@ -71,6 +112,18 @@ fn bind_address_should_fail_when_nonce_is_not_zero() { }); } +#[test] +fn bind_address_should_fail_when_binding_evm_truncated_account() { + ExtBuilder::default().build().execute_with(|| { + let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]); + let account_id = EVMAccounts::account_id(evm_address); + assert_noop!( + EVMAccounts::bind_evm_address(RuntimeOrigin::signed(account_id)), + Error::::TruncatedAccountAlreadyUsed + ); + }); +} + #[test] fn bind_address_should_fail_when_already_bound() { ExtBuilder::default().build().execute_with(|| {