diff --git a/Cargo.lock b/Cargo.lock index 7f924f676..7b108fd13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4621,7 +4621,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "222.0.0" +version = "223.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -4747,7 +4747,7 @@ dependencies = [ [[package]] name = "hydradx-traits" -version = "3.0.1" +version = "3.1.0" dependencies = [ "frame-support", "impl-trait-for-tuples", @@ -7677,12 +7677,13 @@ dependencies = [ [[package]] name = "pallet-evm-accounts" -version = "1.1.0" +version = "1.1.1" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "hex-literal 0.4.1", + "hydradx-traits", "orml-tokens", "orml-traits", "parity-scale-codec", @@ -8589,7 +8590,7 @@ dependencies = [ [[package]] name = "pallet-transaction-multi-payment" -version = "9.3.1" +version = "9.4.0" dependencies = [ "frame-support", "frame-system", @@ -8600,6 +8601,7 @@ dependencies = [ "pallet-balances", "pallet-currencies", "pallet-evm", + "pallet-evm-accounts", "pallet-transaction-payment", "parity-scale-codec", "primitives", @@ -11363,7 +11365,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.19.8" +version = "1.19.9" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index c0e678965..4e6999ae0 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.19.8" +version = "1.19.9" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/integration-tests/src/insufficient_assets_ed.rs b/integration-tests/src/insufficient_assets_ed.rs index 8b83607b5..cb941c86d 100644 --- a/integration-tests/src/insufficient_assets_ed.rs +++ b/integration-tests/src/insufficient_assets_ed.rs @@ -270,7 +270,7 @@ fn sender_should_pay_ed_only_when_dest_didnt_pay_yet() { } #[test] -fn dest_should_pay_ed_only_once_when_insufficient_asset_was_depsitted() { +fn dest_should_pay_ed_only_once_when_insufficient_asset_was_deposited() { TestNet::reset(); Hydra::execute_with(|| { let sht1: AssetId = register_external_asset(0_u128); diff --git a/integration-tests/src/non_native_fee.rs b/integration-tests/src/non_native_fee.rs index 8eb455bbe..13fef8988 100644 --- a/integration-tests/src/non_native_fee.rs +++ b/integration-tests/src/non_native_fee.rs @@ -16,6 +16,7 @@ use primitives::Price; use hydradx_adapters::OraclePriceProvider; use hydradx_traits::{ + evm::InspectEvmAccounts, pools::SpotPriceProvider, router::{AssetPair, RouteProvider}, OraclePeriod, PriceOracle, @@ -117,6 +118,42 @@ fn fee_currency_on_account_lifecycle() { }); } +#[test] +fn fee_currency_on_evm_account_lifecycle() { + TestNet::reset(); + + Hydra::execute_with(|| { + assert_eq!(MultiTransactionPayment::get_currency(AccountId::from(HITCHHIKER)), None); + + let evm_address = hydradx_runtime::EVMAccounts::evm_address(&Into::::into(HITCHHIKER)); + let truncated_account: AccountId = hydradx_runtime::EVMAccounts::truncated_account_id(evm_address); + + // ------------ set on create ------------ + assert_ok!(Currencies::transfer( + RuntimeOrigin::signed(BOB.into()), + truncated_account.clone(), + DAI, + 50_000_000_000_000, + )); + + assert_eq!(Tokens::free_balance(DAI, &truncated_account), 50_000_000_000_000); + assert_eq!( + MultiTransactionPayment::get_currency(truncated_account.clone()), + Some(WETH) + ); + + // ------------ remove on delete ------------ + assert_ok!(Tokens::transfer_all( + RuntimeOrigin::signed(truncated_account.clone()), + BOB.into(), + DAI, + false, + )); + + assert_eq!(MultiTransactionPayment::get_currency(truncated_account), None); + }); +} + #[test] fn pepe_is_not_registered() { TestNet::reset(); diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index 23636ff97..58e801ae1 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -18,7 +18,7 @@ use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; pub use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use hex_literal::hex; use hydradx_runtime::{evm::WETH_ASSET_LOCATION, Referrals, RuntimeOrigin}; -use hydradx_traits::registry::Mutate; +pub use hydradx_traits::{evm::InspectEvmAccounts, registry::Mutate}; use pallet_referrals::{FeeDistribution, Level}; pub use polkadot_primitives::v5::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; use polkadot_runtime_parachains::configuration::HostConfiguration; diff --git a/pallets/evm-accounts/Cargo.toml b/pallets/evm-accounts/Cargo.toml index 0c78ff593..fa04db78f 100644 --- a/pallets/evm-accounts/Cargo.toml +++ b/pallets/evm-accounts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-evm-accounts" -version = "1.1.0" +version = "1.1.1" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" @@ -13,6 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +hydradx-traits = { workspace = true } # parity scale-info = { version = "2.3.1", default-features = false, features = ["derive"] } codec = { default-features = false, features = ["derive"], package = "parity-scale-codec", version = "3.4.0" } diff --git a/pallets/evm-accounts/src/lib.rs b/pallets/evm-accounts/src/lib.rs index 37bd74782..e380c4436 100644 --- a/pallets/evm-accounts/src/lib.rs +++ b/pallets/evm-accounts/src/lib.rs @@ -52,6 +52,7 @@ use frame_support::ensure; use frame_support::pallet_prelude::{DispatchResult, Get}; +use hydradx_traits::evm::InspectEvmAccounts; use sp_core::{ crypto::{AccountId32, ByteArray}, H160, U256, @@ -262,18 +263,24 @@ pub mod pallet { } } -impl Pallet +impl InspectEvmAccounts for Pallet where - T::AccountId: frame_support::traits::IsType, + T::AccountId: AsRef<[u8; 32]> + frame_support::traits::IsType, { - /// get the EVM address from the substrate address. - pub fn evm_address(account_id: &impl AsRef<[u8; 32]>) -> EvmAddress { + /// 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] + } + + /// 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]) } /// Get the truncated address from the EVM address. - pub fn truncated_account_id(evm_address: EvmAddress) -> T::AccountId { + fn truncated_account_id(evm_address: EvmAddress) -> T::AccountId { let mut data: [u8; 32] = [0u8; 32]; data[0..4].copy_from_slice(b"ETH\0"); data[4..24].copy_from_slice(&evm_address[..]); @@ -281,7 +288,7 @@ where } /// Return the Substrate address bound to the EVM account. If not bound, returns `None`. - pub fn bound_account_id(evm_address: EvmAddress) -> Option { + fn bound_account_id(evm_address: EvmAddress) -> Option { let Some(last_12_bytes) = AccountExtension::::get(evm_address) else { return None; }; @@ -293,11 +300,12 @@ where /// Get the Substrate address from the EVM address. /// Returns the truncated version of the address if the address wasn't bind. - pub fn account_id(evm_address: EvmAddress) -> T::AccountId { + fn account_id(evm_address: EvmAddress) -> T::AccountId { Self::bound_account_id(evm_address).unwrap_or_else(|| Self::truncated_account_id(evm_address)) } - pub fn can_deploy_contracts(evm_address: EvmAddress) -> bool { + /// Returns `True` if the address is allowed to deploy smart contracts. + fn can_deploy_contracts(evm_address: EvmAddress) -> bool { ContractDeployer::::contains_key(evm_address) } } diff --git a/pallets/transaction-multi-payment/Cargo.toml b/pallets/transaction-multi-payment/Cargo.toml index c4ed31d2c..c69295519 100644 --- a/pallets/transaction-multi-payment/Cargo.toml +++ b/pallets/transaction-multi-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-multi-payment" -version = "9.3.1" +version = "9.4.0" description = "Transaction multi currency payment support module" authors = ["GalacticCoucil"] edition = "2021" @@ -37,6 +37,7 @@ pallet-evm = { workspace = true, optional = true } pallet-currencies = { workspace = true } orml-tokens = { workspace = true, features=["std"] } pallet-balances = { workspace = true, features=["std"] } +pallet-evm-accounts = { workspace = true, features = ["std"] } sp-io = { workspace = true } test-utils = { workspace = true } diff --git a/pallets/transaction-multi-payment/src/lib.rs b/pallets/transaction-multi-payment/src/lib.rs index 1a11a346f..55f101511 100644 --- a/pallets/transaction-multi-payment/src/lib.rs +++ b/pallets/transaction-multi-payment/src/lib.rs @@ -29,29 +29,29 @@ mod mock; mod tests; mod traits; -use frame_support::traits::Contains; -use frame_support::{dispatch::DispatchResult, ensure, traits::Get, weights::Weight}; +pub use crate::traits::*; +use frame_support::traits::{Contains, IsSubType}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + sp_runtime::{ + traits::{DispatchInfoOf, One, PostDispatchInfoOf, Saturating, Zero}, + transaction_validity::{InvalidTransaction, TransactionValidityError}, + FixedPointNumber, FixedPointOperand, FixedU128, + }, + traits::Get, + weights::Weight, +}; use frame_system::{ensure_signed, pallet_prelude::BlockNumberFor}; use hydra_dx_math::ema::EmaPrice; -use sp_runtime::{ - traits::{DispatchInfoOf, One, PostDispatchInfoOf, Saturating, Zero}, - transaction_validity::{InvalidTransaction, TransactionValidityError}, - FixedU128, +use hydradx_traits::{ + evm::InspectEvmAccounts, + router::{AssetPair, RouteProvider}, + AccountFeeCurrency, NativePriceOracle, OraclePeriod, PriceOracle, }; -use sp_std::prelude::*; - -use pallet_transaction_payment::OnChargeTransaction; -use sp_std::marker::PhantomData; - -use frame_support::sp_runtime::FixedPointNumber; -use frame_support::sp_runtime::FixedPointOperand; -use hydradx_traits::{AccountFeeCurrency, NativePriceOracle}; use orml_traits::{GetByKey, Happened, MultiCurrency}; - -pub use crate::traits::*; -use frame_support::traits::IsSubType; -use hydradx_traits::router::{AssetPair, RouteProvider}; -use hydradx_traits::{OraclePeriod, PriceOracle}; +use pallet_transaction_payment::OnChargeTransaction; +use sp_std::{marker::PhantomData, prelude::*}; type AssetIdOf = <::Currencies as MultiCurrency<::AccountId>>::CurrencyId; type BalanceOf = <::Currencies as MultiCurrency<::AccountId>>::Balance; @@ -123,6 +123,13 @@ pub mod pallet { /// Native Asset #[pallet::constant] type NativeAssetId: Get>; + + /// EVM Asset + #[pallet::constant] + type EvmAssetId: Get>; + + /// EVM Accounts info + type InspectEvmAccounts: InspectEvmAccounts; } #[pallet::event] @@ -176,6 +183,9 @@ pub mod pallet { /// Math overflow Overflow, + + /// It is not allowed to change payment currency of an EVM account. + EvmAccountNotAllowed, } /// Account currency map @@ -226,12 +236,19 @@ pub mod pallet { /// /// When currency is set, fixed fee is withdrawn from the account to pay for the currency change /// + /// EVM accounts are now allowed to change thier payment currency. + /// /// Emits `CurrencySet` event when successful. #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::set_currency())] pub fn set_currency(origin: OriginFor, currency: AssetIdOf) -> DispatchResult { let who = ensure_signed(origin)?; + ensure!( + !T::InspectEvmAccounts::is_evm_account(who.clone()), + Error::::EvmAccountNotAllowed + ); + ensure!( currency == T::NativeAssetId::get() || AcceptedCurrencies::::contains_key(currency), Error::::UnsupportedCurrency @@ -297,6 +314,33 @@ pub mod pallet { Ok(()) }) } + + /// Reset currency of the specified account to HDX. + /// If the account is EVM account, the payment currency is reset to WETH. + /// Only selected members can perform this action. + /// + /// Emits `CurrencySet` when successful. + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::reset_payment_currency())] + pub fn reset_payment_currency(origin: OriginFor, account_id: T::AccountId) -> DispatchResult { + T::AcceptedCurrencyOrigin::ensure_origin(origin)?; + + let currency = if T::InspectEvmAccounts::is_evm_account(account_id.clone()) { + let currency = T::EvmAssetId::get(); + AccountCurrencyMap::::insert(account_id.clone(), currency); + currency + } else { + AccountCurrencyMap::::remove(account_id.clone()); + T::NativeAssetId::get() + }; + + Self::deposit_event(Event::CurrencySet { + account_id, + asset_id: currency, + }); + + Ok(()) + } } } @@ -478,6 +522,12 @@ impl NativePriceOracle, Price> for Pallet { pub struct AddTxAssetOnAccount(PhantomData); impl Happened<(T::AccountId, AssetIdOf)> for AddTxAssetOnAccount { fn happened((who, currency): &(T::AccountId, AssetIdOf)) { + // all new EVM accounts have WETH set as their payment currency + if T::InspectEvmAccounts::is_evm_account(who.clone()) { + AccountCurrencyMap::::insert(who, T::EvmAssetId::get()); + return; + } + if !AccountCurrencyMap::::contains_key(who) && AcceptedCurrencies::::contains_key(currency) && T::Currencies::total_balance(T::NativeAssetId::get(), who).is_zero() diff --git a/pallets/transaction-multi-payment/src/mock.rs b/pallets/transaction-multi-payment/src/mock.rs index 2654888b5..d2705dfba 100644 --- a/pallets/transaction-multi-payment/src/mock.rs +++ b/pallets/transaction-multi-payment/src/mock.rs @@ -23,34 +23,38 @@ use hydra_dx_math::types::Ratio; use frame_support::{ dispatch::DispatchClass, parameter_types, + sp_runtime::{ + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, Perbill, + }, traits::{Everything, Get, Nothing}, weights::{IdentityFee, Weight}, }; use frame_system as system; -use hydradx_traits::router::{RouteProvider, Trade}; -use hydradx_traits::{AssetPairAccountIdFor, OraclePeriod, PriceOracle}; -use orml_traits::currency::MutationHooks; -use orml_traits::parameter_type_with_key; +use hydradx_traits::{ + router::{RouteProvider, Trade}, + AssetPairAccountIdFor, OraclePeriod, PriceOracle, +}; +use orml_traits::{currency::MutationHooks, parameter_type_with_key}; use pallet_currencies::BasicCurrencyAdapter; use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perbill, -}; use sp_std::cell::RefCell; -pub type AccountId = u64; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub type Balance = u128; pub type AssetId = u32; pub type Amount = i128; pub const INITIAL_BALANCE: Balance = 1_000_000_000_000_000u128; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 2; -pub const FEE_RECEIVER: AccountId = 300; +pub const ALICE: AccountId = AccountId::new([1; 32]); +pub const BOB: AccountId = AccountId::new([2; 32]); +pub const CHARLIE: AccountId = AccountId::new([3; 32]); +pub const DAVE: AccountId = AccountId::new([4; 32]); +pub const FEE_RECEIVER: AccountId = AccountId::new([5; 32]); pub const HDX: AssetId = 0; +pub const WETH: AssetId = 20; pub const SUPPORTED_CURRENCY: AssetId = 2000; pub const SUPPORTED_CURRENCY_WITH_PRICE: AssetId = 3000; pub const UNSUPPORTED_CURRENCY: AssetId = 4000; @@ -85,6 +89,7 @@ frame_support::construct_runtime!( Balances: pallet_balances, Currencies: pallet_currencies, Tokens: orml_tokens, + EVMAccounts: pallet_evm_accounts, } ); @@ -94,6 +99,7 @@ parameter_types! { pub const SS58Prefix: u8 = 63; pub const HdxAssetId: u32 = HDX; + pub const EvmAssetId: u32 = WETH; pub const ExistentialDeposit: u128 = 2; pub const MaxLocks: u32 = 50; pub const RegistryStringLimit: u32 = 100; @@ -129,7 +135,7 @@ impl system::Config for Test { type Block = Block; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; @@ -147,13 +153,15 @@ impl system::Config for Test { impl Config for Test { type RuntimeEvent = RuntimeEvent; - type AcceptedCurrencyOrigin = frame_system::EnsureRoot; + type AcceptedCurrencyOrigin = frame_system::EnsureRoot; type Currencies = Currencies; type RouteProvider = DefaultRouteProvider; type OraclePriceProvider = PriceProviderMock; type WeightInfo = (); type WeightToFee = IdentityFee; type NativeAssetId = HdxAssetId; + type EvmAssetId = EvmAssetId; + type InspectEvmAccounts = EVMAccounts; } pub struct DefaultRouteProvider; @@ -202,14 +210,17 @@ impl pallet_transaction_payment::Config for Test { } pub struct AssetPairAccountIdTest(); -impl AssetPairAccountIdFor for AssetPairAccountIdTest { - fn from_assets(asset_a: AssetId, asset_b: AssetId, _: &str) -> u64 { +impl AssetPairAccountIdFor for AssetPairAccountIdTest { + fn from_assets(asset_a: AssetId, asset_b: AssetId, _: &str) -> AccountId { let mut a = asset_a as u128; let mut b = asset_b as u128; if a > b { std::mem::swap(&mut a, &mut b) } - (a * 1000 + b) as u64 + + let mut data: [u8; 32] = [0u8; 32]; + data[28..32].copy_from_slice(&(a * 1000 * b).to_be_bytes()); + AccountId::new(data) } } @@ -261,6 +272,21 @@ impl pallet_currencies::Config for Test { type WeightInfo = (); } +pub struct EvmNonceProvider; +impl pallet_evm_accounts::EvmNonceProvider for EvmNonceProvider { + fn get_nonce(_: sp_core::H160) -> sp_core::U256 { + sp_core::U256::zero() + } +} + +impl pallet_evm_accounts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type EvmNonceProvider = EvmNonceProvider; + type FeeMultiplier = frame_support::traits::ConstU32<10>; + type ControllerOrigin = frame_system::EnsureRoot; + type WeightInfo = (); +} + pub struct ExtBuilder { base_weight: Weight, native_balances: Vec<(AccountId, Balance)>, diff --git a/pallets/transaction-multi-payment/src/tests.rs b/pallets/transaction-multi-payment/src/tests.rs index fbec2762f..d49e05eed 100644 --- a/pallets/transaction-multi-payment/src/tests.rs +++ b/pallets/transaction-multi-payment/src/tests.rs @@ -18,20 +18,20 @@ pub use crate::{mock::*, Config, Error}; use crate::{AcceptedCurrencies, AcceptedCurrencyPrice, Event, PaymentInfo, Price}; -use frame_support::traits::tokens::Precision; use frame_support::{ assert_noop, assert_ok, dispatch::{DispatchInfo, PostDispatchInfo}, sp_runtime::traits::{BadOrigin, SignedExtension}, - traits::Hooks, + traits::{tokens::Precision, Hooks}, weights::Weight, }; +use hydradx_traits::evm::InspectEvmAccounts; use orml_traits::MultiCurrency; use pallet_balances::Call as BalancesCall; use pallet_transaction_payment::ChargeTransactionPayment; const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + &RuntimeCall::Balances(BalancesCall::transfer { dest: BOB, value: 69 }); #[test] fn on_initialize_should_fill_storage_with_prices() { @@ -169,9 +169,32 @@ fn set_native_currency() { } #[test] -fn fee_payment_in_native_currency() { - const CHARLIE: AccountId = 5; +fn set_currency_for_evm_accounts_should_not_work() { + ExtBuilder::default() + .account_tokens(ALICE, WETH, 1_000_000_000) + .build() + .execute_with(|| { + let alice_evm_address = EVMAccounts::evm_address(&ALICE); + let alice_evm_acc = EVMAccounts::truncated_account_id(alice_evm_address); + + assert_ok!(Tokens::transfer( + Some(ALICE).into(), + alice_evm_acc.clone(), + WETH, + 1_000_000_000 + )); + + assert_eq!(PaymentPallet::account_currency(&alice_evm_acc), WETH); + assert_noop!( + PaymentPallet::set_currency(RuntimeOrigin::signed(alice_evm_acc), SUPPORTED_CURRENCY), + Error::::EvmAccountNotAllowed + ); + }); +} + +#[test] +fn fee_payment_in_native_currency() { ExtBuilder::default() .base_weight(5) .account_native_balance(CHARLIE, 100) @@ -190,8 +213,6 @@ fn fee_payment_in_native_currency() { #[test] fn fee_payment_in_non_native_currency() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY_WITH_PRICE, 10_000) @@ -248,8 +269,6 @@ fn fee_payment_in_expensive_non_native_currency_should_be_non_zero() { #[test] fn fee_payment_non_native_insufficient_balance() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 100) @@ -366,8 +385,6 @@ fn default_post_info() -> PostDispatchInfo { #[test] fn fee_should_be_transferred_when_paid_in_native_currency() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 100) .base_weight(5) @@ -404,8 +421,6 @@ fn fee_should_be_transferred_when_paid_in_native_currency() { #[test] fn fee_should_be_withdrawn_when_paid_in_native_currency() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 100) .base_weight(5) @@ -443,8 +458,6 @@ fn fee_should_be_withdrawn_when_paid_in_native_currency() { #[test] fn fee_should_be_transferred_when_paid_in_native_currency_work_with_tip() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 100) .base_weight(5) @@ -483,8 +496,6 @@ fn fee_should_be_transferred_when_paid_in_native_currency_work_with_tip() { #[test] fn fee_should_be_withdrawn_when_paid_in_native_currency_work_with_tip() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 100) .base_weight(5) @@ -524,8 +535,6 @@ fn fee_should_be_withdrawn_when_paid_in_native_currency_work_with_tip() { #[test] fn fee_should_be_transferred_when_paid_in_non_native_currency() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .with_currencies(vec![(CHARLIE, SUPPORTED_CURRENCY)]) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 10_000) @@ -576,8 +585,6 @@ fn fee_should_be_transferred_when_paid_in_non_native_currency() { #[test] fn fee_should_be_withdrawn_when_paid_in_non_native_currency() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .with_currencies(vec![(CHARLIE, SUPPORTED_CURRENCY)]) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 10_000) @@ -621,8 +628,6 @@ fn fee_should_be_withdrawn_when_paid_in_non_native_currency() { #[test] fn fee_should_be_transferred_when_paid_in_non_native_currency_with_tip() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .with_currencies(vec![(CHARLIE, SUPPORTED_CURRENCY)]) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 10_000) @@ -674,8 +679,6 @@ fn fee_should_be_transferred_when_paid_in_non_native_currency_with_tip() { #[test] fn fee_should_be_withdrawn_and_not_refunded_when_paid_in_non_native_currency_with_tip() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .with_currencies(vec![(CHARLIE, SUPPORTED_CURRENCY)]) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 10_000) @@ -719,8 +722,6 @@ fn fee_should_be_withdrawn_and_not_refunded_when_paid_in_non_native_currency_wit #[test] fn fee_payment_in_native_currency_with_no_balance() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_native_balance(CHARLIE, 10) @@ -740,8 +741,6 @@ fn fee_payment_in_native_currency_with_no_balance() { #[test] fn fee_payment_in_non_native_currency_with_no_balance() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 100) @@ -762,8 +761,6 @@ fn fee_payment_in_non_native_currency_with_no_balance() { #[test] fn fee_payment_in_non_native_currency_with_no_price() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 10_000) @@ -792,8 +789,6 @@ fn fee_payment_in_non_native_currency_with_no_price() { #[test] fn fee_payment_in_unregistered_currency() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 100) @@ -818,8 +813,6 @@ fn fee_payment_in_unregistered_currency() { #[test] fn fee_payment_non_native_insufficient_balance_with_no_pool() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_tokens(CHARLIE, SUPPORTED_CURRENCY, 100) @@ -840,8 +833,6 @@ fn fee_payment_non_native_insufficient_balance_with_no_pool() { #[test] fn fee_transfer_can_kill_account_when_paid_in_native() { // Arrange - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 30) .base_weight(5) @@ -924,8 +915,6 @@ fn fee_transfer_can_kill_account_when_paid_in_non_native() { #[test] fn set_and_remove_currency_on_lifecycle_callbacks() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_native_balance(CHARLIE, 10) @@ -952,9 +941,6 @@ fn set_and_remove_currency_on_lifecycle_callbacks() { #[test] fn currency_stays_around_until_reaping() { - const CHARLIE: AccountId = 5; - const DAVE: AccountId = 6; - use frame_support::traits::fungibles::Balanced; ExtBuilder::default() @@ -988,9 +974,6 @@ fn currency_stays_around_until_reaping() { #[test] fn currency_is_removed_when_balance_hits_zero() { - const CHARLIE: AccountId = 5; - const DAVE: AccountId = 6; - use frame_support::traits::fungibles::Balanced; ExtBuilder::default() @@ -1025,9 +1008,6 @@ fn currency_is_removed_when_balance_hits_zero() { #[test] fn currency_is_not_changed_on_unrelated_account_activity() { - const CHARLIE: AccountId = 5; - const DAVE: AccountId = 6; - use frame_support::traits::fungibles::Balanced; ExtBuilder::default() @@ -1064,8 +1044,6 @@ fn currency_is_not_changed_on_unrelated_account_activity() { #[test] fn only_set_fee_currency_for_supported_currency() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .base_weight(5) .account_native_balance(CHARLIE, 10) @@ -1083,8 +1061,6 @@ fn only_set_fee_currency_for_supported_currency() { #[test] fn only_set_fee_currency_when_without_native_currency() { - const CHARLIE: AccountId = 5; - ExtBuilder::default() .account_native_balance(CHARLIE, 10) .build() @@ -1104,9 +1080,6 @@ fn only_set_fee_currency_when_without_native_currency() { #[test] fn do_not_set_fee_currency_for_new_native_account() { - const CHARLIE: AccountId = 5; - const DAVE: AccountId = 6; - ExtBuilder::default() .account_native_balance(CHARLIE, 10) .build() @@ -1137,3 +1110,47 @@ fn returns_prices_for_supported_currencies() { ); }); } + +#[test] +fn reset_payment_currency_should_set_currency_to_hdx_for_non_evm_accounts() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(PaymentPallet::set_currency( + RuntimeOrigin::signed(ALICE), + SUPPORTED_CURRENCY, + )); + + assert_ok!(PaymentPallet::reset_payment_currency(RuntimeOrigin::root(), ALICE,)); + + assert_eq!(PaymentPallet::get_currency(ALICE), None); + + expect_events(vec![Event::CurrencySet { + account_id: ALICE, + asset_id: HDX, + } + .into()]); + }); +} + +#[test] +fn reset_payment_currency_should_set_currency_to_weth_for_evm_accounts() { + let alice_evm_address = EVMAccounts::evm_address(&ALICE); + let alice_evm_acc = EVMAccounts::truncated_account_id(alice_evm_address); + + ExtBuilder::default() + .with_currencies(vec![(alice_evm_acc.clone(), SUPPORTED_CURRENCY)]) + .build() + .execute_with(|| { + assert_ok!(PaymentPallet::reset_payment_currency( + RuntimeOrigin::root(), + alice_evm_acc.clone(), + )); + + assert_eq!(PaymentPallet::get_currency(alice_evm_acc.clone()), Some(WETH)); + + expect_events(vec![Event::CurrencySet { + account_id: alice_evm_acc, + asset_id: WETH, + } + .into()]); + }); +} diff --git a/pallets/transaction-multi-payment/src/weights.rs b/pallets/transaction-multi-payment/src/weights.rs index f7af4fd73..b00df3764 100644 --- a/pallets/transaction-multi-payment/src/weights.rs +++ b/pallets/transaction-multi-payment/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of HydraDX. -// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_transaction_multi_payment` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-27, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-17, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: 1024 @@ -34,7 +34,7 @@ // --heap-pages=4096 // --template=.maintain/pallet-weight-template-no-back.hbs // --pallet=pallet-transaction-multi-payment -// --output=weights-1.1.0/transaction_multi_payment.rs +// --output=weights-1.1.0/payment.rs // --extrinsic=* #![allow(unused_parens)] @@ -53,6 +53,7 @@ pub trait WeightInfo { fn remove_currency() -> Weight; fn set_currency() -> Weight; fn get_oracle_price() -> Weight; + fn reset_payment_currency() -> Weight; } /// Weights for pallet_transaction_multi_payment using the hydraDX node and recommended hardware. @@ -89,8 +90,8 @@ impl WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1241` // Estimated: `3493` - // Minimum execution time: 30_284_000 picoseconds. - Weight::from_parts(30_744_000, 3493) + // Minimum execution time: 31_064_000 picoseconds. + Weight::from_parts(31_466_000, 3493) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -105,6 +106,21 @@ impl WeightInfo for HydraWeight { // Minimum execution time: 93_281_000 picoseconds. Weight::from_parts(94_259_000, 27510).saturating_add(T::DbWeight::get().reads(11)) } + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:0 w:1) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn reset_payment_currency() -> Weight { + // Proof Size summary in bytes: + // Measured: `1280` + // Estimated: `4087` + // Minimum execution time: 28_830_000 picoseconds. + Weight::from_parts(29_378_000, 4087) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } } // For backwards compatibility and tests @@ -139,8 +155,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1241` // Estimated: `3493` - // Minimum execution time: 30_284_000 picoseconds. - Weight::from_parts(30_744_000, 3493) + // Minimum execution time: 31_064_000 picoseconds. + Weight::from_parts(31_466_000, 3493) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -155,4 +171,19 @@ impl WeightInfo for () { // Minimum execution time: 93_281_000 picoseconds. Weight::from_parts(94_259_000, 27510).saturating_add(RocksDbWeight::get().reads(11)) } + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:0 w:1) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn reset_payment_currency() -> Weight { + // Proof Size summary in bytes: + // Measured: `1280` + // Estimated: `4087` + // Minimum execution time: 28_830_000 picoseconds. + Weight::from_parts(29_378_000, 4087) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } } diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 3ccdfd0b1..e3c818bbe 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "222.0.0" +version = "223.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/benchmarking/multi_payment.rs b/runtime/hydradx/src/benchmarking/multi_payment.rs index 0fa89e8c2..5eb3860d9 100644 --- a/runtime/hydradx/src/benchmarking/multi_payment.rs +++ b/runtime/hydradx/src/benchmarking/multi_payment.rs @@ -22,6 +22,7 @@ use frame_benchmarking::BenchmarkError; use frame_support::assert_ok; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::RawOrigin; +use hydradx_traits::evm::InspectEvmAccounts; use hydradx_traits::router::PoolType; use hydradx_traits::router::RouteProvider; use hydradx_traits::PriceOracle; @@ -29,6 +30,7 @@ use orml_benchmarking::runtime_benchmarks; use orml_traits::MultiCurrencyExtended; use pallet_route_executor::MAX_NUMBER_OF_TRADES; use primitives::{BlockNumber, Price}; +use sp_core::Get; use sp_runtime::traits::SaturatedConversion; use sp_runtime::FixedU128; @@ -153,6 +155,17 @@ runtime_benchmarks! { verify{ assert!(_price.is_some()); } + + reset_payment_currency { + let caller: AccountId = account("caller", 0, SEED); + + let caller_evm_address = pallet_evm_accounts::Pallet::::evm_address(&caller); + let caller_evm_acc = pallet_evm_accounts::Pallet::::truncated_account_id(caller_evm_address); + + }: { MultiPaymentPallet::::reset_payment_currency(RawOrigin::Root.into(), caller_evm_acc.clone())? } + verify{ + assert_eq!(MultiPaymentPallet::::get_currency(caller_evm_acc), Some(::EvmAssetId::get())); + } } fn create_xyk_pool(asset_a: AssetId, amount_a: Balance, asset_b: AssetId, amount_b: Balance) diff --git a/runtime/hydradx/src/evm/accounts_conversion.rs b/runtime/hydradx/src/evm/accounts_conversion.rs index 9c35d3903..c3fe1f0f2 100644 --- a/runtime/hydradx/src/evm/accounts_conversion.rs +++ b/runtime/hydradx/src/evm/accounts_conversion.rs @@ -27,6 +27,7 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::traits::IsType; use hex_literal::hex; +use hydradx_traits::evm::InspectEvmAccounts; use pallet_evm::AddressMapping; use sp_core::{crypto::ByteArray, H160}; use sp_runtime::traits::AccountIdConversion; diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 25304c6f1..0731cf3ff 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -46,6 +46,7 @@ pub use xcm::*; use crate::sp_api_hidden_includes_construct_runtime::hidden_include::traits::Hooks; use codec::{Decode, Encode}; +use hydradx_traits::evm::InspectEvmAccounts; use sp_api::impl_runtime_apis; use sp_core::{ConstU128, Get, OpaqueMetadata, H160, H256, U256}; use sp_runtime::{ @@ -108,7 +109,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 222, + spec_version: 223, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/hydradx/src/system.rs b/runtime/hydradx/src/system.rs index c218f2cc0..68203890e 100644 --- a/runtime/hydradx/src/system.rs +++ b/runtime/hydradx/src/system.rs @@ -482,8 +482,10 @@ impl pallet_transaction_multi_payment::Config for Runtime { type RouteProvider = Router; type OraclePriceProvider = OraclePriceProvider; type WeightInfo = weights::payment::HydraWeight; - type WeightToFee = WeightToFee; type NativeAssetId = NativeAssetId; + type EvmAssetId = evm::WethAssetId; + type InspectEvmAccounts = EVMAccounts; + type WeightToFee = WeightToFee; } impl pallet_relaychain_info::Config for Runtime { diff --git a/runtime/hydradx/src/weights/payment.rs b/runtime/hydradx/src/weights/payment.rs index 6faa12376..ea5ffdff8 100644 --- a/runtime/hydradx/src/weights/payment.rs +++ b/runtime/hydradx/src/weights/payment.rs @@ -1,6 +1,6 @@ // This file is part of HydraDX. -// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_transaction_multi_payment` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-02-15, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-17, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: 1024 @@ -33,8 +33,8 @@ // --wasm-execution=compiled // --heap-pages=4096 // --template=.maintain/pallet-weight-template-no-back.hbs -// --pallet=pallet_transaction_multi_payment -// --output=./weights/payment.rs +// --pallet=pallet-transaction-multi-payment +// --output=weights-1.1.0/payment.rs // --extrinsic=* #![cfg_attr(rustfmt, rustfmt_skip)] @@ -80,8 +80,8 @@ impl WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1241` // Estimated: `3493` - // Minimum execution time: 30_095_000 picoseconds. - Weight::from_parts(30_539_000, 3493) + // Minimum execution time: 31_064_000 picoseconds. + Weight::from_parts(31_466_000, 3493) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -97,4 +97,19 @@ impl WeightInfo for HydraWeight { Weight::from_parts(95_196_000, 27510) .saturating_add(T::DbWeight::get().reads(11)) } + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:0 w:1) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn reset_payment_currency() -> Weight { + // Proof Size summary in bytes: + // Measured: `1280` + // Estimated: `4087` + // Minimum execution time: 28_830_000 picoseconds. + Weight::from_parts(29_378_000, 4087) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/traits/Cargo.toml b/traits/Cargo.toml index fbe8b31e3..115abd37b 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-traits" -version = "3.0.1" +version = "3.1.0" description = "Shared traits" authors = ["GalacticCouncil"] edition = "2021" diff --git a/traits/src/evm.rs b/traits/src/evm.rs new file mode 100644 index 000000000..8dc1ea184 --- /dev/null +++ b/traits/src/evm.rs @@ -0,0 +1,20 @@ +pub trait InspectEvmAccounts { + /// Returns `True` if the account is EVM truncated account. + fn is_evm_account(account_id: AccountId) -> bool; + + /// get the EVM address from the substrate address. + fn evm_address(account_id: &impl AsRef<[u8; 32]>) -> EvmAddress; + + /// Get the truncated address from the EVM address. + fn truncated_account_id(evm_address: EvmAddress) -> AccountId; + + /// Return the Substrate address bound to the EVM account. If not bound, returns `None`. + fn bound_account_id(evm_address: EvmAddress) -> Option; + + /// Get the Substrate address from the EVM address. + /// Returns the truncated version of the address if the address wasn't bind. + fn account_id(evm_address: EvmAddress) -> AccountId; + + /// Returns `True` if the address is allowed to deploy smart contracts. + fn can_deploy_contracts(evm_address: EvmAddress) -> bool; +} diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 670a3ab33..3078695ca 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -18,17 +18,17 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::upper_case_acronyms)] +pub mod evm; pub mod liquidity_mining; pub mod nft; +pub mod oracle; pub mod pools; +pub mod price; pub mod registry; pub mod router; -pub use registry::*; -pub mod oracle; -pub mod price; - pub use oracle::*; +pub use registry::*; use codec::{Decode, Encode}; use frame_support::dispatch::{self};