From a97f0547a9ddb4858c9dc3c487474ab2665f98bf Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:14:28 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20Update=20chain=20IDs=20&=20?= =?UTF-8?q?native=20token=20tickers=20for=20all=203=20environments=20(#280?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the 3 DataHaven environments' chain IDs & native token ticker as follows: * **Mainnet** * **Chain ID**: 55930 * **Ticker**: HAVE * **TestNet** * **Chain ID**: 55931 * **Ticker**: MOCK * **Stagenet** * **Chain ID**: 55932 * **Ticker**: STAGE The PR includes a storage migration for the Stagenet & Testnet environments, that are already live, to update the EVM Chain ID stored in the `pallet-evm-chain-id` pallet. Note: the token symbol will only be updated with the genesis config presets or newly generated chain specs. For already live networks, the existing chain spec must be updated (i.e. the tokenSymbol property changed) and used by all nodes in the network. This change in the chain spec will not alter the chain genesis so it safe to do (in the very early stages of the chain obviously). --------- Co-authored-by: Claude --- operator/Cargo.lock | 1 + operator/node/src/chain_spec/mainnet.rs | 2 +- operator/node/src/chain_spec/stagenet.rs | 4 +- operator/node/src/chain_spec/testnet.rs | 4 +- operator/runtime/common/Cargo.toml | 2 + operator/runtime/common/src/migrations.rs | 171 ++++++++++++++++++ operator/runtime/mainnet/src/configs/mod.rs | 2 +- .../mainnet/src/genesis_config_presets.rs | 2 +- operator/runtime/stagenet/build.rs | 2 +- operator/runtime/stagenet/src/configs/mod.rs | 10 +- .../stagenet/src/genesis_config_presets.rs | 2 +- operator/runtime/stagenet/src/precompiles.rs | 4 +- operator/runtime/stagenet/tests/common.rs | 4 +- operator/runtime/testnet/build.rs | 2 +- operator/runtime/testnet/src/configs/mod.rs | 10 +- .../testnet/src/genesis_config_presets.rs | 2 +- operator/runtime/testnet/src/precompiles.rs | 4 +- operator/runtime/testnet/tests/common.rs | 4 +- test/.papi/metadata/datahaven.scale | Bin 622477 -> 622477 bytes test/suites/native-token-transfer.test.ts | 8 +- 20 files changed, 213 insertions(+), 27 deletions(-) diff --git a/operator/Cargo.lock b/operator/Cargo.lock index f1b38094..97906260 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -3171,6 +3171,7 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-evm", + "pallet-evm-chain-id", "pallet-evm-precompile-proxy", "pallet-migrations", "pallet-safe-mode", diff --git a/operator/node/src/chain_spec/mainnet.rs b/operator/node/src/chain_spec/mainnet.rs index 9e1949fd..058d412c 100644 --- a/operator/node/src/chain_spec/mainnet.rs +++ b/operator/node/src/chain_spec/mainnet.rs @@ -3,7 +3,7 @@ use sc_service::ChainType; use super::ChainSpec; -const EVM_CHAIN_ID: u64 = 1289; +const EVM_CHAIN_ID: u64 = 55930; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; const TOKEN_DECIMALS: u8 = 18; const TOKEN_SYMBOL: &str = "HAVE"; diff --git a/operator/node/src/chain_spec/stagenet.rs b/operator/node/src/chain_spec/stagenet.rs index 87ae6b0e..a456b2b9 100644 --- a/operator/node/src/chain_spec/stagenet.rs +++ b/operator/node/src/chain_spec/stagenet.rs @@ -3,10 +3,10 @@ use sc_service::ChainType; use super::ChainSpec; -const EVM_CHAIN_ID: u64 = 1283; +const EVM_CHAIN_ID: u64 = 55932; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; const TOKEN_DECIMALS: u8 = 18; -const TOKEN_SYMBOL: &str = "HAVE"; +const TOKEN_SYMBOL: &str = "STAGE"; pub fn development_chain_spec() -> Result { // Give the token a unit name and decimal places diff --git a/operator/node/src/chain_spec/testnet.rs b/operator/node/src/chain_spec/testnet.rs index 92c371dc..d0c4b352 100644 --- a/operator/node/src/chain_spec/testnet.rs +++ b/operator/node/src/chain_spec/testnet.rs @@ -3,10 +3,10 @@ use sc_service::ChainType; use super::ChainSpec; -const EVM_CHAIN_ID: u64 = 1288; +const EVM_CHAIN_ID: u64 = 55931; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; const TOKEN_DECIMALS: u8 = 18; -const TOKEN_SYMBOL: &str = "HAVE"; +const TOKEN_SYMBOL: &str = "MOCK"; pub fn development_chain_spec() -> Result { // Give the token a unit name and decimal places diff --git a/operator/runtime/common/Cargo.toml b/operator/runtime/common/Cargo.toml index b9f3268d..8c72f790 100644 --- a/operator/runtime/common/Cargo.toml +++ b/operator/runtime/common/Cargo.toml @@ -15,6 +15,7 @@ pallet-authorship = { workspace = true } pallet-balances = { workspace = true } pallet-timestamp = { workspace = true } pallet-evm = { workspace = true } +pallet-evm-chain-id = { workspace = true } pallet-evm-precompile-proxy = { workspace = true } pallet-migrations = { workspace = true } pallet-safe-mode = { workspace = true } @@ -40,6 +41,7 @@ std = [ "pallet-balances/std", "pallet-timestamp/std", "pallet-evm/std", + "pallet-evm-chain-id/std", "pallet-evm-precompile-proxy/std", "pallet-migrations/std", "pallet-safe-mode/std", diff --git a/operator/runtime/common/src/migrations.rs b/operator/runtime/common/src/migrations.rs index 45b3c238..c1695952 100644 --- a/operator/runtime/common/src/migrations.rs +++ b/operator/runtime/common/src/migrations.rs @@ -32,6 +32,101 @@ pub type MigrationStatusHandler = (); /// Default handler triggered on migration failures. pub type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; +/// Multi-block migration for updating the EVM chain ID to the new value. +pub mod evm_chain_id { + use core::marker::PhantomData; + use frame_support::{ + migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, + pallet_prelude::*, + weights::WeightMeter, + }; + + #[cfg(feature = "try-runtime")] + use codec::Encode; + + /// Multi-block migration that updates the stored EVM chain ID to match the new configuration. + pub struct EvmChainIdMigration(PhantomData); + + impl SteppedMigration for EvmChainIdMigration + where + T: pallet_evm_chain_id::Config, + { + type Cursor = (); + type Identifier = MigrationId<20>; + + fn id() -> Self::Identifier { + MigrationId { + pallet_id: *b"dh-evm-chain-id-v1 ", + version_from: 0, + version_to: 1, + } + } + + fn step( + cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + // This migration completes in a single step + if cursor.is_some() { + return Ok(None); + } + + let required = T::DbWeight::get().reads_writes(1, 1); + if meter.try_consume(required).is_err() { + return Err(SteppedMigrationError::InsufficientWeight { required }); + } + + log::info!( + "🔄 [EVM Chain ID Migration] Updating chain ID to {}", + NEW_CHAIN_ID + ); + + // Update the chain ID storage + pallet_evm_chain_id::ChainId::::put(NEW_CHAIN_ID); + + log::info!( + "✅ [EVM Chain ID Migration] Successfully updated chain ID to {}", + NEW_CHAIN_ID + ); + + Ok(None) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let old_chain_id = pallet_evm_chain_id::ChainId::::get(); + log::info!( + "📋 [EVM Chain ID Migration] Current chain ID: {}", + old_chain_id + ); + Ok(old_chain_id.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use codec::Decode; + + let old_chain_id = + u64::decode(&mut &state[..]).map_err(|_| "Failed to decode old chain ID")?; + let new_chain_id = pallet_evm_chain_id::ChainId::::get(); + + log::info!( + "🔍 [EVM Chain ID Migration] Chain ID updated from {} to {}", + old_chain_id, + new_chain_id + ); + + if new_chain_id != NEW_CHAIN_ID { + return Err(sp_runtime::TryRuntimeError::Other( + "Chain ID was not updated correctly", + )); + } + + Ok(()) + } + } +} + /// Multi-block migration for renaming the EVM pallet alias. pub mod evm_alias { use core::marker::PhantomData; @@ -232,6 +327,7 @@ mod tests { Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage}, EVM: pallet_evm::{Pallet, Call, Storage, Config, Event}, + EvmChainId: pallet_evm_chain_id::{Pallet, Storage}, } ); @@ -280,6 +376,8 @@ mod tests { type PrecompilesValue = MockPrecompiles; } + impl pallet_evm_chain_id::Config for TestRuntime {} + pub struct FixedGasPrice; impl pallet_evm::FeeCalculator for FixedGasPrice { fn min_gas_price() -> (U256, Weight) { @@ -375,4 +473,77 @@ mod tests { assert_eq!(count_keys(&old_storage_prefix(b"AccountStorages")[..]), 0); }); } + + #[test] + fn evm_chain_id_migration_updates_storage() { + use super::evm_chain_id::EvmChainIdMigration; + + let mut storage = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig::::default() + .assimilate_storage(&mut storage) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(storage); + + ext.execute_with(|| { + // Set an old chain ID value + const OLD_CHAIN_ID: u64 = 12345; + const NEW_CHAIN_ID: u64 = 55931; + + pallet_evm_chain_id::ChainId::::put(OLD_CHAIN_ID); + assert_eq!( + pallet_evm_chain_id::ChainId::::get(), + OLD_CHAIN_ID + ); + + // Run the migration + let mut meter = WeightMeter::with_limit(Weight::MAX); + let result = EvmChainIdMigration::::step(None, &mut meter); + + // Verify migration succeeded and completed in one step + assert!(result.is_ok()); + assert_eq!(result.unwrap(), None); + + // Verify the chain ID was updated + assert_eq!( + pallet_evm_chain_id::ChainId::::get(), + NEW_CHAIN_ID + ); + }); + } + + #[test] + fn evm_chain_id_migration_is_idempotent() { + use super::evm_chain_id::EvmChainIdMigration; + + let mut storage = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig::::default() + .assimilate_storage(&mut storage) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(storage); + + ext.execute_with(|| { + const NEW_CHAIN_ID: u64 = 55932; + + // Run the migration twice + let mut meter = WeightMeter::with_limit(Weight::MAX); + let result1 = EvmChainIdMigration::::step(None, &mut meter); + + let mut meter = WeightMeter::with_limit(Weight::MAX); + let result2 = EvmChainIdMigration::::step(None, &mut meter); + + // Both should succeed + assert!(result1.is_ok()); + assert!(result2.is_ok()); + + // Chain ID should be set correctly + assert_eq!( + pallet_evm_chain_id::ChainId::::get(), + NEW_CHAIN_ID + ); + }); + } } diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 805552a1..7f3ba6c1 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -164,7 +164,7 @@ use datahaven_runtime_common::benchmarking::BenchmarkHelper; pub(crate) use crate::weights as mainnet_weights; -const EVM_CHAIN_ID: u64 = 1289; +const EVM_CHAIN_ID: u64 = 55930; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; //╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ diff --git a/operator/runtime/mainnet/src/genesis_config_presets.rs b/operator/runtime/mainnet/src/genesis_config_presets.rs index 85143732..ccf4634e 100644 --- a/operator/runtime/mainnet/src/genesis_config_presets.rs +++ b/operator/runtime/mainnet/src/genesis_config_presets.rs @@ -16,7 +16,7 @@ use sp_core::{ecdsa, Pair, Public}; use sp_genesis_builder::{self, PresetId}; use sp_runtime::traits::{IdentifyAccount, Verify}; -const MAINNET_EVM_CHAIN_ID: u64 = 1289; +const MAINNET_EVM_CHAIN_ID: u64 = 55930; // Returns the genesis config presets populated with given parameters. fn testnet_genesis( diff --git a/operator/runtime/stagenet/build.rs b/operator/runtime/stagenet/build.rs index 6fba03d0..b65087b1 100644 --- a/operator/runtime/stagenet/build.rs +++ b/operator/runtime/stagenet/build.rs @@ -1,7 +1,7 @@ #[cfg(all(feature = "std", feature = "metadata-hash"))] fn main() { substrate_wasm_builder::WasmBuilder::init_with_defaults() - .enable_metadata_hash("HAVE", 18) + .enable_metadata_hash("STAGE", 18) .build(); } diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index d1d37a88..4a7766c5 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -164,7 +164,7 @@ use datahaven_runtime_common::benchmarking::BenchmarkHelper; pub(crate) use crate::weights as stagenet_weights; -const EVM_CHAIN_ID: u64 = 1283; +const EVM_CHAIN_ID: u64 = 55932; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; //╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ @@ -855,7 +855,13 @@ impl pallet_parameters::Config for Runtime { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; + type Migrations = ( + datahaven_runtime_common::migrations::MultiBlockMigrationList, + datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration< + Runtime, + EVM_CHAIN_ID, + >, + ); #[cfg(feature = "runtime-benchmarks")] type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; type CursorMaxLen = MigrationCursorMaxLen; diff --git a/operator/runtime/stagenet/src/genesis_config_presets.rs b/operator/runtime/stagenet/src/genesis_config_presets.rs index 844ade99..7c243615 100644 --- a/operator/runtime/stagenet/src/genesis_config_presets.rs +++ b/operator/runtime/stagenet/src/genesis_config_presets.rs @@ -16,7 +16,7 @@ use sp_core::{ecdsa, Pair, Public}; use sp_genesis_builder::{self, PresetId}; use sp_runtime::traits::{IdentifyAccount, Verify}; -const STAGENET_EVM_CHAIN_ID: u64 = 1283; +const STAGENET_EVM_CHAIN_ID: u64 = 55932; // Returns the genesis config presets populated with given parameters. fn testnet_genesis( diff --git a/operator/runtime/stagenet/src/precompiles.rs b/operator/runtime/stagenet/src/precompiles.rs index 3b2919eb..3530b77b 100644 --- a/operator/runtime/stagenet/src/precompiles.rs +++ b/operator/runtime/stagenet/src/precompiles.rs @@ -41,11 +41,11 @@ pub struct NativeErc20Metadata; impl Erc20Metadata for NativeErc20Metadata { fn name() -> &'static str { - "HAVE" + "STAGE" } fn symbol() -> &'static str { - "HAVE" + "STAGE" } fn decimals() -> u8 { diff --git a/operator/runtime/stagenet/tests/common.rs b/operator/runtime/stagenet/tests/common.rs index c20bcef9..05e5d62a 100644 --- a/operator/runtime/stagenet/tests/common.rs +++ b/operator/runtime/stagenet/tests/common.rs @@ -205,8 +205,8 @@ pub fn root_origin() -> RuntimeOrigin { #[allow(dead_code)] pub fn datahaven_token_metadata() -> snowbridge_core::AssetMetadata { snowbridge_core::AssetMetadata { - name: b"HAVE".to_vec().try_into().unwrap(), - symbol: b"wHAVE".to_vec().try_into().unwrap(), + name: b"STAGE".to_vec().try_into().unwrap(), + symbol: b"wSTAGE".to_vec().try_into().unwrap(), decimals: 18, } } diff --git a/operator/runtime/testnet/build.rs b/operator/runtime/testnet/build.rs index 6fba03d0..b7af7f28 100644 --- a/operator/runtime/testnet/build.rs +++ b/operator/runtime/testnet/build.rs @@ -1,7 +1,7 @@ #[cfg(all(feature = "std", feature = "metadata-hash"))] fn main() { substrate_wasm_builder::WasmBuilder::init_with_defaults() - .enable_metadata_hash("HAVE", 18) + .enable_metadata_hash("MOCK", 18) .build(); } diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index 67df3642..39cdbd78 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -164,7 +164,7 @@ use bridge_hub_common::AggregateMessageOrigin; #[cfg(feature = "runtime-benchmarks")] use datahaven_runtime_common::benchmarking::BenchmarkHelper; -const EVM_CHAIN_ID: u64 = 1288; +const EVM_CHAIN_ID: u64 = 55931; const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; //╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ @@ -858,7 +858,13 @@ impl pallet_parameters::Config for Runtime { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; + type Migrations = ( + datahaven_runtime_common::migrations::MultiBlockMigrationList, + datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration< + Runtime, + EVM_CHAIN_ID, + >, + ); #[cfg(feature = "runtime-benchmarks")] type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; type CursorMaxLen = MigrationCursorMaxLen; diff --git a/operator/runtime/testnet/src/genesis_config_presets.rs b/operator/runtime/testnet/src/genesis_config_presets.rs index 9f23b0dd..932fc961 100644 --- a/operator/runtime/testnet/src/genesis_config_presets.rs +++ b/operator/runtime/testnet/src/genesis_config_presets.rs @@ -16,7 +16,7 @@ use sp_core::{ecdsa, Pair, Public}; use sp_genesis_builder::{self, PresetId}; use sp_runtime::traits::{IdentifyAccount, Verify}; -const TESTNET_EVM_CHAIN_ID: u64 = 1288; +const TESTNET_EVM_CHAIN_ID: u64 = 55931; // Returns the genesis config presets populated with given parameters. fn testnet_genesis( diff --git a/operator/runtime/testnet/src/precompiles.rs b/operator/runtime/testnet/src/precompiles.rs index 5c8920c2..675a03f0 100644 --- a/operator/runtime/testnet/src/precompiles.rs +++ b/operator/runtime/testnet/src/precompiles.rs @@ -41,11 +41,11 @@ pub struct NativeErc20Metadata; impl Erc20Metadata for NativeErc20Metadata { fn name() -> &'static str { - "HAVE" + "MOCK" } fn symbol() -> &'static str { - "HAVE" + "MOCK" } fn decimals() -> u8 { diff --git a/operator/runtime/testnet/tests/common.rs b/operator/runtime/testnet/tests/common.rs index 2405bf93..56a8b875 100644 --- a/operator/runtime/testnet/tests/common.rs +++ b/operator/runtime/testnet/tests/common.rs @@ -205,8 +205,8 @@ pub fn root_origin() -> RuntimeOrigin { #[allow(dead_code)] pub fn datahaven_token_metadata() -> snowbridge_core::AssetMetadata { snowbridge_core::AssetMetadata { - name: b"HAVE".to_vec().try_into().unwrap(), - symbol: b"wHAVE".to_vec().try_into().unwrap(), + name: b"MOCK".to_vec().try_into().unwrap(), + symbol: b"wMOCK".to_vec().try_into().unwrap(), decimals: 18, } } diff --git a/test/.papi/metadata/datahaven.scale b/test/.papi/metadata/datahaven.scale index 08f0c18cfe5dc50c601114dc6dd541539102d428..8d66c3f94af54151541fc7cc779ee598e1e5f6d5 100644 GIT binary patch delta 44 vcmeDEuiE=xwV{Qvg{g(Pg=GtCraV*4t@dnrRv=~rVs;?r*q$xVsgwW!bkz>P delta 44 ucmeDEuiE=xwV{Qvg{g(Pg=GtCraTifYkRgl2(tk(I}meh&z9#@N&o;>S`Bgl diff --git a/test/suites/native-token-transfer.test.ts b/test/suites/native-token-transfer.test.ts index f7bd1d15..21afa225 100644 --- a/test/suites/native-token-transfer.test.ts +++ b/test/suites/native-token-transfer.test.ts @@ -146,8 +146,8 @@ describe("Native Token Transfer", () => { sender: { type: "V5", value: { parents: 0, interior: { type: "Here", value: undefined } } }, asset_id: { type: "V5", value: { parents: 0, interior: { type: "Here", value: undefined } } }, metadata: { - name: Binary.fromText("HAVE"), - symbol: Binary.fromText("wHAVE"), + name: Binary.fromText("STAGE"), + symbol: Binary.fromText("wSTAGE"), decimals: 18 } }); @@ -220,8 +220,8 @@ describe("Native Token Transfer", () => { }) as Promise ]); - expect(tokenName).toBe("HAVE"); - expect(tokenSymbol).toBe("wHAVE"); + expect(tokenName).toBe("STAGE"); + expect(tokenSymbol).toBe("wSTAGE"); expect(tokenDecimals).toBe(18); }, 300_000); // 5 minute timeout for registration