diff --git a/Cargo.lock b/Cargo.lock index 01fa1e51c1480..51088f2836f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1340,6 +1340,7 @@ dependencies = [ "pallet-asset-rewards", "pallet-assets", "pallet-assets-freezer", + "pallet-assets-precompiles", "pallet-aura", "pallet-authorship", "pallet-bags-list", @@ -11442,14 +11443,12 @@ dependencies = [ name = "pallet-assets" version = "29.1.0" dependencies = [ - "ethereum-standards", "frame-benchmarking", "frame-support", "frame-system", "impl-trait-for-tuples", "log", "pallet-balances", - "pallet-revive", "parity-scale-codec", "scale-info", "sp-core 28.0.0", @@ -11486,6 +11485,23 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-assets-precompiles" +version = "0.1.0" +dependencies = [ + "ethereum-standards", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-revive", + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-atomic-swap" version = "28.0.0" @@ -16344,6 +16360,7 @@ dependencies = [ "pallet-assets", "pallet-assets-freezer", "pallet-assets-holder", + "pallet-assets-precompiles", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", diff --git a/Cargo.toml b/Cargo.toml index d8c318723389e..9150001edf0f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -330,6 +330,7 @@ members = [ "substrate/frame/assets", "substrate/frame/assets-freezer", "substrate/frame/assets-holder", + "substrate/frame/assets/precompiles", "substrate/frame/atomic-swap", "substrate/frame/aura", "substrate/frame/authority-discovery", @@ -951,6 +952,7 @@ pallet-asset-tx-payment = { path = "substrate/frame/transaction-payment/asset-tx pallet-assets = { path = "substrate/frame/assets", default-features = false } pallet-assets-freezer = { path = "substrate/frame/assets-freezer", default-features = false } pallet-assets-holder = { path = "substrate/frame/assets-holder", default-features = false } +pallet-assets-precompiles = { path = "substrate/frame/assets/precompiles", default-features = false } pallet-atomic-swap = { default-features = false, path = "substrate/frame/atomic-swap" } pallet-aura = { path = "substrate/frame/aura", default-features = false } pallet-authority-discovery = { path = "substrate/frame/authority-discovery", default-features = false } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 328d289b6ba1e..767c43a948110 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -37,6 +37,7 @@ pallet-asset-rate = { workspace = true } pallet-asset-rewards = { workspace = true } pallet-assets = { workspace = true } pallet-assets-freezer = { workspace = true } +pallet-assets-precompiles = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } pallet-bags-list = { workspace = true } @@ -165,6 +166,7 @@ runtime-benchmarks = [ "pallet-asset-rate/runtime-benchmarks", "pallet-asset-rewards/runtime-benchmarks", "pallet-assets-freezer/runtime-benchmarks", + "pallet-assets-precompiles/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-bags-list/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -233,6 +235,7 @@ try-runtime = [ "pallet-asset-rate/try-runtime", "pallet-asset-rewards/try-runtime", "pallet-assets-freezer/try-runtime", + "pallet-assets-precompiles/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", @@ -311,6 +314,7 @@ std = [ "pallet-asset-rate/std", "pallet-asset-rewards/std", "pallet-assets-freezer/std", + "pallet-assets-precompiles/std", "pallet-assets/std", "pallet-aura/std", "pallet-authorship/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index a9a7eb2196e0d..4ee1920724c34 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -67,7 +67,7 @@ use frame_system::{ EnsureRoot, EnsureSigned, EnsureSignedBy, }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; -use pallet_assets::precompiles::{InlineIdConfig, ERC20}; +use pallet_assets_precompiles::{InlineIdConfig, ERC20}; use pallet_nfts::{DestroyWitness, PalletFeatures}; use pallet_nomination_pools::PoolId; use pallet_revive::evm::runtime::EthExtra; diff --git a/prdoc/pr_9796.prdoc b/prdoc/pr_9796.prdoc new file mode 100644 index 0000000000000..c1062b25eefd4 --- /dev/null +++ b/prdoc/pr_9796.prdoc @@ -0,0 +1,13 @@ +title: "pallet-assets: extract precompiles to its own pallet" + +doc: + - audience: Runtime Dev + description: | + Assets pallet includes pallet-revive precompiles and subsequently pull a lot of EVM related dependencies by default. This change + extracts the precompiles into a new pallet `pallet-assets-precompiles`. + +crates: + - name: pallet-assets + bump: minor + - name: pallet-assets-precompiles + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 06e660cd55640..aa5563f0d58bf 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -78,7 +78,7 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce}; use pallet_asset_conversion::{AccountIdConverter, Ascending, Chain, WithFirstAsset}; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; -use pallet_assets::precompiles::{InlineIdConfig, ERC20}; +use pallet_assets_precompiles::{InlineIdConfig, ERC20}; use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600}; use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf}; use pallet_identity::legacy::IdentityInfo; @@ -2480,11 +2480,11 @@ impl EnsureOriginWithArg for DynamicParamet match key { RuntimeParametersKey::Storage(_) => { frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; - return Ok(()) + return Ok(()); }, RuntimeParametersKey::Referenda(_) => { frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; - return Ok(()) + return Ok(()); }, } } diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index d893e7ad11fa1..8d4b8c6a93545 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -17,7 +17,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } -ethereum-standards = { workspace = true } impl-trait-for-tuples = { workspace = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } @@ -28,7 +27,6 @@ frame-support = { workspace = true } # `system` module provides us with all sorts of useful stuff and macros depend on it being around. frame-benchmarking = { optional = true, workspace = true } frame-system = { workspace = true } -pallet-revive = { workspace = true } sp-core = { workspace = true } [dev-dependencies] @@ -44,7 +42,6 @@ std = [ "frame-system/std", "log/std", "pallet-balances/std", - "pallet-revive/std", "scale-info/std", "sp-core/std", "sp-io/std", @@ -55,13 +52,11 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-revive/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", - "pallet-revive/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/assets/precompiles/Cargo.toml b/substrate/frame/assets/precompiles/Cargo.toml new file mode 100644 index 0000000000000..b202f53e97c5c --- /dev/null +++ b/substrate/frame/assets/precompiles/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "pallet-assets-precompiles" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Provides precompiles for `pallet-assets`" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +ethereum-standards = { workspace = true } +frame-support = { workspace = true } +pallet-assets = { workspace = true } +pallet-revive = { workspace = true } + +[dev-dependencies] +codec = { workspace = true } +frame-system = { workspace = true } +pallet-balances = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-assets/std", + "pallet-balances/std", + "pallet-revive/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-revive/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-revive/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/assets/src/precompiles.rs b/substrate/frame/assets/precompiles/src/lib.rs similarity index 58% rename from substrate/frame/assets/src/precompiles.rs rename to substrate/frame/assets/precompiles/src/lib.rs index 3a7915dfd241d..c9f41a4581b86 100644 --- a/substrate/frame/assets/src/precompiles.rs +++ b/substrate/frame/assets/precompiles/src/lib.rs @@ -15,12 +15,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{weights::WeightInfo, Call, Config, PhantomData, TransferFlags}; +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + use alloc::vec::Vec; +use core::marker::PhantomData; use ethereum_standards::{ IERC20, IERC20::{IERC20Calls, IERC20Events}, }; +use pallet_assets::{weights::WeightInfo, Call, Config, TransferFlags}; use pallet_revive::precompiles::{ alloy::{ self, @@ -30,6 +36,11 @@ use pallet_revive::precompiles::{ AddressMapper, AddressMatcher, Error, Ext, Precompile, RuntimeCosts, H160, H256, }; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + /// Mean of extracting the asset id from the precompile address. pub trait AssetIdExtractor { type AssetId; @@ -174,7 +185,7 @@ where ); let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false }; - crate::Pallet::::do_transfer( + pallet_assets::Pallet::::do_transfer( asset_id, &::AddressMapper::to_account_id(&from), &dest, @@ -203,7 +214,8 @@ where use frame_support::traits::fungibles::Inspect; env.charge(>::WeightInfo::total_issuance())?; - let value = Self::to_u256(crate::Pallet::::total_issuance(asset_id))?; + let value = + Self::to_u256(pallet_assets::Pallet::::total_issuance(asset_id))?; return Ok(IERC20::totalSupplyCall::abi_encode_returns(&value)); } @@ -216,7 +228,8 @@ where env.charge(>::WeightInfo::balance())?; let account = call.account.into_array().into(); let account = ::AddressMapper::to_account_id(&account); - let value = Self::to_u256(crate::Pallet::::balance(asset_id, account))?; + let value = + Self::to_u256(pallet_assets::Pallet::::balance(asset_id, account))?; return Ok(IERC20::balanceOfCall::abi_encode_returns(&value)); } @@ -233,7 +246,7 @@ where let spender = call.spender.into_array().into(); let spender = ::AddressMapper::to_account_id(&spender); - let value = Self::to_u256(crate::Pallet::::allowance( + let value = Self::to_u256(pallet_assets::Pallet::::allowance( asset_id, &owner, &spender, ))?; @@ -251,7 +264,7 @@ where let spender = call.spender.into_array().into(); let spender = ::AddressMapper::to_account_id(&spender); - crate::Pallet::::do_approve_transfer( + pallet_assets::Pallet::::do_approve_transfer( asset_id, &::AddressMapper::to_account_id(&owner), &spender, @@ -286,7 +299,7 @@ where let to = call.to.into_array().into(); let to = ::AddressMapper::to_account_id(&to); - crate::Pallet::::do_transfer_approved( + pallet_assets::Pallet::::do_transfer_approved( asset_id, &from, &spender, @@ -306,249 +319,3 @@ where return Ok(IERC20::transferFromCall::abi_encode_returns(&true)); } } - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - mock::{new_test_ext, Assets, Balances, RuntimeEvent, RuntimeOrigin, System, Test}, - precompiles::alloy::hex, - }; - use alloy::primitives::U256; - use frame_support::{assert_ok, traits::Currency}; - use pallet_revive::DepositLimit; - use sp_core::H160; - use sp_runtime::Weight; - - fn assert_contract_event(contract: H160, event: IERC20Events) { - let (topics, data) = event.into_log_data().split(); - let topics = topics.into_iter().map(|v| H256(v.0)).collect::>(); - System::assert_has_event(RuntimeEvent::Revive(pallet_revive::Event::ContractEmitted { - contract, - data: data.to_vec(), - topics, - })); - } - - #[test] - fn asset_id_extractor_works() { - let address: [u8; 20] = - hex::const_decode_to_array(b"0000053900000000000000000000000001200000").unwrap(); - assert!(InlineIdConfig::<0x0120>::MATCHER.matches(&address)); - assert_eq!( - as AssetPrecompileConfig>::AssetIdExtractor::asset_id_from_address( - &address - ) - .unwrap(), - 1337u32 - ); - } - - #[test] - fn precompile_transfer_works() { - new_test_ext().execute_with(|| { - let asset_id = 0u32; - let asset_addr = H160::from( - hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(), - ); - - let from = 123456789; - let to = 987654321; - - Balances::make_free_balance_be(&from, 100); - Balances::make_free_balance_be(&to, 100); - - let from_addr = ::AddressMapper::to_address(&from); - let to_addr = ::AddressMapper::to_address(&to); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, from, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(from), asset_id, from, 100)); - - let data = - IERC20::transferCall { to: to_addr.0.into(), value: U256::from(10) }.abi_encode(); - - pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(from), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ); - - assert_contract_event( - asset_addr, - IERC20Events::Transfer(IERC20::Transfer { - from: from_addr.0.into(), - to: to_addr.0.into(), - value: U256::from(10), - }), - ); - - assert_eq!(Assets::balance(asset_id, from), 90); - assert_eq!(Assets::balance(asset_id, to), 10); - }); - } - - #[test] - fn total_supply_works() { - new_test_ext().execute_with(|| { - let asset_id = 0u32; - let asset_addr = - hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(); - - let owner = 123456789; - - Balances::make_free_balance_be(&owner, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 1000)); - - let data = IERC20::totalSupplyCall {}.abi_encode(); - - let data = pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(owner), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ) - .result - .unwrap() - .data; - - let ret = IERC20::totalSupplyCall::abi_decode_returns(&data).unwrap(); - assert_eq!(ret, U256::from(1000)); - }); - } - - #[test] - fn balance_of_works() { - new_test_ext().execute_with(|| { - let asset_id = 0u32; - let asset_addr = - hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(); - - let owner = 123456789; - - assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 1000)); - - let account = - ::AddressMapper::to_address(&owner).0.into(); - let data = IERC20::balanceOfCall { account }.abi_encode(); - - let data = pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(owner), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ) - .result - .unwrap() - .data; - - let ret = IERC20::balanceOfCall::abi_decode_returns(&data).unwrap(); - assert_eq!(ret, U256::from(1000)); - }); - } - - #[test] - fn approval_works() { - use frame_support::traits::fungibles::approvals::Inspect; - - new_test_ext().execute_with(|| { - let asset_id = 0u32; - let asset_addr = H160::from( - hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(), - ); - - let owner = 123456789; - let spender = 987654321; - let other = 1122334455; - - Balances::make_free_balance_be(&owner, 100); - Balances::make_free_balance_be(&spender, 100); - Balances::make_free_balance_be(&other, 100); - - let owner_addr = ::AddressMapper::to_address(&owner); - let spender_addr = ::AddressMapper::to_address(&spender); - let other_addr = ::AddressMapper::to_address(&other); - - assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 100)); - - let data = - IERC20::approveCall { spender: spender_addr.0.into(), value: U256::from(25) } - .abi_encode(); - - pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(owner), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ); - - assert_contract_event( - asset_addr, - IERC20Events::Approval(IERC20::Approval { - owner: owner_addr.0.into(), - spender: spender_addr.0.into(), - value: U256::from(25), - }), - ); - - let data = IERC20::allowanceCall { - owner: owner_addr.0.into(), - spender: spender_addr.0.into(), - } - .abi_encode(); - - let data = pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(owner), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ) - .result - .unwrap() - .data; - - let ret = IERC20::allowanceCall::abi_decode_returns(&data).unwrap(); - assert_eq!(ret, U256::from(25)); - - let data = IERC20::transferFromCall { - from: owner_addr.0.into(), - to: other_addr.0.into(), - value: U256::from(10), - } - .abi_encode(); - - pallet_revive::Pallet::::bare_call( - RuntimeOrigin::signed(spender), - H160::from(asset_addr), - 0u32.into(), - Weight::MAX, - DepositLimit::UnsafeOnlyForDryRun, - data, - ); - assert_eq!(Assets::balance(asset_id, owner), 90); - assert_eq!(Assets::allowance(asset_id, &owner, &spender), 15); - assert_eq!(Assets::balance(asset_id, other), 10); - - assert_contract_event( - asset_addr, - IERC20Events::Transfer(IERC20::Transfer { - from: owner_addr.0.into(), - to: other_addr.0.into(), - value: U256::from(10), - }), - ); - }); - } -} diff --git a/substrate/frame/assets/precompiles/src/mock.rs b/substrate/frame/assets/precompiles/src/mock.rs new file mode 100644 index 0000000000000..941726a148167 --- /dev/null +++ b/substrate/frame/assets/precompiles/src/mock.rs @@ -0,0 +1,95 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests mock for `pallet-assets-freezer`. + +pub use super::*; +use frame_support::{derive_impl, traits::AsEnsureOriginWithArg}; +use sp_runtime::BuildStorage; + +type Block = frame_system::mocking::MockBlock; + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeTask, + RuntimeHoldReason, + RuntimeFreezeReason + )] + pub struct Test; + + #[runtime::pallet_index(0)] + pub type System = frame_system; + #[runtime::pallet_index(10)] + pub type Balances = pallet_balances; + #[runtime::pallet_index(20)] + pub type Assets = pallet_assets; + #[runtime::pallet_index(21)] + pub type Revive = pallet_revive; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Test { + type AccountStore = System; +} + +#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig as pallet_assets::DefaultConfig)] +impl pallet_assets::Config for Test { + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Currency = Balances; +} + +#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] +impl pallet_revive::Config for Test { + type AddressMapper = pallet_revive::TestAccountMapper; + type Currency = Balances; + type Precompiles = (ERC20>,); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = RuntimeGenesisConfig { + assets: pallet_assets::GenesisConfig { + assets: vec![(1, 0, true, 1)], + metadata: vec![], + accounts: vec![(1, 1, 100)], + next_asset_id: None, + }, + system: Default::default(), + balances: Default::default(), + revive: Default::default(), + } + .build_storage() + .unwrap(); + let mut ext: sp_io::TestExternalities = t.into(); + ext.execute_with(|| { + System::set_block_number(1); + }); + + ext +} diff --git a/substrate/frame/assets/precompiles/src/tests.rs b/substrate/frame/assets/precompiles/src/tests.rs new file mode 100644 index 0000000000000..0da75a49c60c7 --- /dev/null +++ b/substrate/frame/assets/precompiles/src/tests.rs @@ -0,0 +1,255 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate::{ + alloy::hex, + mock::{new_test_ext, Assets, Balances, RuntimeEvent, RuntimeOrigin, System, Test}, +}; +use alloy::primitives::U256; +use frame_support::{assert_ok, traits::Currency}; +use pallet_revive::DepositLimit; +use sp_core::H160; +use sp_runtime::Weight; + +fn assert_contract_event(contract: H160, event: IERC20Events) { + let (topics, data) = event.into_log_data().split(); + let topics = topics.into_iter().map(|v| H256(v.0)).collect::>(); + System::assert_has_event(RuntimeEvent::Revive(pallet_revive::Event::ContractEmitted { + contract, + data: data.to_vec(), + topics, + })); +} + +#[test] +fn asset_id_extractor_works() { + let address: [u8; 20] = + hex::const_decode_to_array(b"0000053900000000000000000000000001200000").unwrap(); + assert!(InlineIdConfig::<0x0120>::MATCHER.matches(&address)); + assert_eq!( + as AssetPrecompileConfig>::AssetIdExtractor::asset_id_from_address( + &address + ) + .unwrap(), + 1337u32 + ); +} + +#[test] +fn precompile_transfer_works() { + new_test_ext().execute_with(|| { + let asset_id = 0u32; + let asset_addr = H160::from( + hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(), + ); + + let from = 123456789; + let to = 987654321; + + Balances::make_free_balance_be(&from, 100); + Balances::make_free_balance_be(&to, 100); + + let from_addr = ::AddressMapper::to_address(&from); + let to_addr = ::AddressMapper::to_address(&to); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, from, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(from), asset_id, from, 100)); + + let data = + IERC20::transferCall { to: to_addr.0.into(), value: U256::from(10) }.abi_encode(); + + pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(from), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ); + + assert_contract_event( + asset_addr, + IERC20Events::Transfer(IERC20::Transfer { + from: from_addr.0.into(), + to: to_addr.0.into(), + value: U256::from(10), + }), + ); + + assert_eq!(Assets::balance(asset_id, from), 90); + assert_eq!(Assets::balance(asset_id, to), 10); + }); +} + +#[test] +fn total_supply_works() { + new_test_ext().execute_with(|| { + let asset_id = 0u32; + let asset_addr = + hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(); + + let owner = 123456789; + + Balances::make_free_balance_be(&owner, 100); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 1000)); + + let data = IERC20::totalSupplyCall {}.abi_encode(); + + let data = pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(owner), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ) + .result + .unwrap() + .data; + + let ret = IERC20::totalSupplyCall::abi_decode_returns(&data).unwrap(); + assert_eq!(ret, U256::from(1000)); + }); +} + +#[test] +fn balance_of_works() { + new_test_ext().execute_with(|| { + let asset_id = 0u32; + let asset_addr = + hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(); + + let owner = 123456789; + + assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 1000)); + + let account = ::AddressMapper::to_address(&owner).0.into(); + let data = IERC20::balanceOfCall { account }.abi_encode(); + + let data = pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(owner), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ) + .result + .unwrap() + .data; + + let ret = IERC20::balanceOfCall::abi_decode_returns(&data).unwrap(); + assert_eq!(ret, U256::from(1000)); + }); +} + +#[test] +fn approval_works() { + use frame_support::traits::fungibles::approvals::Inspect; + + new_test_ext().execute_with(|| { + let asset_id = 0u32; + let asset_addr = H160::from( + hex::const_decode_to_array(b"0000000000000000000000000000000001200000").unwrap(), + ); + + let owner = 123456789; + let spender = 987654321; + let other = 1122334455; + + Balances::make_free_balance_be(&owner, 100); + Balances::make_free_balance_be(&spender, 100); + Balances::make_free_balance_be(&other, 100); + + let owner_addr = ::AddressMapper::to_address(&owner); + let spender_addr = ::AddressMapper::to_address(&spender); + let other_addr = ::AddressMapper::to_address(&other); + + assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(owner), asset_id, owner, 100)); + + let data = IERC20::approveCall { spender: spender_addr.0.into(), value: U256::from(25) } + .abi_encode(); + + pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(owner), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ); + + assert_contract_event( + asset_addr, + IERC20Events::Approval(IERC20::Approval { + owner: owner_addr.0.into(), + spender: spender_addr.0.into(), + value: U256::from(25), + }), + ); + + let data = + IERC20::allowanceCall { owner: owner_addr.0.into(), spender: spender_addr.0.into() } + .abi_encode(); + + let data = pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(owner), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ) + .result + .unwrap() + .data; + + let ret = IERC20::allowanceCall::abi_decode_returns(&data).unwrap(); + assert_eq!(ret, U256::from(25)); + + let data = IERC20::transferFromCall { + from: owner_addr.0.into(), + to: other_addr.0.into(), + value: U256::from(10), + } + .abi_encode(); + + pallet_revive::Pallet::::bare_call( + RuntimeOrigin::signed(spender), + H160::from(asset_addr), + 0u32.into(), + Weight::MAX, + DepositLimit::UnsafeOnlyForDryRun, + data, + ); + assert_eq!(Assets::balance(asset_id, owner), 90); + assert_eq!(Assets::allowance(asset_id, &owner, &spender), 15); + assert_eq!(Assets::balance(asset_id, other), 10); + + assert_contract_event( + asset_addr, + IERC20Events::Transfer(IERC20::Transfer { + from: owner_addr.0.into(), + to: other_addr.0.into(), + value: U256::from(10), + }), + ); + }); +} diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index b119869bd7bd7..4d487ee2f644d 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -622,7 +622,7 @@ impl, I: 'static> Pallet { /// /// Will fail if the amount transferred is so small that it cannot create the destination due /// to minimum balance requirements. - pub(super) fn do_transfer( + pub fn do_transfer( id: T::AssetId, source: &T::AccountId, dest: &T::AccountId, @@ -921,7 +921,7 @@ impl, I: 'static> Pallet { /// while reserving `T::ApprovalDeposit` from owner /// /// If an approval already exists, the new amount is added to such existing approval - pub(super) fn do_approve_transfer( + pub fn do_approve_transfer( id: T::AssetId, owner: &T::AccountId, delegate: &T::AccountId, @@ -969,7 +969,7 @@ impl, I: 'static> Pallet { /// Will fail if `amount` is greater than the approval from `owner` to 'delegate' /// Will unreserve the deposit from `owner` if the entire approved `amount` is spent by /// 'delegate' - pub(super) fn do_transfer_approved( + pub fn do_transfer_approved( id: T::AssetId, owner: &T::AccountId, delegate: &T::AccountId, diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 40275f0916759..ce3aa193240f2 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -155,7 +155,6 @@ pub mod benchmarking; pub mod migration; #[cfg(test)] pub mod mock; -pub mod precompiles; #[cfg(test)] mod tests; pub mod weights; @@ -1204,7 +1203,7 @@ pub mod pallet { ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); ensure!(origin == details.owner, Error::::NoPermission); if details.owner == owner { - return Ok(()) + return Ok(()); } let metadata_deposit = Metadata::::get(&id).deposit; diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 78fb2b78e4f47..9803f929a566f 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -20,7 +20,6 @@ use super::*; use crate as pallet_assets; -use crate::precompiles::{InlineIdConfig, ERC20}; use codec::Encode; use frame_support::{ assert_ok, construct_runtime, derive_impl, parameter_types, @@ -37,7 +36,6 @@ construct_runtime!( System: frame_system, Balances: pallet_balances, Assets: pallet_assets, - Revive: pallet_revive, } ); @@ -56,13 +54,6 @@ impl pallet_balances::Config for Test { type AccountStore = System; } -#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] -impl pallet_revive::Config for Test { - type AddressMapper = pallet_revive::TestAccountMapper; - type Currency = Balances; - type Precompiles = (ERC20>,); -} - pub struct AssetsCallbackHandle; impl AssetsCallback for AssetsCallbackHandle { fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 968182bf01742..45674c744dc56 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -60,6 +60,7 @@ std = [ "pallet-asset-tx-payment?/std", "pallet-assets-freezer?/std", "pallet-assets-holder?/std", + "pallet-assets-precompiles?/std", "pallet-assets?/std", "pallet-atomic-swap?/std", "pallet-aura?/std", @@ -258,6 +259,7 @@ runtime-benchmarks = [ "pallet-asset-tx-payment?/runtime-benchmarks", "pallet-assets-freezer?/runtime-benchmarks", "pallet-assets-holder?/runtime-benchmarks", + "pallet-assets-precompiles?/runtime-benchmarks", "pallet-assets?/runtime-benchmarks", "pallet-babe?/runtime-benchmarks", "pallet-bags-list?/runtime-benchmarks", @@ -391,6 +393,7 @@ try-runtime = [ "pallet-asset-tx-payment?/try-runtime", "pallet-assets-freezer?/try-runtime", "pallet-assets-holder?/try-runtime", + "pallet-assets-precompiles?/try-runtime", "pallet-assets?/try-runtime", "pallet-atomic-swap?/try-runtime", "pallet-aura?/try-runtime", @@ -600,6 +603,7 @@ runtime-full = [ "pallet-assets", "pallet-assets-freezer", "pallet-assets-holder", + "pallet-assets-precompiles", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", @@ -1279,6 +1283,11 @@ default-features = false optional = true path = "../substrate/frame/assets-holder" +[dependencies.pallet-assets-precompiles] +default-features = false +optional = true +path = "../substrate/frame/assets/precompiles" + [dependencies.pallet-atomic-swap] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 2f15c8b8b3c8f..5726f80322ef7 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -340,6 +340,10 @@ pub use pallet_assets_freezer; #[cfg(feature = "pallet-assets-holder")] pub use pallet_assets_holder; +/// Provides precompiles for `pallet-assets`. +#[cfg(feature = "pallet-assets-precompiles")] +pub use pallet_assets_precompiles; + /// FRAME atomic swap pallet. #[cfg(feature = "pallet-atomic-swap")] pub use pallet_atomic_swap;