diff --git a/container-chains/templates/frontier/runtime/src/xcm_config.rs b/container-chains/templates/frontier/runtime/src/xcm_config.rs index 925d5a711..47aa4efaf 100644 --- a/container-chains/templates/frontier/runtime/src/xcm_config.rs +++ b/container-chains/templates/frontier/runtime/src/xcm_config.rs @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see +use pallet_foreign_asset_creator::{ + AssetBalance, AssetId as AssetIdOf, ForeignAssetCreatedHook, ForeignAssetDestroyedHook, +}; use { super::{ precompiles::FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, AccountId, AllPalletsWithSystem, @@ -376,6 +379,33 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = ForeignAssetBenchmarkHelper; } +pub struct RevertCodePrecompileHook; + +impl ForeignAssetCreatedHook, AssetBalance> + for RevertCodePrecompileHook +{ + fn on_asset_created( + _foreign_asset: &MultiLocation, + asset_id: &AssetIdOf, + _min_balance: &AssetBalance, + ) { + let revert_bytecode = [0x60, 0x00, 0x60, 0x00, 0xFD].to_vec(); + let prefix_slice = [255u8; 18]; + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), *asset_id); + + pallet_evm::Pallet::::create_account(account_id.into(), revert_bytecode.clone()); + } +} + +impl ForeignAssetDestroyedHook> for RevertCodePrecompileHook { + fn on_asset_destroyed(_foreign_asset: &MultiLocation, asset_id: &AssetIdOf) { + let prefix_slice = [255u8; 18]; + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), *asset_id); + + pallet_evm::Pallet::::remove_account(&account_id.into()); + } +} + impl pallet_foreign_asset_creator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForeignAsset = MultiLocation; @@ -384,8 +414,8 @@ impl pallet_foreign_asset_creator::Config for Runtime { type ForeignAssetDestroyerOrigin = EnsureRoot; type Fungibles = ForeignAssets; type WeightInfo = pallet_foreign_asset_creator::weights::SubstrateWeight; - type OnForeignAssetCreated = (); - type OnForeignAssetDestroyed = (); + type OnForeignAssetCreated = RevertCodePrecompileHook; + type OnForeignAssetDestroyed = RevertCodePrecompileHook; } impl pallet_asset_rate::Config for Runtime { @@ -439,3 +469,17 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< /// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. pub type AssetRateAsMultiplier = AssetFeeAsExistentialDepositMultiplier; + +#[test] +fn test_asset_id_to_account_conversion() { + let prefix_slice = [255u8].repeat(18); + let asset_ids_to_check = vec![0u16, 123u16, 3453u16, 10000u16, 65535u16]; + for current_asset_id in asset_ids_to_check { + let account_id = Runtime::asset_id_to_account(prefix_slice.as_slice(), current_asset_id); + assert_eq!( + account_id.to_string().to_lowercase(), + String::from("0xffffffffffffffffffffffffffffffffffff") + + format!("{:04x}", current_asset_id).as_str() + ); + } +} diff --git a/test/suites/dev-frontier-template/test-eth-asset-address/test-eth-asset-address-creation.ts b/test/suites/dev-frontier-template/test-eth-asset-address/test-eth-asset-address-creation.ts new file mode 100644 index 000000000..705d09023 --- /dev/null +++ b/test/suites/dev-frontier-template/test-eth-asset-address/test-eth-asset-address-creation.ts @@ -0,0 +1,51 @@ +import { expect, describeSuite } from "@moonwall/cli"; +import { STATEMINT_LOCATION_EXAMPLE } from "../../../util/constants.ts"; +import { alith } from "@moonwall/util"; + +describeSuite({ + id: "DF0901", + title: "Ethereum asset dummy precompile address creation", + foundationMethods: "dev", + testCases: ({ context, it }) => { + it({ + id: "T01", + title: "dummy precompile address is created when creating the asset and removed when destroyed", + test: async function () { + const assetId = 5; + const assetIdAddress = new Uint8Array([ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 5, + ]); + const revertBytecode = "0x60006000fd"; + const addressInHex = "0x" + Buffer.from(assetIdAddress).toString("hex"); + + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo( + context + .polkadotJs() + .tx.foreignAssetsCreator.createForeignAsset( + STATEMINT_LOCATION_EXAMPLE, + assetId, + alith.address, + true, + 1 + ) + ) + ); + + // After the foreign asset creation, the address should contain revert byte code. + expect(await context.web3().eth.getCode(addressInHex)).to.equal(revertBytecode); + + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.foreignAssetsCreator.destroyForeignAsset(assetId)) + ); + + // After the foreign asset destruction, the revert bytecode from that address should be removed. + expect(await context.web3().eth.getCode(addressInHex)).to.equal("0x"); + }, + }); + }, +});