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");
+ },
+ });
+ },
+});