From a8ca172daba31e8fae19d160c3b7e3d5f4370faa Mon Sep 17 00:00:00 2001 From: Ron Date: Mon, 2 Sep 2024 17:00:34 +0800 Subject: [PATCH 01/90] Polkadot assets on ethereum (#128) * Register token from polkadot * Extract AssetRegistrarMetadata * ReserveTransfer from AssetHub * Transfer DOT back to AssetHub * Fix breaking tests * Fix register token * Increase dispatch_gas to cover the actual cost for register token * Add ConvertAssetId to outbound router * Rename to SendForeignToken * Update cost * Move Command.RegisterToken to top level * Use VersionedLocation for storage * Use versioned location * TokenIdOf follow the same pattern as AgentIdOf * Rename MintToken to TransferNativeToken * Rename as SendNativeToken * More refactoring * Remove AgentExecuteCommand * Remove TokenExists check * Update cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs Co-authored-by: Clara van Staden * Benchmark register_token * More checks for the events * More tests for send native token * More tests describe tokenID * More asset identifiers * Add LocationToToken & Always use VersionedLocation * Fix breaking tests * Update gas cost for registering polkadot token * Add AgentExecuteCommand back for compatibility * Split into 2 tests each covers one direction * Revert the change on AH * Switch to native token on penpal for the integration * Make fee asset as configuration parameter of the Channel * Revert fee_asset_id as channel property * Short epoch * Fix integration tests * Fix format * Cleanup * Register relay token * Use relay token as fee asset * Fix clippy * Fix missing dependency * Remove reanchored prefix from asset_id * Fix test * Fix the instruction * Fix test * Multi hop transfer * Fix tests * allow bridge hub assets * Register token from BH directly and remove force_register_token * Decrease gas estimation for PNA * Cleanup * Store Location rather than VersionedLocation * Fix test * Add more tests * Revamp reanchor logic * Improve reanchor & Fix tests * Use secondary governance channel to register PNA * Rename as asset location * Use BoundVec limit size of name&symbol * Describe location of PNA & more tests * Add test * Fix taplo * More tests * Cleanup * Format code * Batch rename token command * Remove agent_id * Rename as AssetMetadata * Add test for penpal native token --------- Co-authored-by: Clara van Staden Co-authored-by: Alistair Singh --- Cargo.lock | 1 + .../pallets/inbound-queue/src/lib.rs | 24 +- .../pallets/inbound-queue/src/mock.rs | 20 +- .../pallets/outbound-queue/src/mock.rs | 10 +- .../pallets/system/src/benchmarking.rs | 23 + bridges/snowbridge/pallets/system/src/lib.rs | 82 +- .../snowbridge/pallets/system/src/tests.rs | 24 +- .../snowbridge/pallets/system/src/weights.rs | 11 + bridges/snowbridge/primitives/core/Cargo.toml | 2 + bridges/snowbridge/primitives/core/src/lib.rs | 54 +- .../primitives/core/src/outbound.rs | 57 ++ .../snowbridge/primitives/core/src/tests.rs | 41 +- .../primitives/router/src/inbound/mod.rs | 175 ++++- .../primitives/router/src/inbound/tests.rs | 66 +- .../primitives/router/src/outbound/mod.rs | 174 ++++- .../primitives/router/src/outbound/tests.rs | 361 +++++---- .../assets/asset-hub-westend/src/genesis.rs | 8 + .../bridges/bridge-hub-westend/src/lib.rs | 1 + .../emulated/common/src/lib.rs | 9 + .../bridges/bridge-hub-westend/src/lib.rs | 10 +- .../bridge-hub-westend/src/tests/mod.rs | 2 + .../src/tests/snowbridge.rs | 702 +++++++++++++++++- .../src/bridge_to_ethereum_config.rs | 206 ++++- .../src/weights/snowbridge_pallet_system.rs | 10 + .../src/bridge_to_ethereum_config.rs | 7 + .../src/weights/snowbridge_pallet_system.rs | 10 + 26 files changed, 1834 insertions(+), 256 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d8f4cef1420..916c0652e96a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19634,6 +19634,7 @@ dependencies = [ "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 4a1486204eb0..9ccb9f4b64de 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -62,9 +62,8 @@ use snowbridge_core::{ sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, StaticLookup, }; -use snowbridge_router_primitives::{ - inbound, - inbound::{ConvertMessage, ConvertMessageError}, +use snowbridge_router_primitives::inbound::{ + ConvertMessage, ConvertMessageError, VersionedMessage, }; use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; @@ -86,6 +85,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); @@ -276,12 +276,12 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } + // Decode payload into VersionMessage + let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) + .map_err(|_| Error::::InvalidPayload)?; + // Decode message into XCM - let (xcm, fee) = - match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { - Ok(message) => Self::do_convert(envelope.message_id, message)?, - Err(_) => return Err(Error::::InvalidPayload.into()), - }; + let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; log::info!( target: LOG_TARGET, @@ -323,12 +323,10 @@ pub mod pallet { impl Pallet { pub fn do_convert( message_id: H256, - message: inbound::VersionedMessage, + message: VersionedMessage, ) -> Result<(Xcm<()>, BalanceOf), Error> { - let (mut xcm, fee) = - T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; - // Append the message id as an XCM topic - xcm.inner_mut().extend(vec![SetTopic(message_id.into())]); + let (xcm, fee) = T::MessageConverter::convert(message_id, message) + .map_err(|e| Error::::ConvertMessage(e))?; Ok((xcm, fee)) } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index a031676c6076..5e1a045f8476 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -10,12 +10,12 @@ use snowbridge_beacon_primitives::{ use snowbridge_core::{ gwei, inbound::{Log, Proof, VerificationError}, - meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, + meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, }; use snowbridge_router_primitives::inbound::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, Verify}, + traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, FixedU128, MultiSignature, }; use sp_std::{convert::From, default::Default}; @@ -111,6 +111,9 @@ parameter_types! { pub const SendTokenExecutionFee: u128 = 1_000_000_000; pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(Westend), Parachain(1013)].into(); + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -204,6 +207,16 @@ impl TransactAsset for SuccessfulTransactor { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::parent()) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; @@ -217,6 +230,9 @@ impl inbound_queue::Config for Test { InboundQueuePalletInstance, AccountId, Balance, + MockTokenIdConvert, + UniversalLocation, + GlobalAssetHub, >; type PricingParameters = Parameters; type ChannelLookup = MockChannelLookup; diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index d65a96e2702d..0b34893333e4 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -164,13 +164,11 @@ pub fn mock_message(sibling_para_id: u32) -> Message { Message { id: None, channel_id: ParaId::from(sibling_para_id).into(), - command: Command::AgentExecute { + command: Command::TransferNativeToken { agent_id: Default::default(), - command: AgentExecuteCommand::TransferToken { - token: Default::default(), - recipient: Default::default(), - amount: 0, - }, + token: Default::default(), + recipient: Default::default(), + amount: 0, }, } } diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index ef908ad6a3f9..3a1540ebc826 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -159,6 +159,29 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn register_token() -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + + let amount: BalanceOf = + (10_000_000_000_000_u128).saturated_into::().saturated_into(); + + T::Token::mint_into(&caller, amount)?; + + let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]); + let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); + let asset_metadata = AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }; + + #[extrinsic_call] + _(RawOrigin::Signed(caller), asset, asset_metadata); + + Ok(()) + } + impl_benchmark_test_suite!( SnowbridgeControl, crate::mock::new_test_ext(true), diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 39c73e3630e7..4ed14d9c0ca7 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -63,13 +63,16 @@ use frame_system::pallet_prelude::*; use snowbridge_core::{ meth, outbound::{Command, Initializer, Message, OperatingMode, SendError, SendMessage}, - sibling_sovereign_account, AgentId, Channel, ChannelId, ParaId, - PricingParameters as PricingParametersRecord, PRIMARY_GOVERNANCE_CHANNEL, + sibling_sovereign_account, AgentId, AssetMetadata, Channel, ChannelId, ParaId, + PricingParameters as PricingParametersRecord, TokenId, TokenIdOf, PRIMARY_GOVERNANCE_CHANNEL, SECONDARY_GOVERNANCE_CHANNEL, }; use sp_core::{RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::BadOrigin, DispatchError, SaturatedConversion}; +use sp_runtime::{ + traits::{BadOrigin, MaybeEquivalence}, + DispatchError, SaturatedConversion, +}; use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -99,7 +102,7 @@ where } /// Hash the location to produce an agent id -fn agent_id_of(location: &Location) -> Result { +pub fn agent_id_of(location: &Location) -> Result { T::AgentIdOf::convert_location(location).ok_or(Error::::LocationConversionFailed.into()) } @@ -211,6 +214,11 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, + /// Register token + RegisterToken { + asset_id: VersionedLocation, + token_id: H256, + }, } #[pallet::error] @@ -226,6 +234,7 @@ pub mod pallet { InvalidTokenTransferFees, InvalidPricingParameters, InvalidUpgradeParameters, + TokenExists, } /// The set of registered agents @@ -243,6 +252,15 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + #[pallet::storage] + #[pallet::getter(fn tokens)] + pub type Tokens = StorageMap<_, Twox64Concat, TokenId, Location, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn location_tokens)] + pub type LocationToToken = + StorageMap<_, Twox64Concat, Location, TokenId, OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -574,6 +592,29 @@ pub mod pallet { }); Ok(()) } + + /// Sends a message to the Gateway contract to register a new + /// token that represents `asset`. + /// + /// - `origin`: Must be `MultiLocation` from sibling parachain + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::register_token())] + pub fn register_token( + origin: OriginFor, + location: Box, + metadata: AssetMetadata, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let asset_loc: Location = + (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; + + let pays_fee = PaysFee::::Yes(who); + + Self::do_register_token(asset_loc, metadata, pays_fee)?; + + Ok(()) + } } impl Pallet { @@ -663,6 +704,30 @@ pub mod pallet { let secondary_exists = Channels::::contains_key(SECONDARY_GOVERNANCE_CHANNEL); primary_exists && secondary_exists } + + pub(crate) fn do_register_token( + asset_loc: Location, + metadata: AssetMetadata, + pays_fee: PaysFee, + ) -> Result<(), DispatchError> { + // Record the token id or fail if it has already been created + let token_id = TokenIdOf::convert_location(&asset_loc) + .ok_or(Error::::LocationConversionFailed)?; + Tokens::::insert(token_id, asset_loc.clone()); + LocationToToken::::insert(asset_loc.clone(), token_id); + + let command = Command::RegisterForeignToken { + token_id, + name: metadata.name.into_inner(), + symbol: metadata.symbol.into_inner(), + decimals: metadata.decimals, + }; + Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; + + Self::deposit_event(Event::::RegisterToken { asset_id: asset_loc.into(), token_id }); + + Ok(()) + } } impl StaticLookup for Pallet { @@ -684,4 +749,13 @@ pub mod pallet { PricingParameters::::get() } } + + impl MaybeEquivalence for Pallet { + fn convert(id: &TokenId) -> Option { + Tokens::::get(id) + } + fn convert_back(loc: &Location) -> Option { + LocationToToken::::get(loc) + } + } } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 09f24195a30a..ba016b9b4854 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -248,7 +248,7 @@ fn create_channel() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); }); } @@ -264,10 +264,10 @@ fn create_channel_fail_already_exists() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); assert_noop!( - EthereumSystem::create_channel(origin, OperatingMode::Normal), + EthereumSystem::create_channel(origin, OperatingMode::Normal,), Error::::ChannelAlreadyCreated ); }); @@ -334,10 +334,10 @@ fn update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to update it - assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal,)); System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::UpdateChannel { channel_id: ParaId::from(2000).into(), @@ -383,12 +383,12 @@ fn update_channel_bad_origin() { // Signed origin not allowed assert_noop!( - EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode), + EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode,), BadOrigin ); // None origin not allowed - assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode), BadOrigin); + assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode,), BadOrigin); }); } @@ -400,7 +400,7 @@ fn update_channel_fails_not_exist() { // Now try to update it assert_noop!( - EthereumSystem::update_channel(origin, OperatingMode::Normal), + EthereumSystem::update_channel(origin, OperatingMode::Normal,), Error::::NoChannel ); }); @@ -419,7 +419,7 @@ fn force_update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to force update it let force_origin = RuntimeOrigin::root(); @@ -463,7 +463,7 @@ fn transfer_native_from_agent() { // First create the agent and channel assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); let origin = make_xcm_origin(origin_location.clone()); assert_ok!(EthereumSystem::transfer_native_from_agent(origin, recipient, amount),); @@ -549,7 +549,7 @@ fn charge_fee_for_create_agent() { assert_ok!(EthereumSystem::create_agent(origin.clone())); let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); // assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee) let message = Message { @@ -584,7 +584,7 @@ fn charge_fee_for_transfer_native_from_agent() { // create_agent & create_channel first assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // assert sovereign_balance decreased by only the base_fee let sovereign_balance_before = Balances::balance(&sovereign_account); diff --git a/bridges/snowbridge/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs index 6e532a0d8a8c..3513097f8b55 100644 --- a/bridges/snowbridge/pallets/system/src/weights.rs +++ b/bridges/snowbridge/pallets/system/src/weights.rs @@ -42,6 +42,7 @@ pub trait WeightInfo { fn force_transfer_native_from_agent() -> Weight; fn set_token_transfer_fees() -> Weight; fn set_pricing_parameters() -> Weight; + fn register_token() -> Weight; } // For backwards compatibility and tests. @@ -246,4 +247,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/bridges/snowbridge/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml index f9bee1ff4959..fa37c795b2d1 100644 --- a/bridges/snowbridge/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -35,6 +35,7 @@ ethabi = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } +xcm-executor = { workspace = true, default-features = true } [features] default = ["std"] @@ -62,4 +63,5 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ed1af4225d24..b4e9c9b3c857 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -21,14 +21,16 @@ pub use ringbuffer::{RingBufferMap, RingBufferMapImpl}; pub use sp_core::U256; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Contains; +use frame_support::{traits::Contains, BoundedVec}; use hex_literal::hex; use scale_info::TypeInfo; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{Junction::Parachain, Location}; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, +}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -163,4 +165,48 @@ impl DescribeLocation for DescribeHere { /// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on /// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = HashedDescription)>; +pub type AgentIdOf = + HashedDescription)>; + +#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +pub struct AssetMetadata { + pub name: BoundedVec>, + pub symbol: BoundedVec>, + pub decimals: u8, +} + +pub type TokenId = H256; + +pub type TokenIdOf = HashedDescription>; + +pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); +impl DescribeLocation for DescribeGlobalPrefix { + fn describe_location(l: &Location) -> Option> { + match (l.parent_count(), l.first_interior()) { + (1, Some(GlobalConsensus(network))) => { + let tail = l.clone().split_first_interior().0; + let interior = Suffix::describe_location(&tail.into())?; + Some((b"pna", network, interior).encode()) + }, + _ => None, + } + } +} + +pub struct DescribeInner; +impl DescribeLocation for DescribeInner { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [Parachain(id)] => Some((*id).encode()), + [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), + [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => + Some((*id, *instance, *index).encode()), + [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => + Some((*id, *instance, *data).encode()), + [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), + [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + _ => None, + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 0ba0fdb61089..77770761822a 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -139,6 +139,37 @@ mod v1 { // Fee multiplier multiplier: UD60x18, }, + /// Transfer ERC20 tokens + TransferNativeToken { + /// ID of the agent + agent_id: H256, + /// Address of the ERC20 token + token: H160, + /// The recipient of the tokens + recipient: H160, + /// The amount of tokens to transfer + amount: u128, + }, + /// Register foreign token from Polkadot + RegisterForeignToken { + /// ID for the token + token_id: H256, + /// Name of the token + name: Vec, + /// Short symbol for the token + symbol: Vec, + /// Number of decimal places + decimals: u8, + }, + /// Mint foreign token from Polkadot + MintForeignToken { + /// ID for the token + token_id: H256, + /// The recipient of the newly minted tokens + recipient: H160, + /// The amount of tokens to mint + amount: u128, + }, } impl Command { @@ -154,6 +185,9 @@ mod v1 { Command::TransferNativeFromAgent { .. } => 6, Command::SetTokenTransferFees { .. } => 7, Command::SetPricingParameters { .. } => 8, + Command::TransferNativeToken { .. } => 9, + Command::RegisterForeignToken { .. } => 10, + Command::MintForeignToken { .. } => 11, } } @@ -211,6 +245,26 @@ mod v1 { Token::Uint(U256::from(*delivery_cost)), Token::Uint(multiplier.clone().into_inner()), ])]), + Command::TransferNativeToken { agent_id, token, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(agent_id.as_bytes().to_owned()), + Token::Address(*token), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), + Command::RegisterForeignToken { token_id, name, symbol, decimals } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::String(name.to_owned()), + Token::String(symbol.to_owned()), + Token::Uint(U256::from(*decimals)), + ])]), + Command::MintForeignToken { token_id, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), } } } @@ -403,6 +457,9 @@ impl GasMeter for ConstantGasMeter { }, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, + Command::TransferNativeToken { .. } => 100_000, + Command::RegisterForeignToken { .. } => 1_200_000, + Command::MintForeignToken { .. } => 100_000, } } } diff --git a/bridges/snowbridge/primitives/core/src/tests.rs b/bridges/snowbridge/primitives/core/src/tests.rs index 725fff1a9c94..3667ff7f03f9 100644 --- a/bridges/snowbridge/primitives/core/src/tests.rs +++ b/bridges/snowbridge/primitives/core/src/tests.rs @@ -1,5 +1,9 @@ -use crate::{ChannelId, ParaId}; +use crate::{ChannelId, ParaId, TokenIdOf}; use hex_literal::hex; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, Parachain, Westend, +}; +use xcm_executor::traits::ConvertLocation; const EXPECT_CHANNEL_ID: [u8; 32] = hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539"); @@ -11,3 +15,38 @@ fn generate_channel_id() { let channel_id: ChannelId = para_id.into(); assert_eq!(channel_id, EXPECT_CHANNEL_ID.into()); } + +#[test] +fn test_describe_relay_token() { + let asset_location: Location = Location::new(1, [GlobalConsensus(Westend)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_primary_token_from_parachain() { + let asset_location: Location = Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_pallet_instance_prefix() { + let asset_location: Location = + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_general_index_prefix() { + let asset_location: Location = + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} + +#[test] +fn test_describe_token_with_general_key_prefix() { + let asset_location: Location = Location::new( + 1, + [GlobalConsensus(Westend), Parachain(2000), GeneralKey { length: 32, data: [1; 32] }], + ); + assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); +} diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 54e47a7a8b6a..c3383ef31e81 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -9,9 +9,10 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; use scale_info::TypeInfo; -use sp_core::{Get, RuntimeDebug, H160}; +use snowbridge_core::TokenId; +use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::MultiAddress; +use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm_executor::traits::ConvertLocation; @@ -45,7 +46,7 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, - /// Send a token to AssetHub or another parachain + /// Send Ethereum token to AssetHub or another parachain SendToken { /// The address of the ERC20 token to be bridged over to AssetHub token: H160, @@ -56,6 +57,17 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, + /// Send Polkadot token back to the original parachain + SendNativeToken { + /// The Id of the token + token_id: TokenId, + /// The destination for the transfer + destination: Destination, + /// Amount to transfer + amount: u128, + /// XCM execution fee on AssetHub + fee: u128, + }, } /// Destination for bridged tokens @@ -89,10 +101,16 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { _phantom: PhantomData<( CreateAssetCall, @@ -100,6 +118,9 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, )>, } @@ -108,6 +129,11 @@ pub struct MessageToXcm< pub enum ConvertMessageError { /// The message version is not supported for conversion. UnsupportedVersion, + InvalidDestination, + InvalidToken, + /// The fee asset is not supported for conversion. + UnsupportedFeeAsset, + CannotReanchor, } /// convert the inbound message to xcm which will be forwarded to the destination chain @@ -120,20 +146,35 @@ pub trait ConvertMessage { pub type CallIndex = [u8; 2]; -impl - ConvertMessage +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > ConvertMessage for MessageToXcm< CreateAssetCall, CreateAssetDeposit, InboundQueuePalletInstance, AccountId, Balance, - > where + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { type Balance = Balance; type AccountId = AccountId; @@ -145,19 +186,51 @@ impl Ok(Self::convert_register_token(chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(chain_id, token, destination, amount, fee)), + Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), + V1(MessageV1 { + chain_id, + command: SendNativeToken { token_id, destination, amount, fee }, + }) => Self::convert_send_native_token( + message_id, + chain_id, + token_id, + destination, + amount, + fee, + ), } } } -impl - MessageToXcm +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > + MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + UniversalLocation, + GlobalAssetHubLocation, + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + UniversalLocation: Get, + GlobalAssetHubLocation: Get, { fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; @@ -291,6 +364,90 @@ where [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], ) } + + fn convert_send_native_token( + message_id: H256, + chain_id: u64, + token_id: TokenId, + destination: Destination, + amount: u128, + asset_hub_fee: u128, + ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { + let network = Ethereum { chain_id }; + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let (dest_para_id, beneficiary, dest_para_fee) = match destination { + // Final destination is a 32-byte account on AssetHub + Destination::AccountId32 { id } => + (None, Location::new(0, [AccountId32 { network: None, id }]), 0), + // Final destination is a 32-byte account on a sibling of AssetHub + Destination::ForeignAccountId32 { para_id, id, fee } => + (Some(para_id), Location::new(0, [AccountId32 { network: None, id }]), fee), + // Final destination is a 20-byte account on a sibling of AssetHub + Destination::ForeignAccountId20 { para_id, id, fee } => + (Some(para_id), Location::new(0, [AccountKey20 { network: None, key: id }]), fee), + }; + + let total_fees = asset_hub_fee.saturating_add(dest_para_fee); + let total_fee_asset: Asset = (Location::parent(), total_fees).into(); + + let asset_loc = + ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; + + let mut reanchored_asset_loc = asset_loc.clone(); + reanchored_asset_loc + .reanchor(&GlobalAssetHubLocation::get(), &UniversalLocation::get()) + .map_err(|_| ConvertMessageError::CannotReanchor)?; + + let asset: Asset = (reanchored_asset_loc, amount).into(); + + let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); + + let mut instructions = vec![ + ReceiveTeleportedAsset(total_fee_asset.clone().into()), + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), + UniversalOrigin(GlobalConsensus(network)), + WithdrawAsset(asset.clone().into()), + SetFeesMode { jit_withdraw: true }, + ]; + + match dest_para_id { + Some(dest_para_id) => { + let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); + + instructions.extend(vec![ + // Perform a deposit reserve to send to destination chain. + InitiateReserveWithdraw { + assets: Wild(AllCounted(2)), + reserve: Location::new(1, [Parachain(dest_para_id)]), + xcm: vec![ + // Buy execution on target. + BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, + // Deposit asset to beneficiary. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), + ] + .into(), + }, + ]); + }, + None => { + instructions.extend(vec![ + // Deposit both asset and fees to beneficiary so the fees will not get + // trapped. Another benefit is when fees left more than ED on AssetHub could be + // used to create the beneficiary account in case it does not exist. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + ]); + }, + } + + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + + Ok((instructions.into(), total_fees.into())) + } } pub struct GlobalConsensusEthereumConvertsFor(PhantomData); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 75670b05c100..282a16443fc8 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -1,6 +1,6 @@ use super::GlobalConsensusEthereumConvertsFor; use crate::inbound::CallIndex; -use frame_support::parameter_types; +use frame_support::{assert_ok, parameter_types}; use hex_literal::hex; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -38,3 +38,67 @@ fn test_contract_location_with_incorrect_location_fails_convert() { None, ); } + +#[test] +fn test_reanchor_relay_token() { + let asset_id: Location = Location::parent(); + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} + +#[test] +fn test_reanchor_pna_from_ah() { + let asset_id: Location = + Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { + parents: 1, + interior: [ + GlobalConsensus(Westend), + Parachain(1000), + PalletInstance(50), + GeneralIndex(2) + ] + .into() + } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} + +#[test] +fn test_reanchor_pna_from_para() { + let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); +} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb61..4963062f4b4a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -11,10 +11,11 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ - outbound::{AgentExecuteCommand, Command, Message, SendMessage}, - ChannelId, ParaId, + outbound::{Command, Message, SendMessage}, + AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -24,15 +25,32 @@ pub struct EthereumBlobExporter< EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); - -impl ExportXcm - for EthereumBlobExporter + ConvertAssetId, +>( + PhantomData<( + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + )>, +); + +impl + ExportXcm + for EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, AgentHashedDescription: ConvertLocation, + ConvertAssetId: MaybeEquivalence, { type Ticket = (Vec, XcmHash); @@ -87,13 +105,8 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &expected_network); - let (agent_execute_command, message_id) = converter.convert().map_err(|err|{ - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); - SendError::Unroutable - })?; - let source_location = Location::new(1, local_sub.clone()); + let agent_id = match AgentHashedDescription::convert_location(&source_location) { Some(id) => id, None => { @@ -102,13 +115,16 @@ where }, }; + let mut converter = + XcmConverter::::new(&message, expected_network, agent_id); + let (command, message_id) = converter.convert().map_err(|err|{ + log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); + SendError::Unroutable + })?; + let channel_id: ChannelId = ParaId::from(para_id).into(); - let outbound_message = Message { - id: Some(message_id.into()), - channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command }, - }; + let outbound_message = Message { id: Some(message_id.into()), channel_id, command }; // validate the message let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { @@ -154,6 +170,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + ReserveAssetDepositedExpected, + InvalidAsset, + UnexpectedInstruction, } macro_rules! match_expression { @@ -165,18 +184,33 @@ macro_rules! match_expression { }; } -struct XcmConverter<'a, Call> { +struct XcmConverter<'a, ConvertAssetId, Call> { iter: Peekable>>, - ethereum_network: &'a NetworkId, + ethereum_network: NetworkId, + agent_id: AgentId, + _marker: PhantomData, } -impl<'a, Call> XcmConverter<'a, Call> { - fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { - Self { iter: message.inner().iter().peekable(), ethereum_network } +impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> +where + ConvertAssetId: MaybeEquivalence, +{ + fn new(message: &'a Xcm, ethereum_network: NetworkId, agent_id: AgentId) -> Self { + Self { + iter: message.inner().iter().peekable(), + ethereum_network, + agent_id, + _marker: Default::default(), + } } - fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - // Get withdraw/deposit and make native tokens create message. - let result = self.native_tokens_unlock_message()?; + fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + let result = match self.peek() { + Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + // Get withdraw/deposit and make native tokens create message. + Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Err(e) => Err(e), + _ => return Err(XcmConverterError::UnexpectedInstruction), + }?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -186,9 +220,7 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } - fn native_tokens_unlock_message( - &mut self, - ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -262,7 +294,10 @@ impl<'a, Call> XcmConverter<'a, Call> { // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; - Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + Ok(( + Command::TransferNativeToken { agent_id: self.agent_id, token, recipient, amount }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -275,9 +310,88 @@ impl<'a, Call> XcmConverter<'a, Call> { fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { - network == self.ethereum_network + *network == self.ethereum_network } else { true } } + + fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + + // Get the reserve assets. + let reserve_assets = + match_expression!(self.next()?, ReserveAssetDeposited(reserve_assets), reserve_assets) + .ok_or(ReserveAssetDepositedExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution or continue parsing. + let fee_asset = match_expression!(self.peek(), Ok(BuyExecution { fees, .. }), fees); + if fee_asset.is_some() { + let _ = self.next(); + } + + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary.unpack(), + (0, [AccountKey20 { network, key }]) + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + // We only support a single asset at a time. + ensure!(reserve_assets.len() == 1, TooManyAssets); + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) + } + } + + let (asset_id, amount) = match reserve_asset { + Asset { id: AssetId(inner_location), fun: Fungible(amount) } => + Some((inner_location.clone(), *amount)), + _ => None, + } + .ok_or(AssetResolutionFailed)?; + + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; + + let expected_asset_id = ConvertAssetId::convert(&token_id).ok_or(InvalidAsset)?; + + ensure!(asset_id == expected_asset_id, InvalidAsset); + + // Check if there is a SetTopic and skip over it if found. + let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok((Command::MintForeignToken { token_id, recipient, amount }, *topic_id)) + } } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7..75dce864493a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -4,7 +4,8 @@ use snowbridge_core::{ outbound::{Fee, SendError, SendMessageFeeProvider}, AgentIdOf, }; -use xcm::v3::prelude::SendError as XcmSendError; +use sp_std::default::Default; +use xcm::prelude::SendError as XcmSendError; use super::*; @@ -57,6 +58,16 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::new(1, [GlobalConsensus(Westend)])) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + #[test] fn exporter_validate_with_unknown_network_yields_not_applicable() { let network = Ethereum { chain_id: 1337 }; @@ -65,14 +76,14 @@ fn exporter_validate_with_unknown_network_yields_not_applicable() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -84,14 +95,14 @@ fn exporter_validate_with_invalid_destination_yields_missing_argument() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -106,14 +117,14 @@ fn exporter_validate_with_x8_destination_yields_not_applicable() { ); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -125,14 +136,14 @@ fn exporter_validate_without_universal_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -144,14 +155,14 @@ fn exporter_validate_without_global_universal_location_yields_unroutable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -163,14 +174,14 @@ fn exporter_validate_without_global_bridge_location_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -183,14 +194,14 @@ fn exporter_validate_with_remote_universal_source_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -202,14 +213,14 @@ fn exporter_validate_without_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -222,14 +233,14 @@ fn exporter_validate_complex_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -242,14 +253,14 @@ fn exporter_validate_without_xcm_message_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -289,14 +300,14 @@ fn exporter_validate_with_max_target_fee_yields_unroutable() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -316,14 +327,14 @@ fn exporter_validate_with_unparsable_xcm_yields_unroutable() { let mut message: Option> = Some(vec![WithdrawAsset(fees), BuyExecution { fees: fee, weight_limit: Unlimited }].into()); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -362,14 +373,14 @@ fn exporter_validate_xcm_success_case_1() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert!(result.is_ok()); } @@ -381,6 +392,7 @@ fn exporter_deliver_with_submit_failure_yields_unroutable() { BridgedNetwork, MockErrOutboundQueue, AgentIdOf, + MockTokenIdConvert, >::deliver((hex!("deadbeef").to_vec(), XcmHash::default())); assert_eq!(result, Err(XcmSendError::Transport("other transport error"))) } @@ -410,8 +422,10 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -443,8 +457,10 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -478,8 +494,10 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -513,8 +531,10 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::TransferNativeToken { + agent_id: Default::default(), token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, @@ -547,7 +567,8 @@ fn xcm_converter_convert_without_set_topic_yields_set_topic_expected() { ClearTopic, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } @@ -564,7 +585,8 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { .into(); let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } @@ -595,7 +617,8 @@ fn xcm_converter_with_different_fee_asset_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -625,7 +648,8 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -636,7 +660,8 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let message: Xcm<()> = vec![].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); @@ -668,7 +693,8 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -698,10 +724,11 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] @@ -723,7 +750,8 @@ fn xcm_converter_convert_without_withdraw_asset_yields_deposit_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); @@ -756,7 +784,8 @@ fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -794,7 +823,8 @@ fn xcm_converter_convert_with_two_assets_yields_too_many_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -825,7 +855,8 @@ fn xcm_converter_convert_without_consuming_filter_yields_filter_does_not_consume SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); @@ -856,7 +887,8 @@ fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -886,7 +918,8 @@ fn xcm_converter_convert_non_ethereum_asset_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -919,7 +952,8 @@ fn xcm_converter_convert_non_ethereum_chain_asset_yields_asset_resolution_failed SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -952,7 +986,8 @@ fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -989,7 +1024,8 @@ fn xcm_converter_convert_with_non_ethereum_beneficiary_yields_beneficiary_resolu SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1025,7 +1061,8 @@ fn xcm_converter_convert_with_non_ethereum_chain_beneficiary_yields_beneficiary_ SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1056,3 +1093,65 @@ fn test_describe_here() { hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into() ) } + +#[test] +fn xcm_converter_transfer_native_token_success() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + let asset_location = Location::new(1, [GlobalConsensus(Westend)]); + let token_id = TokenIdOf::convert_location(&asset_location).unwrap(); + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = + Command::MintForeignToken { recipient: beneficiary_address.into(), amount, token_id }; + let result = converter.convert(); + assert_eq!(result, Ok((expected_payload, [0; 32]))); +} + +#[test] +fn xcm_converter_transfer_native_token_with_invalid_location_will_fail() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + // Invalid asset location from a different consensus + let asset_location = Location { parents: 2, interior: [GlobalConsensus(Rococo)].into() }; + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let result = converter.convert(); + assert_eq!(result.err(), Some(XcmConverterError::InvalidAsset)); +} diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index d20e059f9fea..7d2de43888b9 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -20,6 +20,7 @@ use sp_core::{sr25519, storage::Storage}; // Cumulus use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, + PenpalBSiblingSovereignAccount, PenpalBTeleportableAssetLocation, PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, }; @@ -80,6 +81,13 @@ pub fn genesis() -> Storage { false, ED, ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), + true, + ED, + ), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index feb59c411c8d..e7a28ebf4a46 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -46,6 +46,7 @@ decl_test_parachains! { pallets = { PolkadotXcm: bridge_hub_westend_runtime::PolkadotXcm, Balances: bridge_hub_westend_runtime::Balances, + EthereumSystem: bridge_hub_westend_runtime::EthereumSystem, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 30e66ced1fb0..e45d41db8f40 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -60,6 +60,7 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const USDT_ID: u32 = 1984; pub const PENPAL_ID: u32 = 2000; +pub const PENPAL_B_ID: u32 = 2001; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { @@ -71,6 +72,14 @@ parameter_types! { ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); + pub PenpalBTeleportableAssetLocation: xcm::v3::Location + = xcm::v3::Location::new(1, [ + xcm::v3::Junction::Parachain(PENPAL_B_ID), + xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + ] + ); + pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); } /// Helper function to generate a crypto pair from seed diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 5e0462d14882..98f640a46800 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -52,7 +52,10 @@ mod imports { BridgeHubWestendParaPallet as BridgeHubWestendPallet, BridgeHubWestendXcmConfig, }, penpal_emulated_chain::{ - penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, + penpal_runtime::xcm_config::{ + LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UniversalLocation as PenpalUniversalLocation, + }, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, westend_emulated_chain::{ @@ -66,8 +69,9 @@ mod imports { BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, - PenpalBParaSender as PenpalBSender, WestendRelay as Westend, - WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, + PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, + WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, + WestendRelaySender as WestendSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index bf894a3baf58..8112697a07df 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -21,6 +21,8 @@ mod send_xcm; mod snowbridge; mod teleport; +mod snowbridge; + pub(crate) fn asset_hub_rococo_location() -> Location { Location::new(2, [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())]) } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b4db9b365f39..4ef5d66fe536 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -16,20 +16,29 @@ use crate::imports::*; use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; use codec::{Decode, Encode}; +use emulated_integration_tests_common::{ + PenpalBSiblingSovereignAccount, RESERVABLE_ASSET_ID, TELEPORTABLE_ASSET_ID, +}; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use snowbridge_core::outbound::OperatingMode; +use rococo_westend_system_emulated_network::{ + asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner, + penpal_emulated_chain::penpal_runtime::xcm_config::RelayLocation, +}; +use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, MessageV1, VersionedMessage, + Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, + VersionedMessage, }; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; +use xcm_executor::traits::ConvertLocation; -const INITIAL_FUND: u128 = 5_000_000_000_000_000_000; +const INITIAL_FUND: u128 = 5_000_000_000_000; pub const CHAIN_ID: u64 = 11155111; pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const XCM_FEE: u128 = 100_000_000_000; -const WETH_AMOUNT: u128 = 1_000_000_000; +const TOKEN_AMOUNT: u128 = 100_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -37,6 +46,12 @@ pub enum ControlCall { CreateAgent, #[codec(index = 4)] CreateChannel { mode: OperatingMode }, + #[codec(index = 11)] + ForceRegisterToken { + location: Box, + asset: Box, + metadata: AssetMetadata, + }, } #[allow(clippy::large_enum_variant)] @@ -61,14 +76,12 @@ fn register_weth_token_from_ethereum_to_asset_hub() { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); - let (xcm, _) = Converter::convert(message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + let (xcm, _) = Converter::convert(message_id, message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -77,9 +90,7 @@ fn register_weth_token_from_ethereum_to_asset_hub() { assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {},] ); }); } @@ -127,7 +138,7 @@ fn send_token_from_ethereum_to_asset_hub() { command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -137,9 +148,7 @@ fn send_token_from_ethereum_to_asset_hub() { // Check that the message was sent assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -149,9 +158,7 @@ fn send_token_from_ethereum_to_asset_hub() { // Check that the token was received and issued as a foreign asset on AssetHub assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); }); } @@ -167,13 +174,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); - AssetHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - BridgeHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - AssetHubWestend::force_xcm_version( - Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), - XCM_VERSION, - ); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); AssetHubWestend::execute_with(|| { @@ -202,7 +202,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -212,8 +212,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Check that the send token message was sent using xcm assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] ); }); @@ -224,9 +223,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Check that AssetHub has issued the foreign asset assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); let assets = vec![Asset { id: AssetId(Location::new( @@ -236,7 +233,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AccountKey20 { network: None, key: WETH }, ], )), - fun: Fungible(WETH_AMOUNT), + fun: Fungible(TOKEN_AMOUNT), }]; let multi_assets = VersionedAssets::V4(Assets::from(assets)); @@ -279,10 +276,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Outbound Queue assert_expected_events!( BridgeHubWestend, - vec![ - - RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued - {..}) => {}, ] + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] ); let events = BridgeHubWestend::events(); // Check that the local fee was credited to the Snowbridge sovereign account @@ -305,3 +299,637 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { ); }); } + +#[test] +fn transfer_relay_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + + let token_id = TokenIdOf::convert_location(&asset_id).unwrap(); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( + 2, + [GlobalConsensus(EthereumNetwork::get())], + )) + .unwrap() + .into(); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + sp_runtime::MultiAddress::Id(BridgeHubWestendSender::get()), + INITIAL_FUND * 10, + )); + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::signed(BridgeHubWestendSender::get()), + Box::new(VersionedLocation::V4(asset_id.clone())), + AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},] + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + )); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, ..}) + if *amount == TOKEN_AMOUNT, + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + // Send relay token back to AH + let message_id: H256 = [0; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Balances(pallet_balances::Event::Burned{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, ..}) + if *who == ethereum_sovereign.clone(), + )), + "native token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *amount >= TOKEN_AMOUNT && *who == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_ah_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + + let asset_id: Location = + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + + let asset_id_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) + .appended_with(asset_id.clone().interior) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); + + // Register token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + asset_id_after_reanchored.clone(), + ); + }); + + // Mint some token + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + RESERVABLE_ASSET_ID, + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // Send partial of the token, will fail if send all + let assets = + vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Transferred{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id, to, ..}) + if *asset_id == RESERVABLE_ASSET_ID && *to == ethereum_sovereign.clone() + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Burned{..}) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Burned { owner, .. }) + if *owner == ethereum_sovereign.clone(), + )), + "token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { owner, .. }) + if *owner == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_penpal_native_token() { + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]); + + let penpal_asset_location_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]); + + let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); + + // Register token on AH + AssetHubWestend::force_create_foreign_asset( + penpal_asset_location.clone().try_into().unwrap(), + PenpalBSiblingSovereignAccount::get().clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // Fund sender on AH + AssetHubWestend::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), + penpal_asset_location.clone().try_into().unwrap(), + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Fund sov of AH on penpal + let ah_sovereign = + PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); + PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + ah_sovereign.clone(), + INITIAL_FUND, + ); + + // Create token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + penpal_asset_location_after_reanchored.clone(), + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { + id: penpal_asset_location.clone().into(), + fun: Fungible(TOKEN_AMOUNT / 10), + }]; + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(VersionedAssets::from(Assets::from(assets))), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) + if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::ForeignAccountId32 { + para_id: PenpalB::para_id().into(), + id: PenpalBReceiver::get().into(), + fee: XCM_FEE, + }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token burnt from some reserved account + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token issued to beneficial + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {},] + ); + + let events = PenpalB::events(); + + // Check that token issued to destination account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { amount, who, ..}) + if *amount == TOKEN_AMOUNT/10 && *who == PenpalBReceiver::get() + )), + "Token minted to beneficiary." + ); + }) +} + +#[test] +fn transfer_penpal_asset() { + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]) + .appended_with(PenpalLocalTeleportableToAssetHub::get()) + .unwrap(); + + let penpal_asset_location_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]) + .appended_with(PenpalLocalTeleportableToAssetHub::get()) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); + + // Fund sender on AH + AssetHubWestend::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), + penpal_asset_location.clone().try_into().unwrap(), + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Fund sov of AH on penpal + let ah_sovereign = + PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); + PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + ah_sovereign.clone(), + INITIAL_FUND, + ); + PenpalB::mint_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + TELEPORTABLE_ASSET_ID, + ah_sovereign.clone(), + TOKEN_AMOUNT, + ); + + // create token + BridgeHubWestend::execute_with(|| { + type Runtime = ::Runtime; + + snowbridge_pallet_system::Tokens::::insert( + token_id, + penpal_asset_location_after_reanchored.clone(), + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { + id: penpal_asset_location.clone().into(), + fun: Fungible(TOKEN_AMOUNT / 10), + }]; + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(VersionedAssets::from(Assets::from(assets))), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) + if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::ForeignAccountId32 { + para_id: PenpalB::para_id().into(), + id: PenpalBReceiver::get().into(), + fee: XCM_FEE, + }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token burnt from some reserved account + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] + ); + }); + + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that token issued to beneficial + assert_expected_events!( + PenpalB, + vec![RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {},] + ); + + let events = PenpalB::events(); + + // Check that token issued to destination account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { amount, owner, ..}) + if *amount == TOKEN_AMOUNT/10 && *owner == PenpalBReceiver::get() + )), + "Token minted to beneficiary." + ); + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 01a762d4b99f..8c122c461ac8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -14,9 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{xcm_config::UniversalLocation, Runtime}; -use snowbridge_router_primitives::outbound::EthereumBlobExporter; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +#[cfg(not(feature = "runtime-benchmarks"))] +use crate::XcmRouter; +use crate::{ + xcm_config, xcm_config::UniversalLocation, Balances, EthereumInboundQueue, + EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, + TreasuryAccount, +}; +use parachains_common::{AccountId, Balance}; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use sp_core::H160; +use testnet_parachains_constants::rococo::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; + +use crate::xcm_config::RelayNetwork; +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; +use sp_runtime::{ + traits::{ConstU32, ConstU8, Keccak256}, + FixedU128, +}; +use xcm::prelude::{GlobalConsensus, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -24,4 +49,179 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; + +// Ethereum Bridge +parameter_types! { + pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); +} + +parameter_types! { + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; + pub Parameters: PricingParameters = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: gwei(20), + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), + }; + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + ConstU8, + AccountId, + Balance, + EthereumSystem, + UniversalLocation, + GlobalAssetHub, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [144, 0, 0, 111], // 0x90000069 + epoch: 0, + }, + altair: Fork { + version: [144, 0, 0, 112], // 0x90000070 + epoch: 50, + }, + bellatrix: Fork { + version: [144, 0, 0, 113], // 0x90000071 + epoch: 100, + }, + capella: Fork { + version: [144, 0, 0, 114], // 0x90000072 + epoch: 56832, + }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, + }; +} + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + // Free consensus update every epoch. Works out to be 225 updates per day. + type FreeHeadersInterval = ConstU32<32>; + type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_pallet_inbound_queue::BenchmarkHelper; + use sp_core::H256; + use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; + + impl BenchmarkHelper for Runtime { + fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs index c6c188e323af..3831111f0977 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 7922d3ed02b1..7c61189c9954 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -33,6 +33,7 @@ use testnet_parachains_constants::westend::{ snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; +use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; @@ -41,6 +42,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; +use xcm::prelude::{GlobalConsensus, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -48,6 +50,7 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; // Ethereum Bridge @@ -64,6 +67,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -84,6 +88,9 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { ConstU8, AccountId, Balance, + EthereumSystem, + UniversalLocation, + GlobalAssetHub, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs index c6c188e323af..3831111f0977 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } From a13e3f915f762ce8ba4a86b0f520885e84cd37f9 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 19:19:18 +0800 Subject: [PATCH 02/90] Fix breaking test --- .../pallets/inbound-queue/src/lib.rs | 5 +- .../primitives/router/src/inbound/mod.rs | 27 ++- .../emulated/common/src/lib.rs | 10 +- .../bridge-hub-westend/src/tests/mod.rs | 1 - .../src/tests/snowbridge.rs | 16 +- .../src/bridge_to_ethereum_config.rs | 2 - .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 186 +----------------- 7 files changed, 40 insertions(+), 207 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 9ccb9f4b64de..676cd8f3a373 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -48,12 +48,11 @@ use frame_support::{ }; use frame_system::ensure_signed; use scale_info::TypeInfo; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ - send_xcm, Instruction::SetTopic, Junction::*, Location, SendError as XcmpSendError, SendXcm, - Xcm, XcmContext, XcmHash, + send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, }; use xcm_executor::traits::TransactAsset; diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index c3383ef31e81..12f00153e20d 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -141,7 +141,10 @@ pub trait ConvertMessage { type Balance: BalanceT + From; type AccountId; /// Converts a versioned message into an XCM message and an optional topicID - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; } pub type CallIndex = [u8; 2]; @@ -179,12 +182,15 @@ where type Balance = Balance; type AccountId = AccountId; - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { use Command::*; use VersionedMessage::*; match message { V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(chain_id, token, fee)), + Ok(Self::convert_register_token(message_id, chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), V1(MessageV1 { @@ -232,7 +238,12 @@ where UniversalLocation: Get, GlobalAssetHubLocation: Get, { - fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { + fn convert_register_token( + message_id: H256, + chain_id: u64, + token: H160, + fee: u128, + ) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; let xcm_fee: Asset = (Location::parent(), fee).into(); let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); @@ -275,6 +286,8 @@ where // Clear the origin so that remaining assets in holding // are claimable by the physical origin (BridgeHub) ClearOrigin, + // Forward message id to Asset Hub + SetTopic(message_id.into()), ] .into(); @@ -282,6 +295,7 @@ where } fn convert_send_token( + message_id: H256, chain_id: u64, token: H160, destination: Destination, @@ -339,6 +353,8 @@ where BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, // Deposit asset to beneficiary. DepositAsset { assets: Definite(asset.into()), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), ] .into(), }, @@ -354,6 +370,9 @@ where }, } + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + (instructions.into(), total_fees.into()) } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index e45d41db8f40..c6b8889730e5 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -72,11 +72,11 @@ parameter_types! { ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); - pub PenpalBTeleportableAssetLocation: xcm::v3::Location - = xcm::v3::Location::new(1, [ - xcm::v3::Junction::Parachain(PENPAL_B_ID), - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalBTeleportableAssetLocation: xcm::v4::Location + = xcm::v4::Location::new(1, [ + xcm::v4::Junction::Parachain(PENPAL_B_ID), + xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 8112697a07df..756fdbfac9f6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -18,7 +18,6 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; mod send_xcm; -mod snowbridge; mod teleport; mod snowbridge; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 4ef5d66fe536..baadb7cc7ff8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -27,9 +27,9 @@ use rococo_westend_system_emulated_network::{ }; use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, - VersionedMessage, + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, }; +use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm_executor::traits::ConvertLocation; @@ -70,13 +70,11 @@ fn register_weth_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); - let (xcm, _) = Converter::convert(message_id, message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); assert_expected_events!( @@ -131,8 +129,6 @@ fn send_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendToken { @@ -142,7 +138,7 @@ fn send_token_from_ethereum_to_asset_hub() { fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the message was sent @@ -194,8 +190,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, @@ -206,7 +200,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the send token message was sent using xcm diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 8c122c461ac8..d55bb694c8ae 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -169,8 +169,6 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; - // Free consensus update every epoch. Works out to be 225 updates per day. - type FreeHeadersInterval = ConstU32<32>; type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c65880771e08..17cc5c4bb6be 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -45,20 +45,17 @@ use bridge_runtime_common::extensions::{ refund_relayer_extension::RefundableParachain, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{ - gwei, meth, outbound::{Command, Fee}, - AgentId, AllowSiblingsOnly, PricingParameters, Rewards, + AgentId, PricingParameters, }; -use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, Keccak256}, + traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, + ApplyExtrinsicResult, }; #[cfg(feature = "std")] @@ -79,16 +76,13 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; -use testnet_parachains_constants::rococo::{ - consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, -}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, AggregateMessageOrigin, }; -use pallet_xcm::EnsureXcm; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm::VersionedLocation; @@ -114,8 +108,6 @@ use parachains_common::{ #[cfg(feature = "runtime-benchmarks")] use alloc::boxed::Box; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -519,174 +511,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -// Ethereum Bridge -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); -} - -parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; - pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmark_helpers { - use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; - use codec::Encode; - use snowbridge_beacon_primitives::BeaconHeader; - use snowbridge_pallet_inbound_queue::BenchmarkHelper; - use sp_core::H256; - use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; - - impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); - } - } - - pub struct DoNothingRouter; - impl SendXcm for DoNothingRouter { - type Ticket = Xcm<()>; - - fn validate( - _dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - Ok((xcm.clone().unwrap(), Assets::new())) - } - fn deliver(xcm: Xcm<()>) -> Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } - } - - impl snowbridge_pallet_system::BenchmarkHelper for () { - fn make_xcm_origin(location: Location) -> RuntimeOrigin { - RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) - } - } -} - -impl snowbridge_pallet_inbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; - type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; -} - -impl snowbridge_pallet_outbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Hashing = Keccak256; - type MessageQueue = MessageQueue; - type Decimals = ConstU8<12>; - type MaxMessagePayloadSize = ConstU32<2048>; - type MaxMessagesPerBlock = ConstU32<32>; - type GasMeter = snowbridge_core::outbound::ConstantGasMeter; - type Balance = Balance; - type WeightToFee = WeightToFee; - type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type Channels = EthereumSystem; -} - -#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 - epoch: 0, - }, - deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 - epoch: 0, - } - }; -} - -#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [144, 0, 0, 111], // 0x90000069 - epoch: 0, - }, - altair: Fork { - version: [144, 0, 0, 112], // 0x90000070 - epoch: 50, - }, - bellatrix: Fork { - version: [144, 0, 0, 113], // 0x90000071 - epoch: 100, - }, - capella: Fork { - version: [144, 0, 0, 114], // 0x90000072 - epoch: 56832, - }, - deneb: Fork { - version: [144, 0, 0, 115], // 0x90000073 - epoch: 132608, - }, - }; -} - -impl snowbridge_pallet_ethereum_client::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; -} - -impl snowbridge_pallet_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OutboundQueue = EthereumOutboundQueue; - type SiblingOrigin = EnsureXcm; - type AgentIdOf = snowbridge_core::AgentIdOf; - type TreasuryAccount = TreasuryAccount; - type Token = Balances; - type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type DefaultPricingParameters = Parameters; - type InboundDeliveryCost = EthereumInboundQueue; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime From 94f1d1b6bc4073438fdcf874cbc8199ee19b8eda Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 19:32:23 +0800 Subject: [PATCH 03/90] Disable register PNA --- bridges/snowbridge/pallets/system/src/benchmarking.rs | 2 +- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 3a1540ebc826..4314c7528af6 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -177,7 +177,7 @@ mod benchmarks { }; #[extrinsic_call] - _(RawOrigin::Signed(caller), asset, asset_metadata); + _(RawOrigin::Root, asset, asset_metadata); Ok(()) } diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 4ed14d9c0ca7..8631a45d4f64 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -604,12 +604,12 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - let who = ensure_signed(origin)?; + ensure_root(origin)?; let asset_loc: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::Yes(who); + let pays_fee = PaysFee::::No; Self::do_register_token(asset_loc, metadata, pays_fee)?; From 664e3dc5262907b46656cd4df7959ab60bda2d1e Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:28:37 +0800 Subject: [PATCH 04/90] Split half of the asset_hub_fee for delivery cost on AH --- .../primitives/router/src/inbound/mod.rs | 14 ++++++++++---- .../bridge-hub-westend/src/tests/snowbridge.rs | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 12f00153e20d..00a7c0cc66fc 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -393,7 +393,7 @@ where asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -428,7 +428,6 @@ where DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), UniversalOrigin(GlobalConsensus(network)), WithdrawAsset(asset.clone().into()), - SetFeesMode { jit_withdraw: true }, ]; match dest_para_id { @@ -436,9 +435,16 @@ where let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // Perform a deposit reserve to send to destination chain. + // Perform a reserve withdraw to send to destination chain. Leave half of the + // asset_hub_fee for the delivery cost InitiateReserveWithdraw { - assets: Wild(AllCounted(2)), + assets: Definite( + vec![ + asset.clone(), + (Location::parent(), dest_para_fee + asset_hub_fee / 2).into(), + ] + .into(), + ), reserve: Location::new(1, [Parachain(dest_para_id)]), xcm: vec![ // Buy execution on target. diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index baadb7cc7ff8..e0dd2a42c340 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -325,7 +325,7 @@ fn transfer_relay_token() { )); assert_ok!(::EthereumSystem::register_token( - RuntimeOrigin::signed(BridgeHubWestendSender::get()), + RuntimeOrigin::root(), Box::new(VersionedLocation::V4(asset_id.clone())), AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), @@ -457,6 +457,7 @@ fn transfer_ah_token() { GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) .unwrap() .into(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); let asset_id: Location = [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); From 5ef32c8729f8f1f8c41b82a2d87f99e65f450c2c Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:46:03 +0800 Subject: [PATCH 05/90] Explicit to use XCM v4 as a storage key --- bridges/snowbridge/pallets/system/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 8631a45d4f64..30aba4588ba5 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -254,12 +254,13 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn tokens)] - pub type Tokens = StorageMap<_, Twox64Concat, TokenId, Location, OptionQuery>; + pub type Tokens = + StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; #[pallet::storage] #[pallet::getter(fn location_tokens)] pub type LocationToToken = - StorageMap<_, Twox64Concat, Location, TokenId, OptionQuery>; + StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] From 37e28ed1f36d6bd706b6db3b908f6180af1da5a4 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 22:54:17 +0800 Subject: [PATCH 06/90] Fix test --- .../bridge-hubs/bridge-hub-rococo/tests/tests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e91837af0b21..c866cce5663d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -18,11 +18,13 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, + bridge_common_config, bridge_to_bulletin_config, + bridge_to_ethereum_config::EthereumGatewayAddress, + bridge_to_westend_config, xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, - AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, - Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, + AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, + SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; From e44ea71e080a41f6abcf459a33484a9d91b19daf Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 23:30:02 +0800 Subject: [PATCH 07/90] Add prdoc --- prdoc/pr_5546.prdoc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 prdoc/pr_5546.prdoc diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc new file mode 100644 index 000000000000..70cf730f840d --- /dev/null +++ b/prdoc/pr_5546.prdoc @@ -0,0 +1,30 @@ +title: "Transfer polkadot native assets to Ethereum through snowbridge" + +doc: + - audience: Runtime Dev + description: | + Transfer polkadot native asset to Ethereum through snowbridge. + +crates: + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-system + bump: patch + - name: snowbridge-core + bump: patch + - name: snowbridge-router-primitives + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-emulated-chain + bump: patch + - name: bridge-hub-westend-integration-tests + bump: minor + - name: asset-hub-westend-emulated-chain + bump: patch + - name: emulated-integration-tests-common + bump: patch From 071ffbe0cb995fe3538cfe7a7feaee65a40d7100 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 2 Sep 2024 23:37:52 +0800 Subject: [PATCH 08/90] Fix fmt --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 6 ++---- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 00a7c0cc66fc..fd3f71390bbe 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,8 +168,7 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -227,8 +226,7 @@ impl< ConvertAssetId, UniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 4963062f4b4a..1ad8be72bcbc 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -44,8 +44,7 @@ impl -where + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, From db71d274265f2562af6e098b813936b5be569721 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 02:09:45 +0800 Subject: [PATCH 09/90] Fix for compatibility --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 1ad8be72bcbc..ee86feaeabc7 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -11,7 +11,7 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ - outbound::{Command, Message, SendMessage}, + outbound::{AgentExecuteCommand, Command, Message, SendMessage}, AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; @@ -294,7 +294,10 @@ where let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; Ok(( - Command::TransferNativeToken { agent_id: self.agent_id, token, recipient, amount }, + Command::AgentExecute { + agent_id: self.agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, *topic_id, )) } From 07f8e82d307c9159d082662dbd4cd7c855983f34 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:21:55 +0200 Subject: [PATCH 10/90] PNA system pallet cleanups (#173) --- bridges/snowbridge/pallets/system/src/lib.rs | 45 +++++++++++-------- bridges/snowbridge/primitives/core/src/lib.rs | 2 +- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 30aba4588ba5..46a94782666c 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -35,8 +35,15 @@ //! //! Typically, Polkadot governance will use the `force_transfer_native_from_agent` and //! `force_update_channel` and extrinsics to manage agents and channels for system parachains. +//! +//! ## Polkadot-native tokens on Ethereum +//! +//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a prerequisite, +//! the token should be registered first. +//! +//! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. +//! #![cfg_attr(not(feature = "std"), no_std)] - #[cfg(test)] mod mock; @@ -214,7 +221,7 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, - /// Register token + /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum RegisterToken { asset_id: VersionedLocation, token_id: H256, @@ -252,14 +259,16 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + /// Lookup table for foreign to native token ID #[pallet::storage] #[pallet::getter(fn tokens)] - pub type Tokens = + pub type ForeignToNativeId = StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; + /// Lookup table for native to foreign token ID #[pallet::storage] #[pallet::getter(fn location_tokens)] - pub type LocationToToken = + pub type NativeToForeignId = StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] @@ -594,10 +603,9 @@ pub mod pallet { Ok(()) } - /// Sends a message to the Gateway contract to register a new - /// token that represents `asset`. + /// Registers a Polkadot-native token as a wrapped ERC20 token on Ethereum. /// - /// - `origin`: Must be `MultiLocation` from sibling parachain + /// - `origin`: Must be root #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( @@ -607,12 +615,12 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; - let asset_loc: Location = + let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; let pays_fee = PaysFee::::No; - Self::do_register_token(asset_loc, metadata, pays_fee)?; + Self::do_register_token(location, metadata, pays_fee)?; Ok(()) } @@ -707,15 +715,16 @@ pub mod pallet { } pub(crate) fn do_register_token( - asset_loc: Location, + location: Location, metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { // Record the token id or fail if it has already been created - let token_id = TokenIdOf::convert_location(&asset_loc) + let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - Tokens::::insert(token_id, asset_loc.clone()); - LocationToToken::::insert(asset_loc.clone(), token_id); + + ForeignToNativeId::::insert(token_id, location.clone()); + NativeToForeignId::::insert(location.clone(), token_id); let command = Command::RegisterForeignToken { token_id, @@ -725,7 +734,7 @@ pub mod pallet { }; Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; - Self::deposit_event(Event::::RegisterToken { asset_id: asset_loc.into(), token_id }); + Self::deposit_event(Event::::RegisterToken { asset_id: location.into(), token_id }); Ok(()) } @@ -752,11 +761,11 @@ pub mod pallet { } impl MaybeEquivalence for Pallet { - fn convert(id: &TokenId) -> Option { - Tokens::::get(id) + fn convert(foreign_id: &TokenId) -> Option { + ForeignToNativeId::::get(id) } - fn convert_back(loc: &Location) -> Option { - LocationToToken::::get(loc) + fn convert_back(location: &Location) -> Option { + NativeToForeignId::::get(location) } } } diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index b4e9c9b3c857..0afba79fd765 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,7 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; -#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, pub symbol: BoundedVec>, From 7566c01e46480fc91096590a91df6b43c0bb56a5 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:20:22 +0800 Subject: [PATCH 11/90] Fix building error --- bridges/snowbridge/pallets/system/src/lib.rs | 7 +++---- bridges/snowbridge/primitives/core/src/lib.rs | 2 +- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 2 ++ .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 ---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 46a94782666c..0814a0e35f0c 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -38,11 +38,10 @@ //! //! ## Polkadot-native tokens on Ethereum //! -//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a prerequisite, -//! the token should be registered first. +//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a +//! prerequisite, the token should be registered first. //! //! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. -//! #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; @@ -762,7 +761,7 @@ pub mod pallet { impl MaybeEquivalence for Pallet { fn convert(foreign_id: &TokenId) -> Option { - ForeignToNativeId::::get(id) + ForeignToNativeId::::get(foreign_id) } fn convert_back(location: &Location) -> Option { NativeToForeignId::::get(location) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 0afba79fd765..f2e4f9131b50 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,7 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, pub symbol: BoundedVec>, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 4e585c99b89e..26857d262c20 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -166,6 +166,8 @@ parameter_types! { }; } +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; + impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index b28113e76b3d..14409ce4642d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -42,10 +42,6 @@ use bridge_runtime_common::extensions::{ CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_core::{ - outbound::{Command, Fee}, - AgentId, PricingParameters, -}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ From ae0f67468d8b55fc0eb623ceaa3b99426b237b67 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:37:47 +0800 Subject: [PATCH 12/90] Fix test --- .../bridge-hub-westend/src/tests/snowbridge.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index e0dd2a42c340..996ab2657329 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -46,12 +46,6 @@ pub enum ControlCall { CreateAgent, #[codec(index = 4)] CreateChannel { mode: OperatingMode }, - #[codec(index = 11)] - ForceRegisterToken { - location: Box, - asset: Box, - metadata: AssetMetadata, - }, } #[allow(clippy::large_enum_variant)] @@ -473,7 +467,7 @@ fn transfer_ah_token() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, asset_id_after_reanchored.clone(), ); @@ -643,7 +637,7 @@ fn transfer_penpal_native_token() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, penpal_asset_location_after_reanchored.clone(), ); @@ -813,7 +807,7 @@ fn transfer_penpal_asset() { BridgeHubWestend::execute_with(|| { type Runtime = ::Runtime; - snowbridge_pallet_system::Tokens::::insert( + snowbridge_pallet_system::ForeignToNativeId::::insert( token_id, penpal_asset_location_after_reanchored.clone(), ); From 26263acee7bb734ec80538bffc67a69d906f6c3f Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 08:49:38 +0800 Subject: [PATCH 13/90] Fix test --- .../primitives/router/src/outbound/tests.rs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 75dce864493a..6e4fd5946340 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -424,11 +424,13 @@ fn xcm_converter_convert_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -459,11 +461,13 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -496,11 +500,13 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -533,11 +539,13 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { .into(); let mut converter = XcmConverter::::new(&message, network, Default::default()); - let expected_payload = Command::TransferNativeToken { + let expected_payload = Command::AgentExecute { agent_id: Default::default(), - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); From 7ee483249a8986acefbc2b69ee8862ebd7120dc8 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 10:04:05 +0800 Subject: [PATCH 14/90] Fix doc --- bridges/snowbridge/pallets/system/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 0814a0e35f0c..145c8c612909 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -41,7 +41,8 @@ //! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a //! prerequisite, the token should be registered first. //! -//! * [`Call:register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. +//! * [`Call::register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. + #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; @@ -603,8 +604,13 @@ pub mod pallet { } /// Registers a Polkadot-native token as a wrapped ERC20 token on Ethereum. + /// Privileged. Can only be called by root. + /// + /// Fee required: No /// /// - `origin`: Must be root + /// - `location`: Location of the asset + /// - `metadata`: Metadata(name,symbol,decimals) of the asset #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( From 46cb3528cd8cd1394af2335a6907d7ab8647717a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 10:20:23 +0800 Subject: [PATCH 15/90] SetAppendix with fee not trapped --- .../snowbridge/primitives/router/src/inbound/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index fd3f71390bbe..d8292488f14f 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -433,15 +433,16 @@ impl< let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ + // `SetAppendix` ensures that `fees` are not trapped in any case + SetAppendix(Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: Location::new(1, [Parachain(dest_para_id)]), + }])), // Perform a reserve withdraw to send to destination chain. Leave half of the // asset_hub_fee for the delivery cost InitiateReserveWithdraw { assets: Definite( - vec![ - asset.clone(), - (Location::parent(), dest_para_fee + asset_hub_fee / 2).into(), - ] - .into(), + vec![asset.clone(), (Location::parent(), dest_para_fee).into()].into(), ), reserve: Location::new(1, [Parachain(dest_para_id)]), xcm: vec![ From d96c452b542992f8925ce4077d8bbbbae6b21329 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Tue, 3 Sep 2024 13:03:08 +0200 Subject: [PATCH 16/90] semver fixes --- prdoc/pr_5546.prdoc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 70cf730f840d..16e2bd993463 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -10,12 +10,14 @@ crates: bump: patch - name: snowbridge-pallet-inbound-queue bump: patch - - name: snowbridge-pallet-system + - name: snowbridge-pallet-outbound-queue bump: patch + - name: snowbridge-pallet-system + bump: major - name: snowbridge-core - bump: patch + bump: major - name: snowbridge-router-primitives - bump: patch + bump: major - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime @@ -28,3 +30,4 @@ crates: bump: patch - name: emulated-integration-tests-common bump: patch + From c80a5cb6e6c0b32febef41699f71b192b6214d77 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 21:21:00 +0800 Subject: [PATCH 17/90] EnsureRootOrSigned --- bridges/snowbridge/pallets/system/src/lib.rs | 46 ++++++++++++++++++- bridges/snowbridge/pallets/system/src/mock.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 145c8c612909..c2c68c111de1 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -59,6 +59,7 @@ pub mod weights; pub use weights::*; use frame_support::{ + dispatch::RawOrigin, pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, @@ -135,6 +136,39 @@ where No, } +pub struct EnsureRootOrSigned(core::marker::PhantomData); +impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOrSigned { + type Success = (bool, Option>); + fn try_origin(o: T::RuntimeOrigin) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Root => Ok((true, None)), + RawOrigin::Signed(t) => Ok((false, Some(t))), + r => Err(T::RuntimeOrigin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(RawOrigin::Root.into()) + } +} + +pub struct EnsureRootOnly(core::marker::PhantomData); +impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOnly { + type Success = (bool, Option>); + fn try_origin(o: T::RuntimeOrigin) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Root => Ok((true, None)), + r => Err(T::RuntimeOrigin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(RawOrigin::Root.into()) + } +} + #[frame_support::pallet] pub mod pallet { use snowbridge_core::StaticLookup; @@ -176,6 +210,11 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; + + type RegisterTokenOrigin: EnsureOrigin< + Self::RuntimeOrigin, + Success = (bool, Option>), + >; } #[pallet::event] @@ -618,12 +657,15 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - ensure_root(origin)?; + let (is_sudo, who) = T::RegisterTokenOrigin::ensure_origin(origin)?; let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::No; + let mut pays_fee = PaysFee::::No; + if !is_sudo && who.is_some() { + pays_fee = PaysFee::::Yes(who.unwrap()); + } Self::do_register_token(location, metadata, pays_fee)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27..a3946497b8cf 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -22,6 +22,7 @@ use xcm::prelude::*; #[cfg(feature = "runtime-benchmarks")] use crate::BenchmarkHelper; +use crate::EnsureRootOrSigned; type Block = frame_system::mocking::MockBlock; type Balance = u128; @@ -210,6 +211,7 @@ impl crate::Config for Test { type InboundDeliveryCost = InboundDeliveryCost; #[cfg(feature = "runtime-benchmarks")] type Helper = (); + type RegisterTokenOrigin = EnsureRootOrSigned; } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 26857d262c20..49d436df07ee 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -37,6 +37,7 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; +use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -188,6 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 621d39af5af0..e11b55da619f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -38,6 +38,7 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; +use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -188,6 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] From 9caa782ea7c1304fb803b6ccbff74099d840ca3f Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 21:25:56 +0800 Subject: [PATCH 18/90] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index d8292488f14f..1f66d1a2e42a 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -382,6 +382,9 @@ impl< ) } + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign account + /// of the Gateway contract and either deposits those assets into a recipient account or + /// forwards the assets to another parachain. fn convert_send_native_token( message_id: H256, chain_id: u64, From 92df2b5b3b9459ac960648b99f27c7a1a9c3d026 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 21:26:08 +0800 Subject: [PATCH 19/90] Update bridges/snowbridge/primitives/core/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index f2e4f9131b50..e7043310f478 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -177,6 +177,7 @@ pub struct AssetMetadata { pub type TokenId = H256; +/// Convert a token location to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); From c699503046edbce76211c8e1ae93eb09456f4481 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 21:44:34 +0800 Subject: [PATCH 20/90] Revert "EnsureRootOrSigned" This reverts commit c80a5cb6e6c0b32febef41699f71b192b6214d77. --- bridges/snowbridge/pallets/system/src/lib.rs | 46 +------------------ bridges/snowbridge/pallets/system/src/mock.rs | 2 - .../src/bridge_to_ethereum_config.rs | 2 - .../src/bridge_to_ethereum_config.rs | 2 - 4 files changed, 2 insertions(+), 50 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c2c68c111de1..145c8c612909 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -59,7 +59,6 @@ pub mod weights; pub use weights::*; use frame_support::{ - dispatch::RawOrigin, pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, @@ -136,39 +135,6 @@ where No, } -pub struct EnsureRootOrSigned(core::marker::PhantomData); -impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOrSigned { - type Success = (bool, Option>); - fn try_origin(o: T::RuntimeOrigin) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Root => Ok((true, None)), - RawOrigin::Signed(t) => Ok((false, Some(t))), - r => Err(T::RuntimeOrigin::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(RawOrigin::Root.into()) - } -} - -pub struct EnsureRootOnly(core::marker::PhantomData); -impl EnsureOrigin<::RuntimeOrigin> for EnsureRootOnly { - type Success = (bool, Option>); - fn try_origin(o: T::RuntimeOrigin) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Root => Ok((true, None)), - r => Err(T::RuntimeOrigin::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(RawOrigin::Root.into()) - } -} - #[frame_support::pallet] pub mod pallet { use snowbridge_core::StaticLookup; @@ -210,11 +176,6 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; - - type RegisterTokenOrigin: EnsureOrigin< - Self::RuntimeOrigin, - Success = (bool, Option>), - >; } #[pallet::event] @@ -657,15 +618,12 @@ pub mod pallet { location: Box, metadata: AssetMetadata, ) -> DispatchResult { - let (is_sudo, who) = T::RegisterTokenOrigin::ensure_origin(origin)?; + ensure_root(origin)?; let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let mut pays_fee = PaysFee::::No; - if !is_sudo && who.is_some() { - pays_fee = PaysFee::::Yes(who.unwrap()); - } + let pays_fee = PaysFee::::No; Self::do_register_token(location, metadata, pays_fee)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index a3946497b8cf..98bd3da9ab27 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -22,7 +22,6 @@ use xcm::prelude::*; #[cfg(feature = "runtime-benchmarks")] use crate::BenchmarkHelper; -use crate::EnsureRootOrSigned; type Block = frame_system::mocking::MockBlock; type Balance = u128; @@ -211,7 +210,6 @@ impl crate::Config for Test { type InboundDeliveryCost = InboundDeliveryCost; #[cfg(feature = "runtime-benchmarks")] type Helper = (); - type RegisterTokenOrigin = EnsureRootOrSigned; } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 49d436df07ee..26857d262c20 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -37,7 +37,6 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; -use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -189,7 +188,6 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; - type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index e11b55da619f..621d39af5af0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -38,7 +38,6 @@ use crate::xcm_config::RelayNetwork; use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; -use snowbridge_pallet_system::EnsureRootOnly; use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, @@ -189,7 +188,6 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; - type RegisterTokenOrigin = EnsureRootOnly; } #[cfg(feature = "runtime-benchmarks")] From bce32ea0d4838c69c93eed1225c12f0481e1313a Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:10:29 +0800 Subject: [PATCH 21/90] Update bridges/snowbridge/primitives/core/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index e7043310f478..c31124edf51d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -168,6 +168,7 @@ impl DescribeLocation for DescribeHere { pub type AgentIdOf = HashedDescription)>; +/// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { pub name: BoundedVec>, From e5cb15978bc60ca4016f87599b8446b01df1605f Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:20:27 +0800 Subject: [PATCH 22/90] Use reference instead --- bridges/snowbridge/pallets/system/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 145c8c612909..6b8aa7cdaa7e 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -625,7 +625,7 @@ pub mod pallet { let pays_fee = PaysFee::::No; - Self::do_register_token(location, metadata, pays_fee)?; + Self::do_register_token(&location, metadata, pays_fee)?; Ok(()) } @@ -720,7 +720,7 @@ pub mod pallet { } pub(crate) fn do_register_token( - location: Location, + location: &Location, metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { @@ -739,7 +739,10 @@ pub mod pallet { }; Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; - Self::deposit_event(Event::::RegisterToken { asset_id: location.into(), token_id }); + Self::deposit_event(Event::::RegisterToken { + asset_id: location.clone().into(), + token_id, + }); Ok(()) } From ee7c1e338b42c690b4cf2bf8ab87f69908caaf99 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:21:15 +0800 Subject: [PATCH 23/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 6b8aa7cdaa7e..16e0c48bbf7d 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -610,7 +610,7 @@ pub mod pallet { /// /// - `origin`: Must be root /// - `location`: Location of the asset - /// - `metadata`: Metadata(name,symbol,decimals) of the asset + /// - `metadata`: Metadata to include in the instantiated ERC20 contract on Ethereum #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] pub fn register_token( From 51f38b3b99d589e44890cc7335205c4229ee826e Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:22:40 +0800 Subject: [PATCH 24/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e0c48bbf7d..dd23f660edb9 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -261,7 +261,6 @@ pub mod pallet { /// Lookup table for foreign to native token ID #[pallet::storage] - #[pallet::getter(fn tokens)] pub type ForeignToNativeId = StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; From cfb926980b56a60e7854fdce5416c0b490036795 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:22:53 +0800 Subject: [PATCH 25/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index dd23f660edb9..c643bc491584 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -42,7 +42,6 @@ //! prerequisite, the token should be registered first. //! //! * [`Call::register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. - #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; From 263b622703a04ea0a4cd06efe1e966c2e0ab33e8 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:23:03 +0800 Subject: [PATCH 26/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c643bc491584..a0d90dce78ff 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -222,8 +222,10 @@ pub mod pallet { }, /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum RegisterToken { - asset_id: VersionedLocation, - token_id: H256, + /// Location of Polkadot-native token + location: VersionedLocation, + /// ID of Polkadot-native token on Ethereum + foreign_token_id: H256, }, } From 0306436196eab3f1702840641c813f3c02320e18 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 3 Sep 2024 22:23:13 +0800 Subject: [PATCH 27/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/pallets/system/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index a0d90dce78ff..0d9c0bf89eee 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -267,7 +267,6 @@ pub mod pallet { /// Lookup table for native to foreign token ID #[pallet::storage] - #[pallet::getter(fn location_tokens)] pub type NativeToForeignId = StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; From f339995cdbb59419f3a0c23e7dc7df6f370aec7d Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:42:42 +0800 Subject: [PATCH 28/90] Add comment for the xcm format --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ee86feaeabc7..d3b6c116dd7a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -318,6 +318,13 @@ where } } + /// Convert the xcm for Polkadot-native token from AH into the Command + /// To match transfers of Polkadot-native tokens, we expect an input of the form: + /// # ReserveAssetDeposited + /// # ClearOrigin + /// # BuyExecution + /// # DepositAsset + /// # SetTopic fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; From 91707b7441464de1ec1a037c0eb0a92e66bf0914 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:43:02 +0800 Subject: [PATCH 29/90] Fix building --- bridges/snowbridge/pallets/system/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 0d9c0bf89eee..051e15c387ad 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -224,7 +224,7 @@ pub mod pallet { RegisterToken { /// Location of Polkadot-native token location: VersionedLocation, - /// ID of Polkadot-native token on Ethereum + /// ID of Polkadot-native token on Ethereum foreign_token_id: H256, }, } @@ -739,8 +739,8 @@ pub mod pallet { Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; Self::deposit_event(Event::::RegisterToken { - asset_id: location.clone().into(), - token_id, + location: location.clone().into(), + foreign_token_id: token_id, }); Ok(()) From 2bbee55050f95fd8cd8689b5976436ee7aa855c6 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 3 Sep 2024 22:49:23 +0800 Subject: [PATCH 30/90] Fix format --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 1f66d1a2e42a..3e1ddb1ffc6a 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -382,8 +382,8 @@ impl< ) } - /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign account - /// of the Gateway contract and either deposits those assets into a recipient account or + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign + /// account of the Gateway contract and either deposits those assets into a recipient account or /// forwards the assets to another parachain. fn convert_send_native_token( message_id: H256, From 4090be1ea34de666029ccf44528231b799418223 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 4 Sep 2024 18:56:52 +0800 Subject: [PATCH 31/90] Support more foreign locations --- bridges/snowbridge/primitives/core/src/lib.rs | 27 ++--------- .../xcm-builder/src/location_conversion.rs | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index c31124edf51d..ca5e7e39b8d7 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -28,9 +28,7 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, -}; +use xcm::prelude::{GlobalConsensus, Junction::Parachain, Location}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -179,7 +177,8 @@ pub struct AssetMetadata { pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = HashedDescription>; +pub type TokenIdOf = + HashedDescription>>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { @@ -188,27 +187,9 @@ impl DescribeLocation for DescribeGlobalPrefix (1, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"pna", network, interior).encode()) + Some((b"PNA", network, interior).encode()) }, _ => None, } } } - -pub struct DescribeInner; -impl DescribeLocation for DescribeInner { - fn describe_location(l: &Location) -> Option> { - match l.unpack().1 { - [] => Some(Vec::::new().encode()), - [Parachain(id)] => Some((*id).encode()), - [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), - [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => - Some((*id, *instance, *index).encode()), - [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => - Some((*id, *instance, *data).encode()), - [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), - [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), - _ => None, - } - } -} diff --git a/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs index 1d840e9c0dde..e90b86b04533 100644 --- a/polkadot/xcm/xcm-builder/src/location_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/location_conversion.rs @@ -110,6 +110,48 @@ impl DescribeLocation for DescribeBodyTerminal { } } +pub struct DescribePalletInstanceAndGeneralIndexTerminal; +impl DescribeLocation for DescribePalletInstanceAndGeneralIndexTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [PalletInstance(instance), GeneralIndex(index)]) => + Some((b"Pallet", b"GeneralIndex", *instance, *index).encode()), + _ => return None, + } + } +} + +pub struct DescribePalletInstanceAndGeneralKeyTerminal; +impl DescribeLocation for DescribePalletInstanceAndGeneralKeyTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [PalletInstance(instance), GeneralKey { data, .. }]) => + Some((b"Pallet", b"GeneralKey", *instance, *data).encode()), + _ => return None, + } + } +} + +pub struct DescribeGeneralKeyTerminal; +impl DescribeLocation for DescribeGeneralKeyTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [GeneralKey { data, .. }]) => Some((b"GeneralKey", *data).encode()), + _ => return None, + } + } +} + +pub struct DescribeGeneralIndexTerminal; +impl DescribeLocation for DescribeGeneralIndexTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, [GeneralIndex(index)]) => Some((b"GeneralIndex", *index).encode()), + _ => return None, + } + } +} + pub type DescribeAllTerminal = ( DescribeTerminus, DescribePalletTerminal, @@ -117,6 +159,10 @@ pub type DescribeAllTerminal = ( DescribeAccountKey20Terminal, DescribeTreasuryVoiceTerminal, DescribeBodyTerminal, + DescribePalletInstanceAndGeneralIndexTerminal, + DescribePalletInstanceAndGeneralKeyTerminal, + DescribeGeneralKeyTerminal, + DescribeGeneralIndexTerminal, ); pub struct DescribeFamily(PhantomData); From 2561fb667370410252e6d0c9af94822950de3e76 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 14:32:07 +0200 Subject: [PATCH 32/90] Revert "Support more foreign locations" This reverts commit 4090be1ea34de666029ccf44528231b799418223. --- bridges/snowbridge/primitives/core/src/lib.rs | 27 +++++++++-- .../xcm-builder/src/location_conversion.rs | 46 ------------------- 2 files changed, 23 insertions(+), 50 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ca5e7e39b8d7..c31124edf51d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -28,7 +28,9 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{GlobalConsensus, Junction::Parachain, Location}; +use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, +}; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract @@ -177,8 +179,7 @@ pub struct AssetMetadata { pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = - HashedDescription>>; +pub type TokenIdOf = HashedDescription>; pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { @@ -187,9 +188,27 @@ impl DescribeLocation for DescribeGlobalPrefix (1, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"PNA", network, interior).encode()) + Some((b"pna", network, interior).encode()) }, _ => None, } } } + +pub struct DescribeInner; +impl DescribeLocation for DescribeInner { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [Parachain(id)] => Some((*id).encode()), + [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), + [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => + Some((*id, *instance, *index).encode()), + [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => + Some((*id, *instance, *data).encode()), + [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), + [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + _ => None, + } + } +} diff --git a/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs index e90b86b04533..1d840e9c0dde 100644 --- a/polkadot/xcm/xcm-builder/src/location_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/location_conversion.rs @@ -110,48 +110,6 @@ impl DescribeLocation for DescribeBodyTerminal { } } -pub struct DescribePalletInstanceAndGeneralIndexTerminal; -impl DescribeLocation for DescribePalletInstanceAndGeneralIndexTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [PalletInstance(instance), GeneralIndex(index)]) => - Some((b"Pallet", b"GeneralIndex", *instance, *index).encode()), - _ => return None, - } - } -} - -pub struct DescribePalletInstanceAndGeneralKeyTerminal; -impl DescribeLocation for DescribePalletInstanceAndGeneralKeyTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [PalletInstance(instance), GeneralKey { data, .. }]) => - Some((b"Pallet", b"GeneralKey", *instance, *data).encode()), - _ => return None, - } - } -} - -pub struct DescribeGeneralKeyTerminal; -impl DescribeLocation for DescribeGeneralKeyTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [GeneralKey { data, .. }]) => Some((b"GeneralKey", *data).encode()), - _ => return None, - } - } -} - -pub struct DescribeGeneralIndexTerminal; -impl DescribeLocation for DescribeGeneralIndexTerminal { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, [GeneralIndex(index)]) => Some((b"GeneralIndex", *index).encode()), - _ => return None, - } - } -} - pub type DescribeAllTerminal = ( DescribeTerminus, DescribePalletTerminal, @@ -159,10 +117,6 @@ pub type DescribeAllTerminal = ( DescribeAccountKey20Terminal, DescribeTreasuryVoiceTerminal, DescribeBodyTerminal, - DescribePalletInstanceAndGeneralIndexTerminal, - DescribePalletInstanceAndGeneralKeyTerminal, - DescribeGeneralKeyTerminal, - DescribeGeneralIndexTerminal, ); pub struct DescribeFamily(PhantomData); From 06bc7af5d84cbebfd9e436528770a0fb5ae2d5d1 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 22:07:18 +0200 Subject: [PATCH 33/90] Use describe family and decribe Account terminals --- bridges/snowbridge/primitives/core/src/lib.rs | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index c31124edf51d..fd0d3bb51597 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -29,12 +29,12 @@ use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, Location, PalletInstance, + AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, + Location, PalletInstance, }; use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract -pub type AgentId = H256; pub use operating_mode::BasicOperatingMode; pub use pricing::{PricingParameters, Rewards}; @@ -153,21 +153,6 @@ pub const PRIMARY_GOVERNANCE_CHANNEL: ChannelId = pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId = ChannelId::new(hex!("0000000000000000000000000000000000000000000000000000000000000002")); -pub struct DescribeHere; -impl DescribeLocation for DescribeHere { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, []) => Some(Vec::::new().encode()), - _ => None, - } - } -} - -/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on -/// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = - HashedDescription)>; - /// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { @@ -176,38 +161,63 @@ pub struct AssetMetadata { pub decimals: u8, } +pub type AgentId = H256; + +/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on +/// Ethereum which acts as the sovereign account for the Location. +pub type AgentIdOf = + HashedDescription)>; + pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = HashedDescription>; +pub type TokenIdOf = + HashedDescription>>; +pub struct DescribeHere; +impl DescribeLocation for DescribeHere { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, []) => Some(Vec::::new().encode()), + _ => None, + } + } +} pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); impl DescribeLocation for DescribeGlobalPrefix { fn describe_location(l: &Location) -> Option> { match (l.parent_count(), l.first_interior()) { - (1, Some(GlobalConsensus(network))) => { + (_, Some(GlobalConsensus(network))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; - Some((b"pna", network, interior).encode()) + Some((b"PNA", network, interior).encode()) }, _ => None, } } } -pub struct DescribeInner; -impl DescribeLocation for DescribeInner { +pub struct DescribeToken; +impl DescribeLocation for DescribeToken { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [Parachain(id)] => Some((*id).encode()), - [Parachain(id), PalletInstance(instance)] => Some((*id, *instance).encode()), - [Parachain(id), PalletInstance(instance), GeneralIndex(index)] => - Some((*id, *instance, *index).encode()), - [Parachain(id), PalletInstance(instance), GeneralKey { data, .. }] => - Some((*id, *instance, *data).encode()), - [Parachain(id), GeneralIndex(index)] => Some((*id, *index).encode()), - [Parachain(id), GeneralKey { data, .. }] => Some((*id, *data).encode()), + [GeneralIndex(index)] => Some((*index).encode()), + [GeneralKey { data, .. }] => Some((*data).encode()), + [AccountKey20 { key, .. }] => Some((*key).encode()), + [AccountId32 { id, .. }] => Some((*id).encode()), + + // Pallet + [PalletInstance(instance)] => Some((*instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((*instance, *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((*instance, *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + + // Reject all other locations _ => None, } } From 8092a1f4506d06d25176b81532ce38fefa145417 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 22:54:17 +0200 Subject: [PATCH 34/90] separate to new file --- bridges/snowbridge/primitives/core/src/lib.rs | 70 +-------- .../primitives/core/src/location.rs | 138 ++++++++++++++++++ .../snowbridge/primitives/core/src/tests.rs | 41 +----- 3 files changed, 142 insertions(+), 107 deletions(-) create mode 100644 bridges/snowbridge/primitives/core/src/location.rs diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index fd0d3bb51597..01ad86a26018 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -9,11 +9,13 @@ mod tests; pub mod inbound; +pub mod location; pub mod operating_mode; pub mod outbound; pub mod pricing; pub mod ringbuffer; +pub use location::{AgentId, AgentIdOf, TokenId, TokenIdOf}; pub use polkadot_parachain_primitives::primitives::{ Id as ParaId, IsSystem, Sibling as SiblingParaId, }; @@ -28,11 +30,7 @@ use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; -use xcm::prelude::{ - AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Junction::Parachain, - Location, PalletInstance, -}; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; +use xcm::prelude::{Junction::Parachain, Location}; /// The ID of an agent contract pub use operating_mode::BasicOperatingMode; @@ -160,65 +158,3 @@ pub struct AssetMetadata { pub symbol: BoundedVec>, pub decimals: u8, } - -pub type AgentId = H256; - -/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on -/// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = - HashedDescription)>; - -pub type TokenId = H256; - -/// Convert a token location to a stable ID that can be used on the Ethereum side -pub type TokenIdOf = - HashedDescription>>; - -pub struct DescribeHere; -impl DescribeLocation for DescribeHere { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, []) => Some(Vec::::new().encode()), - _ => None, - } - } -} -pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); -impl DescribeLocation for DescribeGlobalPrefix { - fn describe_location(l: &Location) -> Option> { - match (l.parent_count(), l.first_interior()) { - (_, Some(GlobalConsensus(network))) => { - let tail = l.clone().split_first_interior().0; - let interior = Suffix::describe_location(&tail.into())?; - Some((b"PNA", network, interior).encode()) - }, - _ => None, - } - } -} - -pub struct DescribeToken; -impl DescribeLocation for DescribeToken { - fn describe_location(l: &Location) -> Option> { - match l.unpack().1 { - [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((*index).encode()), - [GeneralKey { data, .. }] => Some((*data).encode()), - [AccountKey20 { key, .. }] => Some((*key).encode()), - [AccountId32 { id, .. }] => Some((*id).encode()), - - // Pallet - [PalletInstance(instance)] => Some((*instance).encode()), - [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), - [PalletInstance(instance), GeneralKey { data, .. }] => - Some((*instance, *data).encode()), - - [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((*instance, *key).encode()), - [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), - - // Reject all other locations - _ => None, - } - } -} diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs new file mode 100644 index 000000000000..28952cd29713 --- /dev/null +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! # Location +//! +//! Location helpers for dealing with Tokens and Agents + +pub use polkadot_parachain_primitives::primitives::{ + Id as ParaId, IsSystem, Sibling as SiblingParaId, +}; +pub use sp_core::U256; + +use codec::Encode; +use sp_core::H256; +use sp_std::prelude::*; +use xcm::prelude::{ + AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, +}; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; + +pub type AgentId = H256; + +/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on +/// Ethereum which acts as the sovereign account for the Location. +pub type AgentIdOf = + HashedDescription)>; + +pub type TokenId = H256; + +/// Convert a token location to a stable ID that can be used on the Ethereum side +pub type TokenIdOf = HashedDescription< + TokenId, + DescribeGlobalPrefix<(DescribeHere, DescribeFamily)>, +>; + +pub struct DescribeHere; +impl DescribeLocation for DescribeHere { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, []) => Some(Vec::::new().encode()), + _ => None, + } + } +} +pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); +impl DescribeLocation for DescribeGlobalPrefix { + fn describe_location(l: &Location) -> Option> { + match (l.parent_count(), l.first_interior()) { + (_, Some(GlobalConsensus(network))) => { + let mut tail = l.clone().split_first_interior().0; + tail.dec_parent(); + let interior = Suffix::describe_location(&tail)?; + Some((b"PNA", network, interior).encode()) + }, + _ => None, + } + } +} + +pub struct DescribeTokenTerminal; +impl DescribeLocation for DescribeTokenTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [GeneralIndex(index)] => Some((*index).encode()), + [GeneralKey { data, .. }] => Some((*data).encode()), + [AccountKey20 { key, .. }] => Some((*key).encode()), + [AccountId32 { id, .. }] => Some((*id).encode()), + + // Pallet + [PalletInstance(instance)] => Some((*instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((*instance, *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((*instance, *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + + // Reject all other locations + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use crate::TokenIdOf; + use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Kusama, Location, PalletInstance, Parachain, + Westend, + }; + use xcm_executor::traits::ConvertLocation; + + #[test] + fn test_token_of_id() { + let token_locations = [ + // Relay Chain cases + // Relay Chain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend)]), + // Relay Chain relative to another polkadot chain. + Location::new(2, [GlobalConsensus(Kusama)]), + // Parachain cases + // Parachain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), + // Parachain relative to another polkadot chain. + Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + // Parachain general index + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + // Parchain Pallet instance cases + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), + // Parachain General Key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + GeneralKey { length: 32, data: [1; 32] }, + ], + ), + ]; + + for token in token_locations { + assert!( + TokenIdOf::convert_location(&token).is_some(), + "Valid token = {token:?} yeilds no TokenId." + ); + } + + let non_token_locations = [Location::new(1, [])]; + + for token in non_token_locations { + assert!( + TokenIdOf::convert_location(&token).is_none(), + "Invalid token = {token:?} yeilds a TokenId." + ); + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/tests.rs b/bridges/snowbridge/primitives/core/src/tests.rs index 3667ff7f03f9..725fff1a9c94 100644 --- a/bridges/snowbridge/primitives/core/src/tests.rs +++ b/bridges/snowbridge/primitives/core/src/tests.rs @@ -1,9 +1,5 @@ -use crate::{ChannelId, ParaId, TokenIdOf}; +use crate::{ChannelId, ParaId}; use hex_literal::hex; -use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, Parachain, Westend, -}; -use xcm_executor::traits::ConvertLocation; const EXPECT_CHANNEL_ID: [u8; 32] = hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539"); @@ -15,38 +11,3 @@ fn generate_channel_id() { let channel_id: ChannelId = para_id.into(); assert_eq!(channel_id, EXPECT_CHANNEL_ID.into()); } - -#[test] -fn test_describe_relay_token() { - let asset_location: Location = Location::new(1, [GlobalConsensus(Westend)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_primary_token_from_parachain() { - let asset_location: Location = Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_pallet_instance_prefix() { - let asset_location: Location = - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_general_index_prefix() { - let asset_location: Location = - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} - -#[test] -fn test_describe_token_with_general_key_prefix() { - let asset_location: Location = Location::new( - 1, - [GlobalConsensus(Westend), Parachain(2000), GeneralKey { length: 32, data: [1; 32] }], - ); - assert_eq!(TokenIdOf::convert_location(&asset_location).is_some(), true); -} From 0ae39b95a56a6ba6825b4a55cd344d885c0e6a0b Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 4 Sep 2024 23:18:58 +0200 Subject: [PATCH 35/90] all cases added --- .../primitives/core/src/location.rs | 104 +++++++++++++++--- 1 file changed, 89 insertions(+), 15 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 28952cd29713..17c5ada75684 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -49,7 +49,7 @@ impl DescribeLocation for DescribeGlobalPrefix let mut tail = l.clone().split_first_interior().0; tail.dec_parent(); let interior = Suffix::describe_location(&tail)?; - Some((b"PNA", network, interior).encode()) + Some((b"GlobalConsensus", network, interior).encode()) }, _ => None, } @@ -61,20 +61,22 @@ impl DescribeLocation for DescribeTokenTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((*index).encode()), - [GeneralKey { data, .. }] => Some((*data).encode()), - [AccountKey20 { key, .. }] => Some((*key).encode()), - [AccountId32 { id, .. }] => Some((*id).encode()), + [GeneralIndex(index)] => Some((b"Index", *index).encode()), + [GeneralKey { data, .. }] => Some((b"Key", *data).encode()), + [AccountKey20 { key, .. }] => Some((b"AccountKey", *key).encode()), + [AccountId32 { id, .. }] => Some((b"AccountId", *id).encode()), // Pallet - [PalletInstance(instance)] => Some((*instance).encode()), - [PalletInstance(instance), GeneralIndex(index)] => Some((*instance, *index).encode()), + [PalletInstance(instance)] => Some((b"Pallet", *instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => + Some((b"Pallet", *instance, "Index", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => - Some((*instance, *data).encode()), + Some((b"Pallet", *instance, b"Key", *data).encode()), [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((*instance, *key).encode()), - [PalletInstance(instance), AccountId32 { id, .. }] => Some((*instance, *id).encode()), + Some((b"Pallet", *instance, b"AccountKey", *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => + Some((b"Pallet", *instance, b"AccountId", *id).encode()), // Reject all other locations _ => None, @@ -86,8 +88,8 @@ impl DescribeLocation for DescribeTokenTerminal { mod tests { use crate::TokenIdOf; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Kusama, Location, PalletInstance, Parachain, - Westend, + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, }; use xcm_executor::traits::ConvertLocation; @@ -106,15 +108,82 @@ mod tests { Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), // Parachain general index Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + // Parachain general key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountId32 { network: None, id: [0; 32] }, + ], + ), // Parchain Pallet instance cases + // Parachain pallet instance Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), - // Parachain General Key + // Parachain Pallet general index + Location::new( + 1, + [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8), GeneralIndex(1)], + ), + // Parachain Pallet general key Location::new( 1, [ GlobalConsensus(Westend), Parachain(2000), - GeneralKey { length: 32, data: [1; 32] }, + PalletInstance(8), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain Pallet account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain Pallet account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountId32 { network: None, id: [0; 32] }, + ], + ), + // Ethereum cases + // Ethereum location relative to Polkadot + Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]), + // Ethereum location relative to Ethereum + Location::new(1, [GlobalConsensus(Ethereum { chain_id: 2 })]), + // Ethereum ERC20 location relative to Polkadot + Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 1 }), + AccountKey20 { network: None, key: [0; 20] }, ], ), ]; @@ -126,7 +195,12 @@ mod tests { ); } - let non_token_locations = [Location::new(1, [])]; + let non_token_locations = [ + // Relative location for a token should fail. + Location::new(1, []), + // Relative location for a token should fail. + Location::new(1, [Parachain(1000)]), + ]; for token in non_token_locations { assert!( From 3da4cbb031155436ff2505e0eedca18cf01098c0 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 08:32:03 +0800 Subject: [PATCH 36/90] Use full junction name --- .../snowbridge/primitives/core/src/location.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 17c5ada75684..14776452ff16 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -61,22 +61,22 @@ impl DescribeLocation for DescribeTokenTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack().1 { [] => Some(Vec::::new().encode()), - [GeneralIndex(index)] => Some((b"Index", *index).encode()), - [GeneralKey { data, .. }] => Some((b"Key", *data).encode()), - [AccountKey20 { key, .. }] => Some((b"AccountKey", *key).encode()), - [AccountId32 { id, .. }] => Some((b"AccountId", *id).encode()), + [GeneralIndex(index)] => Some((b"GeneralIndex", *index).encode()), + [GeneralKey { data, .. }] => Some((b"GeneralKey", *data).encode()), + [AccountKey20 { key, .. }] => Some((b"AccountKey20", *key).encode()), + [AccountId32 { id, .. }] => Some((b"AccountId32", *id).encode()), // Pallet - [PalletInstance(instance)] => Some((b"Pallet", *instance).encode()), + [PalletInstance(instance)] => Some((b"PalletInstance", *instance).encode()), [PalletInstance(instance), GeneralIndex(index)] => - Some((b"Pallet", *instance, "Index", *index).encode()), + Some((b"PalletInstance", *instance, "GeneralIndex", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => - Some((b"Pallet", *instance, b"Key", *data).encode()), + Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), [PalletInstance(instance), AccountKey20 { key, .. }] => - Some((b"Pallet", *instance, b"AccountKey", *key).encode()), + Some((b"PalletInstance", *instance, b"AccountKey20", *key).encode()), [PalletInstance(instance), AccountId32 { id, .. }] => - Some((b"Pallet", *instance, b"AccountId", *id).encode()), + Some((b"PalletInstance", *instance, b"AccountId32", *id).encode()), // Reject all other locations _ => None, From 991de7c153bb2792b42ab1f7a825658aaf5801c3 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 10:19:22 +0800 Subject: [PATCH 37/90] Add SetFeesMode --- .../snowbridge/primitives/router/src/inbound/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 3e1ddb1ffc6a..85d3091543b2 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -394,7 +394,7 @@ impl< asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -431,15 +431,19 @@ impl< WithdrawAsset(asset.clone().into()), ]; + let bridge_location = Location::new(2, GlobalConsensus(network)); + match dest_para_id { Some(dest_para_id) => { let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ + // `SetFeesMode` to pay transport fee from bridge sovereign + SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: Location::new(1, [Parachain(dest_para_id)]), + assets: AllCounted(2).into(), + beneficiary: bridge_location, }])), // Perform a reserve withdraw to send to destination chain. Leave half of the // asset_hub_fee for the delivery cost @@ -451,7 +455,7 @@ impl< xcm: vec![ // Buy execution on target. BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit asset to beneficiary. + // Deposit asset and fee to beneficiary. DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, // Forward message id to destination parachain. SetTopic(message_id.into()), From 77cca934b5fe18f3f51e752e750a9badc01be0a6 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 16:29:29 +0800 Subject: [PATCH 38/90] Reanchor test --- .../primitives/core/src/location.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 14776452ff16..4d29ef07733d 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -87,9 +87,10 @@ impl DescribeLocation for DescribeTokenTerminal { #[cfg(test)] mod tests { use crate::TokenIdOf; + use frame_support::assert_ok; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, - PalletInstance, Parachain, + GeneralIndex, GeneralKey, GlobalConsensus, InteriorLocation, Junction::*, Location, + NetworkId::*, PalletInstance, Parachain, Reanchorable, }; use xcm_executor::traits::ConvertLocation; @@ -209,4 +210,22 @@ mod tests { ); } } + + #[test] + fn test_reanchor_relay_token_from_different_consensus() { + let asset_id: Location = Location::new(2, [GlobalConsensus(Rococo)]); + let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); + let mut reanchored_asset = asset_id.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_eq!( + reanchored_asset, + Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } + ); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); + let mut reanchored_asset = reanchored_asset.clone(); + assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); + assert_eq!(reanchored_asset, asset_id); + } } From ff870fe24fbeeda86641a361e4833e07bd889cd7 Mon Sep 17 00:00:00 2001 From: Ron Date: Thu, 5 Sep 2024 17:44:53 +0800 Subject: [PATCH 39/90] Update bridges/snowbridge/pallets/inbound-queue/src/lib.rs Co-authored-by: Francisco Aguirre --- bridges/snowbridge/pallets/inbound-queue/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 676cd8f3a373..423b92b9fae0 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -275,7 +275,7 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } - // Decode payload into VersionMessage + // Decode payload into `VersionedMessage` let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; From f3127cbd71ab6b47a72c51d33422710d5367c29f Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 17:49:42 +0800 Subject: [PATCH 40/90] Change ParaId to 1002 --- bridges/snowbridge/pallets/inbound-queue/src/mock.rs | 2 +- bridges/snowbridge/primitives/core/src/location.rs | 2 +- bridges/snowbridge/primitives/router/src/inbound/tests.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index e639dd8771f6..f5e44c2e46a8 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -113,7 +113,7 @@ parameter_types! { pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; pub UniversalLocation: InteriorLocation = - [GlobalConsensus(Westend), Parachain(1013)].into(); + [GlobalConsensus(Westend), Parachain(1002)].into(); pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 4d29ef07733d..ffdab463101f 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -222,7 +222,7 @@ mod tests { reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 282a16443fc8..ea1197cc38ea 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -50,7 +50,7 @@ fn test_reanchor_relay_token() { reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); @@ -78,7 +78,7 @@ fn test_reanchor_pna_from_ah() { .into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); From 34c655ba7bf8192b417f76e19be3d7d2435a3fcf Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 18:14:26 +0800 Subject: [PATCH 41/90] Fix parent count allow 1 only --- .../primitives/core/src/location.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index ffdab463101f..e47200ddd5c9 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -45,7 +45,7 @@ pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData DescribeLocation for DescribeGlobalPrefix { fn describe_location(l: &Location) -> Option> { match (l.parent_count(), l.first_interior()) { - (_, Some(GlobalConsensus(network))) => { + (1, Some(GlobalConsensus(network))) => { let mut tail = l.clone().split_first_interior().0; tail.dec_parent(); let interior = Suffix::describe_location(&tail)?; @@ -100,13 +100,9 @@ mod tests { // Relay Chain cases // Relay Chain relative to Ethereum Location::new(1, [GlobalConsensus(Westend)]), - // Relay Chain relative to another polkadot chain. - Location::new(2, [GlobalConsensus(Kusama)]), // Parachain cases // Parachain relative to Ethereum Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), - // Parachain relative to another polkadot chain. - Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), // Parachain general index Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), // Parachain general key @@ -174,19 +170,6 @@ mod tests { AccountId32 { network: None, id: [0; 32] }, ], ), - // Ethereum cases - // Ethereum location relative to Polkadot - Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]), - // Ethereum location relative to Ethereum - Location::new(1, [GlobalConsensus(Ethereum { chain_id: 2 })]), - // Ethereum ERC20 location relative to Polkadot - Location::new( - 2, - [ - GlobalConsensus(Ethereum { chain_id: 1 }), - AccountKey20 { network: None, key: [0; 20] }, - ], - ), ]; for token in token_locations { From a01e9fc5adbdb15b07236bc789319276e8d62da9 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 5 Sep 2024 12:25:43 +0200 Subject: [PATCH 42/90] deprecate DescribeHere --- bridges/snowbridge/primitives/core/src/location.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index e47200ddd5c9..ee91432b3184 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -15,12 +15,15 @@ use sp_std::prelude::*; use xcm::prelude::{ AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, }; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; +use xcm_builder::{ + DescribeAllTerminal, DescribeFamily, DescribeLocation, DescribeTerminus, HashedDescription, +}; pub type AgentId = H256; /// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on /// Ethereum which acts as the sovereign account for the Location. +#[allow(deprecated)] pub type AgentIdOf = HashedDescription)>; @@ -29,10 +32,16 @@ pub type TokenId = H256; /// Convert a token location to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription< TokenId, - DescribeGlobalPrefix<(DescribeHere, DescribeFamily)>, + DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, >; +/// This looks like DescribeTerminus that was added to xcm-builder. However this does an extra +/// `encode` to the Vector producing a different output to DescribeTerminus. `DescribeHere` +/// should NOT be used for new code. This is left here for backwards compatibility of channels and +/// agents. +#[deprecated(note = "Use DescribeTerminus from xcm-builder instead.")] pub struct DescribeHere; +#[allow(deprecated)] impl DescribeLocation for DescribeHere { fn describe_location(l: &Location) -> Option> { match l.unpack() { From d5b9b226477dcce85261f34e5bf6bfa67913619a Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 5 Sep 2024 18:36:24 +0800 Subject: [PATCH 43/90] Improve comments --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 85d3091543b2..7935de40ac3d 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -438,7 +438,9 @@ impl< let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // `SetFeesMode` to pay transport fee from bridge sovereign + // `SetFeesMode` to pay transport fee from bridge sovereign, which depends on + // unspent AH fees deposited to the bridge sovereign, + // more context and analysis in https://github.com/paritytech/polkadot-sdk/pull/5546#discussion_r1744682864 SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { From 435138967b1d9a94bae4062aefa00df10001af19 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 5 Sep 2024 15:29:38 +0200 Subject: [PATCH 44/90] make register token relative --- bridges/snowbridge/pallets/system/src/lib.rs | 13 +- bridges/snowbridge/pallets/system/src/mock.rs | 6 +- .../snowbridge/pallets/system/src/tests.rs | 176 +++++++++++++++++- .../src/tests/snowbridge.rs | 8 +- .../src/bridge_to_ethereum_config.rs | 2 + .../src/bridge_to_ethereum_config.rs | 2 + 6 files changed, 200 insertions(+), 7 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 051e15c387ad..c0be7dcb6e03 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -173,6 +173,12 @@ pub mod pallet { type WeightInfo: WeightInfo; + /// This chain's Universal Location. + type UniversalLocation: Get; + + // The bridges configured Ethereum network with chain id. + type EthereumNetwork: Get; + #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -242,7 +248,6 @@ pub mod pallet { InvalidTokenTransferFees, InvalidPricingParameters, InvalidUpgradeParameters, - TokenExists, } /// The set of registered agents @@ -723,6 +728,12 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { + let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let mut location = location.clone(); + location + .reanchor(&bridge_location, &T::UniversalLocation::get()) + .map_err(|_| Error::::LocationConversionFailed)?; + // Record the token id or fail if it has already been created let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27..d8c905449d9a 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -170,6 +170,7 @@ parameter_types! { pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; } pub const DOT: u128 = 10_000_000_000; @@ -177,8 +178,8 @@ pub const DOT: u128 = 10_000_000_000; parameter_types! { pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); pub Fee: u64 = 1000; - pub const RococoNetwork: NetworkId = NetworkId::Rococo; pub const InitialFunding: u128 = 1_000_000_000_000; + pub BridgeHubParaId: ParaId = ParaId::new(1002); pub AssetHubParaId: ParaId = ParaId::new(1000); pub TestParaId: u32 = 2000; pub Parameters: PricingParameters = PricingParameters { @@ -188,7 +189,6 @@ parameter_types! { multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; - } #[cfg(feature = "runtime-benchmarks")] @@ -208,6 +208,8 @@ impl crate::Config for Test { type DefaultPricingParameters = Parameters; type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index ba016b9b4854..0745f435ba05 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -549,7 +549,7 @@ fn charge_fee_for_create_agent() { assert_ok!(EthereumSystem::create_agent(origin.clone())); let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); // assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee) let message = Message { @@ -631,3 +631,177 @@ fn no_genesis_build_is_uninitialized() { assert!(!EthereumSystem::is_initialized(), "Ethereum initialized."); }); } + +#[test] +fn register_token_with_root_yeilds_success() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(1, []); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_complex_location_simplifies_and_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Kusama)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); + let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_doubled_bridged_polkadot_location_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Rococo)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id = + hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(); + let expected_location = Location::new(1, [GlobalConsensus(Rococo)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + Error::::LocationConversionFailed + ); + }); +} + +#[test] +fn register_token_with_double_bridged_ethereum_address_succeeds() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + const NETWORK: NetworkId = Ethereum { chain_id: 1 }; + let location = Location::new(2, [GlobalConsensus(NETWORK)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let expected_token_id: H256 = + hex!("37fd94739deb1c2a8929b45a4f70ffcb52de8b54791609ee13ee0a2b33730269").into(); + let expected_location = Location::new(1, [GlobalConsensus(NETWORK)]); + + System::assert_last_event(RuntimeEvent::EthereumSystem( + crate::Event::::RegisterToken { + location: expected_location.into(), + foreign_token_id: expected_token_id, + }, + )); + }); +} + +#[test] +fn register_token_with_signed_yeilds_bad_origin() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 10, + name: b"Dot".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + BadOrigin + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 996ab2657329..abcc38c16ab3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -295,9 +295,11 @@ fn transfer_relay_token() { ); BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); - let asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + let asset_id: Location = Location { parents: 1, interior: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; - let token_id = TokenIdOf::convert_location(&asset_id).unwrap(); + let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); let ethereum_sovereign: AccountId = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( @@ -389,7 +391,7 @@ fn transfer_relay_token() { let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendNativeToken { - token_id, + token_id: expected_token_id, destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, amount: TOKEN_AMOUNT, fee: XCM_FEE, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 26857d262c20..1e096f19ef80 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 621d39af5af0..fee9f9a2f610 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -188,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumNetwork = EthereumNetwork; } #[cfg(feature = "runtime-benchmarks")] From 3191ea93888e97f175ef4821f908ab22fa755b41 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 6 Sep 2024 00:41:09 +0800 Subject: [PATCH 45/90] Update bridges/snowbridge/primitives/core/src/location.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/primitives/core/src/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index ee91432b3184..c126618bba35 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -78,7 +78,7 @@ impl DescribeLocation for DescribeTokenTerminal { // Pallet [PalletInstance(instance)] => Some((b"PalletInstance", *instance).encode()), [PalletInstance(instance), GeneralIndex(index)] => - Some((b"PalletInstance", *instance, "GeneralIndex", *index).encode()), + Some((b"PalletInstance", *instance, b"GeneralIndex", *index).encode()), [PalletInstance(instance), GeneralKey { data, .. }] => Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), From 57b91712e4f5c3ab8d2a4acc90f468149fb2a624 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 6 Sep 2024 00:50:15 +0800 Subject: [PATCH 46/90] Update bridges/snowbridge/pallets/system/src/lib.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/pallets/system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c0be7dcb6e03..94fb80066960 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -613,7 +613,7 @@ pub mod pallet { /// Fee required: No /// /// - `origin`: Must be root - /// - `location`: Location of the asset + /// - `location`: Location of the asset (relative to this chain) /// - `metadata`: Metadata to include in the instantiated ERC20 contract on Ethereum #[pallet::call_index(10)] #[pallet::weight(T::WeightInfo::register_token())] From 0abc9ca6a53c4b56d5bf7a9f38811fe721fb7b5e Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 01:16:18 +0800 Subject: [PATCH 47/90] Tx fee waived for root --- bridges/snowbridge/pallets/system/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index c0be7dcb6e03..bcff080d2cda 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -136,6 +136,7 @@ where #[frame_support::pallet] pub mod pallet { + use frame_support::dispatch::PostDispatchInfo; use snowbridge_core::StaticLookup; use sp_core::U256; @@ -621,7 +622,7 @@ pub mod pallet { origin: OriginFor, location: Box, metadata: AssetMetadata, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let location: Location = @@ -631,7 +632,10 @@ pub mod pallet { Self::do_register_token(&location, metadata, pays_fee)?; - Ok(()) + Ok(PostDispatchInfo { + actual_weight: Some(T::WeightInfo::register_token()), + pays_fee: Pays::No, + }) } } From 41dc959371f45b8e25a424a7c58c17fb2a2091d1 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 01:19:06 +0800 Subject: [PATCH 48/90] Fix benchmark --- bridges/snowbridge/pallets/system/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 4314c7528af6..20798b7c3493 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -168,7 +168,7 @@ mod benchmarks { T::Token::mint_into(&caller, amount)?; - let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]); + let relay_token_asset_id: Location = Location::parent(); let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); let asset_metadata = AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), From b7cf50747732757b1ca0bc202a7d04e2094b284a Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 09:09:47 +0800 Subject: [PATCH 49/90] Revamp test to use relative location --- .../primitives/router/src/inbound/tests.rs | 21 +++++----- .../src/tests/snowbridge.rs | 39 ++++++++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index ea1197cc38ea..7b4469c1a68c 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -59,12 +59,16 @@ fn test_reanchor_relay_token() { #[test] fn test_reanchor_pna_from_ah() { - let asset_id: Location = + let asset_id_in_ah: Location = Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let asset_id: Location = Location { + parents: 1, + interior: [Parachain(1000), PalletInstance(50), GeneralIndex(2)].into(), + }; + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); assert_eq!( reanchored_asset, Location { @@ -78,27 +82,26 @@ fn test_reanchor_pna_from_ah() { .into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); + assert_eq!(reanchored_asset, asset_id_in_ah); } #[test] fn test_reanchor_pna_from_para() { + let asset_id_in_ah: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); assert_eq!( reanchored_asset, Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1013)].into(); let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); let mut reanchored_asset = reanchored_asset.clone(); assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); + assert_eq!(reanchored_asset, asset_id_in_ah); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index abcc38c16ab3..389b3ec33427 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -458,6 +458,15 @@ fn transfer_ah_token() { let asset_id: Location = [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + let asset_id_in_bh: Location = Location::new( + 1, + [ + Parachain(AssetHubWestend::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(RESERVABLE_ASSET_ID.into()), + ], + ); + let asset_id_after_reanchored = Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) .appended_with(asset_id.clone().interior) @@ -467,12 +476,17 @@ fn transfer_ah_token() { // Register token BridgeHubWestend::execute_with(|| { - type Runtime = ::Runtime; + type RuntimeOrigin = ::RuntimeOrigin; - snowbridge_pallet_system::ForeignToNativeId::::insert( - token_id, - asset_id_after_reanchored.clone(), - ); + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id_in_bh.clone())), + AssetMetadata { + name: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); }); // Mint some token @@ -637,12 +651,17 @@ fn transfer_penpal_native_token() { // Create token BridgeHubWestend::execute_with(|| { - type Runtime = ::Runtime; + type RuntimeOrigin = ::RuntimeOrigin; - snowbridge_pallet_system::ForeignToNativeId::::insert( - token_id, - penpal_asset_location_after_reanchored.clone(), - ); + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(penpal_asset_location.clone())), + AssetMetadata { + name: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); }); // Send token to Ethereum From 5fdc0369758989a7d05c95de76183d63240fdaec Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 6 Sep 2024 14:28:44 +0200 Subject: [PATCH 50/90] do not allow changes to token_id locations once set --- bridges/snowbridge/pallets/system/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e9a6bb6361..092eb1addb47 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -733,17 +733,18 @@ pub mod pallet { pays_fee: PaysFee, ) -> Result<(), DispatchError> { let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); - let mut location = location.clone(); - location - .reanchor(&bridge_location, &T::UniversalLocation::get()) + let location = location + .clone() + .reanchored(&bridge_location, &T::UniversalLocation::get()) .map_err(|_| Error::::LocationConversionFailed)?; - // Record the token id or fail if it has already been created let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - ForeignToNativeId::::insert(token_id, location.clone()); - NativeToForeignId::::insert(location.clone(), token_id); + if (!ForeignToNativeId::::contains_key(token_id)) { + ForeignToNativeId::::insert(token_id, location.clone()); + NativeToForeignId::::insert(location.clone(), token_id); + } let command = Command::RegisterForeignToken { token_id, From b395eafc4aa53042606fbda6fb7a7ba4e287fbb8 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 6 Sep 2024 14:31:42 +0200 Subject: [PATCH 51/90] fix syntax --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 092eb1addb47..d935bb78579e 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -741,9 +741,9 @@ pub mod pallet { let token_id = TokenIdOf::convert_location(&location) .ok_or(Error::::LocationConversionFailed)?; - if (!ForeignToNativeId::::contains_key(token_id)) { - ForeignToNativeId::::insert(token_id, location.clone()); + if !ForeignToNativeId::::contains_key(token_id) { NativeToForeignId::::insert(location.clone(), token_id); + ForeignToNativeId::::insert(token_id, location.clone()); } let command = Command::RegisterForeignToken { From c0ce942513d27e15f4f084cb4f02bf718502c0ed Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 6 Sep 2024 22:25:17 +0800 Subject: [PATCH 52/90] Use Blake2_* hasher instead --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 16e9a6bb6361..f2b8cce1def2 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -269,12 +269,12 @@ pub mod pallet { /// Lookup table for foreign to native token ID #[pallet::storage] pub type ForeignToNativeId = - StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>; + StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; /// Lookup table for native to foreign token ID #[pallet::storage] pub type NativeToForeignId = - StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>; + StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] From c94da518d0165a222d978925784ddea3b0c2fa19 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 08:49:32 +0800 Subject: [PATCH 53/90] Fix to reanchor in context of Ethereum --- .../primitives/router/src/inbound/tests.rs | 95 ++++++------------- .../src/bridge_to_ethereum_config.rs | 5 +- .../src/bridge_to_ethereum_config.rs | 6 +- 3 files changed, 37 insertions(+), 69 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 7b4469c1a68c..1c018f3ab0c4 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -40,68 +40,35 @@ fn test_contract_location_with_incorrect_location_fails_convert() { } #[test] -fn test_reanchor_relay_token() { - let asset_id: Location = Location::parent(); - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Westend)].into() } - ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); -} - -#[test] -fn test_reanchor_pna_from_ah() { - let asset_id_in_ah: Location = - Location { parents: 0, interior: [PalletInstance(50), GeneralIndex(2)].into() }; - let asset_id: Location = Location { - parents: 1, - interior: [Parachain(1000), PalletInstance(50), GeneralIndex(2)].into(), - }; - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); - assert_eq!( - reanchored_asset, - Location { - parents: 1, - interior: [ - GlobalConsensus(Westend), - Parachain(1000), - PalletInstance(50), - GeneralIndex(2) - ] - .into() - } - ); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id_in_ah); -} - -#[test] -fn test_reanchor_pna_from_para() { - let asset_id_in_ah: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let asset_id: Location = Location { parents: 1, interior: [Parachain(2000)].into() }; - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &bh_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Westend), Parachain(2000)].into() } - ); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id_in_ah); +fn test_reanchor_all_assets() { + let ethereum_context: InteriorLocation = [GlobalConsensus(Ethereum { chain_id: 1 })].into(); + let ethereum = Location::new(2, ethereum_context.clone()); + let ah_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); + let global_ah = Location::new(1, ah_context.clone()); + let bh_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1002)].into(); + let assets = vec![ + // DOT + Location::new(1, []), + // GLMR (Some Polkadot parachain currency) + Location::new(1, [Parachain(2004)]), + // AH asset + Location::new(0, [PalletInstance(50), GeneralIndex(42)]), + // KSM + Location::new(2, [GlobalConsensus(Kusama)]), + // KAR (Some Kusama parachain currency) + Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + ]; + for asset in assets.iter() { + // reanchor logic in pallet_xcm on AH + let mut reanchored_asset = asset.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + // reanchor back to original location in context of Ethereum + let mut reanchored_asset_with_ethereum_context = reanchored_asset.clone(); + assert_ok!(reanchored_asset_with_ethereum_context.reanchor(&global_ah, ðereum_context)); + assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); + // reanchor back to original location in context of BH + let mut reanchored_asset_with_bh_context = reanchored_asset.clone(); + assert_ok!(reanchored_asset_with_bh_context.reanchor(&global_ah, &bh_context)); + assert_eq!(reanchored_asset_with_bh_context, asset.clone()); + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 1e096f19ef80..fde214ed42d6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -41,7 +41,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; -use xcm::prelude::{GlobalConsensus, Location, Parachain}; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -67,6 +67,7 @@ parameter_types! { multiplier: FixedU128::from_rational(1, 1), }; pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -88,7 +89,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { AccountId, Balance, EthereumSystem, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHub, >; type WeightToFee = WeightToFee; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index fee9f9a2f610..4b3f61a42ad4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -42,7 +42,7 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; -use xcm::prelude::{GlobalConsensus, Location, Parachain}; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; @@ -70,8 +70,8 @@ parameter_types! { multiplier: FixedU128::from_rational(1, 1), }; pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } - impl snowbridge_pallet_inbound_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; @@ -91,7 +91,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { AccountId, Balance, EthereumSystem, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHub, >; type WeightToFee = WeightToFee; From 176c7af58f73c8e78c004d35e7a30f3a9faa1d5f Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 09:38:42 +0800 Subject: [PATCH 54/90] Update bridges/snowbridge/pallets/system/src/tests.rs Co-authored-by: Francisco Aguirre --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 0745f435ba05..d7493e02acb2 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,7 +633,7 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_root_yeilds_success() { +fn register_token_with_root_yields_success() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); let location = Location::new(1, []); From 0944d8a034404372e990edf6d29b62bf7fe5a772 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 09:46:00 +0800 Subject: [PATCH 55/90] Use Polkadot as RelayNetwork in tests --- bridges/snowbridge/pallets/system/src/mock.rs | 2 +- bridges/snowbridge/pallets/system/src/tests.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index d8c905449d9a..72605ea2283e 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -166,7 +166,7 @@ impl snowbridge_pallet_outbound_queue::Config for Test { parameter_types! { pub const SS58Prefix: u8 = 42; pub const AnyNetwork: Option = None; - pub const RelayNetwork: Option = Some(NetworkId::Kusama); + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 0745f435ba05..4f24b0eb9856 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -647,8 +647,8 @@ fn register_token_with_root_yeilds_success() { assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); + let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); System::assert_last_event(RuntimeEvent::EthereumSystem( crate::Event::::RegisterToken { @@ -674,8 +674,8 @@ fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); + hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); + let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); System::assert_last_event(RuntimeEvent::EthereumSystem( crate::Event::::RegisterToken { From 0fa95ac5ac64a2f546e1c88f183781ea8b31c747 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 10:53:21 +0800 Subject: [PATCH 56/90] Refactor relative locations tests --- bridges/snowbridge/pallets/system/src/mock.rs | 1 + .../snowbridge/pallets/system/src/tests.rs | 251 +++++++----------- 2 files changed, 98 insertions(+), 154 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 72605ea2283e..f70641288250 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -171,6 +171,7 @@ parameter_types! { pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumDestination: Location = Location::new(2,[GlobalConsensus(EthereumNetwork::get())]); } pub const DOT: u128 = 10_000_000_000; diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 574dd4c53bb7..3920d1c7e2ad 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,118 +633,10 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_root_yields_success() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(1, []); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); - let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_relative_address_reanchors_to_ethereum_and_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(1, []); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2").into(); - let expected_location = Location::new(1, [GlobalConsensus(Polkadot)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_complex_location_simplifies_and_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Kusama)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852").into(); - let expected_location = Location::new(1, [GlobalConsensus(Kusama)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_doubled_bridged_polkadot_location_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Rococo)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id = - hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(); - let expected_location = Location::new(1, [GlobalConsensus(Rococo)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); -} - -#[test] -fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { +fn register_token_with_signed_yeilds_bad_origin() { new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - let location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]); + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); let versioned_location: Box = Box::new(location.clone().into()); let asset_metadata = AssetMetadata { decimals: 10, @@ -754,54 +646,105 @@ fn register_token_with_ethereum_address_reanchors_to_relative_and_fails() { assert_noop!( EthereumSystem::register_token(origin, versioned_location, asset_metadata), - Error::::LocationConversionFailed + BadOrigin ); }); } -#[test] -fn register_token_with_double_bridged_ethereum_address_succeeds() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::root(); - const NETWORK: NetworkId = Ethereum { chain_id: 1 }; - let location = Location::new(2, [GlobalConsensus(NETWORK)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); - - let expected_token_id: H256 = - hex!("37fd94739deb1c2a8929b45a4f70ffcb52de8b54791609ee13ee0a2b33730269").into(); - let expected_location = Location::new(1, [GlobalConsensus(NETWORK)]); - - System::assert_last_event(RuntimeEvent::EthereumSystem( - crate::Event::::RegisterToken { - location: expected_location.into(), - foreign_token_id: expected_token_id, - }, - )); - }); +pub struct TokenInfo { + pub location: Location, + pub metadata: AssetMetadata, + pub foreign_token_id: TokenId, } #[test] -fn register_token_with_signed_yeilds_bad_origin() { - new_test_ext(true).execute_with(|| { - let origin = RuntimeOrigin::signed([14; 32].into()); - let location = Location::new(1, [Parachain(2000)]); - let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), - BadOrigin - ); - }); +fn register_all_tokens_succeeds() { + let assets = vec![ + // DOT + TokenInfo { + location: Location::parent(), + metadata: AssetMetadata { + decimals: 10, + name: b"DOT".to_vec().try_into().unwrap(), + symbol: b"DOT".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" + ) + .into(), + }, + // GLMR (Some Polkadot parachain currency) + TokenInfo { + location: Location::new(1, [Parachain(2004)]), + metadata: AssetMetadata { + decimals: 10, + name: b"GLMR".to_vec().try_into().unwrap(), + symbol: b"GLMR".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" + ) + .into(), + }, + // USDT + TokenInfo { + location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), + metadata: AssetMetadata { + decimals: 10, + name: b"USDT".to_vec().try_into().unwrap(), + symbol: b"USDT".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "d49fe2118be0cca618e4d171e60ffea98b7b648dd80dc37d6342116b910b7aa5" + ) + .into(), + }, + // KSM + TokenInfo { + location: Location::new(2, [GlobalConsensus(Kusama)]), + metadata: AssetMetadata { + decimals: 12, + name: b"KSM".to_vec().try_into().unwrap(), + symbol: b"KSM".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" + ) + .into(), + }, + // KAR (Some Kusama parachain currency) + TokenInfo { + location: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + metadata: AssetMetadata { + decimals: 12, + name: b"KAR".to_vec().try_into().unwrap(), + symbol: b"KAR".to_vec().try_into().unwrap(), + }, + foreign_token_id: hex!( + "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" + ) + .into(), + }, + ]; + for asset in assets.iter() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let versioned_location: Box = + Box::new(asset.location.clone().into()); + let asset_metadata = asset.metadata.clone(); + + assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + + let location = asset + .location + .clone() + .reanchored(&EthereumDestination::get(), &UniversalLocation::get()) + .unwrap(); + + System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { + location: location.into(), + foreign_token_id: asset.foreign_token_id, + })); + }); + } } From df60eb864e9d39bb5808d9ea284120bbf3f7a967 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:03:29 +0800 Subject: [PATCH 57/90] More tests --- .../snowbridge/pallets/system/src/tests.rs | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 3920d1c7e2ad..22b02d51dab2 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -677,7 +677,7 @@ fn register_all_tokens_succeeds() { TokenInfo { location: Location::new(1, [Parachain(2004)]), metadata: AssetMetadata { - decimals: 10, + decimals: 12, name: b"GLMR".to_vec().try_into().unwrap(), symbol: b"GLMR".to_vec().try_into().unwrap(), }, @@ -690,7 +690,7 @@ fn register_all_tokens_succeeds() { TokenInfo { location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), metadata: AssetMetadata { - decimals: 10, + decimals: 6, name: b"USDT".to_vec().try_into().unwrap(), symbol: b"USDT".to_vec().try_into().unwrap(), }, @@ -748,3 +748,31 @@ fn register_all_tokens_succeeds() { }); } } + +#[test] +fn register_ethereum_native_token_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 11155111 }), + AccountKey20 { + network: None, + key: hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"), + }, + ], + ); + let versioned_location: Box = Box::new(location.clone().into()); + let asset_metadata = AssetMetadata { + decimals: 18, + name: b"WETH".to_vec().try_into().unwrap(), + symbol: b"WETH".to_vec().try_into().unwrap(), + }; + + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, asset_metadata), + Error::::LocationConversionFailed + ); + }); +} From ad9ad230c76a4ec7d387749071368519d46cbd04 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:05:11 +0800 Subject: [PATCH 58/90] Fix typo --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 22b02d51dab2..d436724caba6 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -633,7 +633,7 @@ fn no_genesis_build_is_uninitialized() { } #[test] -fn register_token_with_signed_yeilds_bad_origin() { +fn register_token_with_signed_yields_bad_origin() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::signed([14; 32].into()); let location = Location::new(1, [Parachain(2000)]); From 388d1be8396925652212d52da6150eba5a32330f Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 11:18:45 +0800 Subject: [PATCH 59/90] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 7935de40ac3d..4aa721728449 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -479,6 +479,7 @@ impl< // Forward message id to Asset Hub. instructions.push(SetTopic(message_id.into())); + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also teleport fees) Ok((instructions.into(), total_fees.into())) } } From 7f110c2d2d7721ec1ea2151f83226bc8ea61cd8d Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:26:19 +0800 Subject: [PATCH 60/90] Rename var --- bridges/snowbridge/pallets/system/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 84b41cd0b9ea..fd2f2eb5b3ab 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -732,10 +732,11 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { - let bridge_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let ethereum_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + // reanchor to Ethereum context let location = location .clone() - .reanchored(&bridge_location, &T::UniversalLocation::get()) + .reanchored(ðereum_location, &T::UniversalLocation::get()) .map_err(|_| Error::::LocationConversionFailed)?; let token_id = TokenIdOf::convert_location(&location) From d0ac1bffe919226a49210177d6db32c4d1367991 Mon Sep 17 00:00:00 2001 From: Ron Date: Sat, 7 Sep 2024 11:26:42 +0800 Subject: [PATCH 61/90] Update bridges/snowbridge/primitives/core/src/location.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/core/src/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index c126618bba35..07c998a5c40e 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -29,7 +29,7 @@ pub type AgentIdOf = pub type TokenId = H256; -/// Convert a token location to a stable ID that can be used on the Ethereum side +/// Convert a token location (relative to Ethereum) to a stable ID that can be used on the Ethereum side pub type TokenIdOf = HashedDescription< TokenId, DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, From d65711353dda3fefb6abda5a367c255beddb08d5 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:38:50 +0800 Subject: [PATCH 62/90] Make test asset non sufficient --- .../chains/parachains/assets/asset-hub-westend/src/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index 73c5ef863309..a9cfcda0dacd 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -86,7 +86,7 @@ pub fn genesis() -> Storage { ( PenpalBTeleportableAssetLocation::get(), PenpalBSiblingSovereignAccount::get(), - true, + false, ED, ), ], From 5be8f8224af731e551671cb5f7412e90b9413e46 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:41:10 +0800 Subject: [PATCH 63/90] Bump package as minor --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 16e2bd993463..44c8ee177a6f 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: major + bump: minor - name: snowbridge-core - bump: major + bump: minor - name: snowbridge-router-primitives - bump: major + bump: minor - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 8d1c9d7b18e258ac77146b2dc81e879ce818104a Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 11:47:08 +0800 Subject: [PATCH 64/90] Improve comments --- bridges/snowbridge/pallets/system/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index fd2f2eb5b3ab..9712f589b808 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -266,12 +266,12 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; - /// Lookup table for foreign to native token ID + /// Lookup table for foreign token ID to native location relative to ethereum #[pallet::storage] pub type ForeignToNativeId = StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; - /// Lookup table for native to foreign token ID + /// Lookup table for native location relative to ethereum to foreign token ID #[pallet::storage] pub type NativeToForeignId = StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; From 9761118c357a15666743a84507fea168d9580961 Mon Sep 17 00:00:00 2001 From: ron Date: Sat, 7 Sep 2024 16:13:18 +0800 Subject: [PATCH 65/90] Revert "Bump package as minor" This reverts commit 5be8f8224af731e551671cb5f7412e90b9413e46. --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 44c8ee177a6f..16e2bd993463 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: minor + bump: major - name: snowbridge-core - bump: minor + bump: major - name: snowbridge-router-primitives - bump: minor + bump: major - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 100c779e0d1be938dfe9ad1bdb4a80ac8b743dc7 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 9 Sep 2024 23:50:31 +0800 Subject: [PATCH 66/90] Revert "Revert "Bump package as minor"" This reverts commit 9761118c357a15666743a84507fea168d9580961. --- prdoc/pr_5546.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 16e2bd993463..44c8ee177a6f 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -13,11 +13,11 @@ crates: - name: snowbridge-pallet-outbound-queue bump: patch - name: snowbridge-pallet-system - bump: major + bump: minor - name: snowbridge-core - bump: major + bump: minor - name: snowbridge-router-primitives - bump: major + bump: minor - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From de6d3ee61759711c10f4a88d4c8eaffe78edf1d7 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 9 Sep 2024 23:52:52 +0800 Subject: [PATCH 67/90] Ignore validate check --- prdoc/pr_5546.prdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prdoc/pr_5546.prdoc b/prdoc/pr_5546.prdoc index 44c8ee177a6f..95f02dbe13b2 100644 --- a/prdoc/pr_5546.prdoc +++ b/prdoc/pr_5546.prdoc @@ -14,10 +14,13 @@ crates: bump: patch - name: snowbridge-pallet-system bump: minor + validate: false - name: snowbridge-core bump: minor + validate: false - name: snowbridge-router-primitives bump: minor + validate: false - name: bridge-hub-westend-runtime bump: patch - name: bridge-hub-rococo-runtime From 116c421d1d56dc43923d6af01b5d5f7b38678099 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 00:00:42 +0800 Subject: [PATCH 68/90] Rename as EthereumUniversalLocation be more clear --- .../primitives/router/src/inbound/mod.rs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 4aa721728449..49bb731e5e77 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -102,14 +102,14 @@ pub struct MessageToXcm< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { _phantom: PhantomData<( @@ -119,7 +119,7 @@ pub struct MessageToXcm< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, )>, } @@ -156,7 +156,7 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > ConvertMessage for MessageToXcm< @@ -166,16 +166,17 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { type Balance = Balance; @@ -214,7 +215,7 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, > MessageToXcm< @@ -224,16 +225,17 @@ impl< AccountId, Balance, ConvertAssetId, - UniversalLocation, + EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, ConvertAssetId: MaybeEquivalence, - UniversalLocation: Get, + EthereumUniversalLocation: Get, GlobalAssetHubLocation: Get, { fn convert_register_token( @@ -416,7 +418,7 @@ impl< let mut reanchored_asset_loc = asset_loc.clone(); reanchored_asset_loc - .reanchor(&GlobalAssetHubLocation::get(), &UniversalLocation::get()) + .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) .map_err(|_| ConvertMessageError::CannotReanchor)?; let asset: Asset = (reanchored_asset_loc, amount).into(); @@ -479,7 +481,8 @@ impl< // Forward message id to Asset Hub. instructions.push(SetTopic(message_id.into())); - // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also teleport fees) + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also + // teleport fees) Ok((instructions.into(), total_fees.into())) } } From 5203dc02b59d4743bca9633b3b6bcc7e37b7e883 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 00:40:00 +0800 Subject: [PATCH 69/90] Switch to using half the fee for local exec and half for transport --- .../snowbridge/primitives/router/src/inbound/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 49bb731e5e77..6c107cfa07c2 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,8 +168,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -227,8 +226,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -396,7 +394,7 @@ where asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -440,10 +438,6 @@ where let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); instructions.extend(vec![ - // `SetFeesMode` to pay transport fee from bridge sovereign, which depends on - // unspent AH fees deposited to the bridge sovereign, - // more context and analysis in https://github.com/paritytech/polkadot-sdk/pull/5546#discussion_r1744682864 - SetFeesMode { jit_withdraw: true }, // `SetAppendix` ensures that `fees` are not trapped in any case SetAppendix(Xcm(vec![DepositAsset { assets: AllCounted(2).into(), From 3779af2bb81cb9f022c1b9d3567613ede8049629 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 10 Sep 2024 19:21:51 +0800 Subject: [PATCH 70/90] Update bridges/snowbridge/primitives/router/src/inbound/mod.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 6c107cfa07c2..10630fc2ad84 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -394,7 +394,7 @@ impl< asset_hub_fee: u128, ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee / 2).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub From 96c01dc5dc8750dc96458326b6170242e0909e73 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 10 Sep 2024 19:22:26 +0800 Subject: [PATCH 71/90] Update bridges/snowbridge/pallets/system/src/tests.rs Co-authored-by: Adrian Catangiu --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index d436724caba6..51cb377b24a7 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -688,7 +688,7 @@ fn register_all_tokens_succeeds() { }, // USDT TokenInfo { - location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1084)]), + location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), metadata: AssetMetadata { decimals: 6, name: b"USDT".to_vec().try_into().unwrap(), From a802127d9248f6e006b5597cb56cae0e100e0cdc Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 20:57:41 +0800 Subject: [PATCH 72/90] Use full location of Ethereum --- bridges/snowbridge/pallets/system/src/lib.rs | 6 +++--- bridges/snowbridge/pallets/system/src/mock.rs | 2 +- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 4 ++-- .../bridge-hub-westend/src/bridge_to_ethereum_config.rs | 4 ++-- cumulus/parachains/runtimes/constants/src/rococo.rs | 3 ++- cumulus/parachains/runtimes/constants/src/westend.rs | 3 ++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 9712f589b808..8f3235279956 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -177,8 +177,8 @@ pub mod pallet { /// This chain's Universal Location. type UniversalLocation: Get; - // The bridges configured Ethereum network with chain id. - type EthereumNetwork: Get; + // The bridges configured Ethereum location + type EthereumLocation: Get; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; @@ -732,7 +732,7 @@ pub mod pallet { metadata: AssetMetadata, pays_fee: PaysFee, ) -> Result<(), DispatchError> { - let ethereum_location = Location::new(2, [GlobalConsensus(T::EthereumNetwork::get())]); + let ethereum_location = T::EthereumLocation::get(); // reanchor to Ethereum context let location = location .clone() diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index f70641288250..47b089866a53 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -210,7 +210,7 @@ impl crate::Config for Test { type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumDestination; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index fde214ed42d6..d9a3869bd6e1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -29,7 +29,7 @@ use sp_core::H160; use testnet_parachains_constants::rococo::{ currency::*, fee::WeightToFee, - snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; use crate::xcm_config::RelayNetwork; @@ -190,7 +190,7 @@ impl snowbridge_pallet_system::Config for Runtime { type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 4b3f61a42ad4..1bd425ab4075 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -30,7 +30,7 @@ use sp_core::H160; use testnet_parachains_constants::westend::{ currency::*, fee::WeightToFee, - snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; use crate::xcm_config::RelayNetwork; @@ -189,7 +189,7 @@ impl snowbridge_pallet_system::Config for Runtime { type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; type UniversalLocation = UniversalLocation; - type EthereumNetwork = EthereumNetwork; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index 56f4868371c1..6c5380df2bfb 100644 --- a/cumulus/parachains/runtimes/constants/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -148,7 +148,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -159,6 +159,7 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); } } diff --git a/cumulus/parachains/runtimes/constants/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs index fec66cec2eb6..a4667f83fbef 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -171,7 +171,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -182,5 +182,6 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); } } From baf5b27b37c0c26420d7ecd55ef2c3878da66bb4 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 20:57:49 +0800 Subject: [PATCH 73/90] Fix test --- bridges/snowbridge/pallets/system/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 51cb377b24a7..b3699ec2f24d 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -695,7 +695,7 @@ fn register_all_tokens_succeeds() { symbol: b"USDT".to_vec().try_into().unwrap(), }, foreign_token_id: hex!( - "d49fe2118be0cca618e4d171e60ffea98b7b648dd80dc37d6342116b910b7aa5" + "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" ) .into(), }, From ed6fc8a175c7c61a2fb1807f2e04c6cbe1a7e386 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 22:14:47 +0800 Subject: [PATCH 74/90] Fix fmt --- bridges/snowbridge/primitives/core/src/location.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 07c998a5c40e..fe8db0e35283 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -29,7 +29,8 @@ pub type AgentIdOf = pub type TokenId = H256; -/// Convert a token location (relative to Ethereum) to a stable ID that can be used on the Ethereum side +/// Convert a token location (relative to Ethereum) to a stable ID that can be used on the Ethereum +/// side pub type TokenIdOf = HashedDescription< TokenId, DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, From 5870827873079050883ab7e0b1724e91b50e6712 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 10 Sep 2024 23:08:59 +0800 Subject: [PATCH 75/90] Remove tests unused --- .../src/tests/snowbridge.rs | 343 ------------------ 1 file changed, 343 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 389b3ec33427..9841e9c51d15 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -600,346 +600,3 @@ fn transfer_ah_token() { ); }); } - -#[test] -fn transfer_penpal_native_token() { - let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); - let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); - - let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); - let ethereum_sovereign: AccountId = - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) - .unwrap() - .into(); - AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); - - let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]); - - let penpal_asset_location_after_reanchored = - Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]); - - let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); - - // Register token on AH - AssetHubWestend::force_create_foreign_asset( - penpal_asset_location.clone().try_into().unwrap(), - PenpalBSiblingSovereignAccount::get().clone(), - false, - ASSET_MIN_BALANCE, - vec![], - ); - - // Fund sender on AH - AssetHubWestend::mint_foreign_asset( - ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), - penpal_asset_location.clone().try_into().unwrap(), - AssetHubWestendSender::get(), - TOKEN_AMOUNT, - ); - - // Fund sov of AH on penpal - let ah_sovereign = - PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); - PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); - PenpalB::mint_foreign_asset( - ::RuntimeOrigin::signed(PenpalAssetOwner::get()), - RelayLocation::get(), - ah_sovereign.clone(), - INITIAL_FUND, - ); - - // Create token - BridgeHubWestend::execute_with(|| { - type RuntimeOrigin = ::RuntimeOrigin; - - assert_ok!(::EthereumSystem::register_token( - RuntimeOrigin::root(), - Box::new(VersionedLocation::V4(penpal_asset_location.clone())), - AssetMetadata { - name: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), - symbol: "penpal_asset".as_bytes().to_vec().try_into().unwrap(), - decimals: 12, - }, - )); - }); - - // Send token to Ethereum - AssetHubWestend::execute_with(|| { - type RuntimeOrigin = ::RuntimeOrigin; - type RuntimeEvent = ::RuntimeEvent; - - let assets = vec![Asset { - id: penpal_asset_location.clone().into(), - fun: Fungible(TOKEN_AMOUNT / 10), - }]; - - let beneficiary = VersionedLocation::V4(Location::new( - 0, - [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], - )); - - assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( - RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(VersionedLocation::from(ethereum_destination)), - Box::new(beneficiary), - Box::new(VersionedAssets::from(Assets::from(assets))), - 0, - Unlimited, - )); - - assert_expected_events!( - AssetHubWestend, - vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] - ); - - let events = AssetHubWestend::events(); - // Check that the native asset transferred to some reserved account(sovereign of Ethereum) - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) - if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign - )), - "native token reserved to Ethereum sovereign account." - ); - }); - - // Send token back from Ethereum - BridgeHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that the transfer token back to Ethereum message was queue in the Ethereum - // Outbound Queue - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] - ); - - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::SendNativeToken { - token_id, - destination: Destination::ForeignAccountId32 { - para_id: PenpalB::para_id().into(), - id: PenpalBReceiver::get().into(), - fee: XCM_FEE, - }, - amount: TOKEN_AMOUNT / 10, - fee: XCM_FEE, - }, - }); - - // Convert the message to XCM - let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); - // Send the XCM - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); - - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] - ); - }); - - AssetHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that token burnt from some reserved account - assert_expected_events!( - AssetHubWestend, - vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] - ); - }); - - PenpalB::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that token issued to beneficial - assert_expected_events!( - PenpalB, - vec![RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {},] - ); - - let events = PenpalB::events(); - - // Check that token issued to destination account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { amount, who, ..}) - if *amount == TOKEN_AMOUNT/10 && *who == PenpalBReceiver::get() - )), - "Token minted to beneficiary." - ); - }) -} - -#[test] -fn transfer_penpal_asset() { - let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); - let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); - - let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); - let ethereum_sovereign: AccountId = - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) - .unwrap() - .into(); - - AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); - - let penpal_asset_location = Location::new(1, [Parachain(PenpalB::para_id().into())]) - .appended_with(PenpalLocalTeleportableToAssetHub::get()) - .unwrap(); - - let penpal_asset_location_after_reanchored = - Location::new(1, [GlobalConsensus(Westend), Parachain(PenpalB::para_id().into())]) - .appended_with(PenpalLocalTeleportableToAssetHub::get()) - .unwrap(); - - let token_id = TokenIdOf::convert_location(&penpal_asset_location_after_reanchored).unwrap(); - - // Fund sender on AH - AssetHubWestend::mint_foreign_asset( - ::RuntimeOrigin::signed(PenpalBSiblingSovereignAccount::get()), - penpal_asset_location.clone().try_into().unwrap(), - AssetHubWestendSender::get(), - TOKEN_AMOUNT, - ); - - // Fund sov of AH on penpal - let ah_sovereign = - PenpalB::sovereign_account_id_of(PenpalB::sibling_location_of(AssetHubWestend::para_id())); - PenpalB::fund_accounts(vec![(ah_sovereign.clone(), INITIAL_FUND)]); - PenpalB::mint_foreign_asset( - ::RuntimeOrigin::signed(PenpalAssetOwner::get()), - RelayLocation::get(), - ah_sovereign.clone(), - INITIAL_FUND, - ); - PenpalB::mint_asset( - ::RuntimeOrigin::signed(PenpalAssetOwner::get()), - TELEPORTABLE_ASSET_ID, - ah_sovereign.clone(), - TOKEN_AMOUNT, - ); - - // create token - BridgeHubWestend::execute_with(|| { - type Runtime = ::Runtime; - - snowbridge_pallet_system::ForeignToNativeId::::insert( - token_id, - penpal_asset_location_after_reanchored.clone(), - ); - }); - - // Send token to Ethereum - AssetHubWestend::execute_with(|| { - type RuntimeOrigin = ::RuntimeOrigin; - type RuntimeEvent = ::RuntimeEvent; - - let assets = vec![Asset { - id: penpal_asset_location.clone().into(), - fun: Fungible(TOKEN_AMOUNT / 10), - }]; - - let beneficiary = VersionedLocation::V4(Location::new( - 0, - [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], - )); - - assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( - RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(VersionedLocation::from(ethereum_destination)), - Box::new(beneficiary), - Box::new(VersionedAssets::from(Assets::from(assets))), - 0, - Unlimited, - )); - - assert_expected_events!( - AssetHubWestend, - vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred{..}) => {},] - ); - - let events = AssetHubWestend::events(); - // Check that the native asset transferred to some reserved account(sovereign of Ethereum) - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::ForeignAssets(pallet_assets::Event::Transferred { amount, to, ..}) - if *amount == TOKEN_AMOUNT/10 && *to == ethereum_sovereign - )), - "native token reserved to Ethereum sovereign account." - ); - }); - - // Send token back from Ethereum - BridgeHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that the transfer token back to Ethereum message was queue in the Ethereum - // Outbound Queue - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{..}) => {},] - ); - - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::SendNativeToken { - token_id, - destination: Destination::ForeignAccountId32 { - para_id: PenpalB::para_id().into(), - id: PenpalBReceiver::get().into(), - fee: XCM_FEE, - }, - amount: TOKEN_AMOUNT / 10, - fee: XCM_FEE, - }, - }); - - // Convert the message to XCM - let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); - // Send the XCM - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); - - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] - ); - }); - - AssetHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that token burnt from some reserved account - assert_expected_events!( - AssetHubWestend, - vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { .. }) => {},] - ); - }); - - PenpalB::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that token issued to beneficial - assert_expected_events!( - PenpalB, - vec![RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {},] - ); - - let events = PenpalB::events(); - - // Check that token issued to destination account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Assets(pallet_assets::Event::Issued { amount, owner, ..}) - if *amount == TOKEN_AMOUNT/10 && *owner == PenpalBReceiver::get() - )), - "Token minted to beneficiary." - ); - }) -} From 947201d37a84666e797a1aa13ab539d2a65966af Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 11 Sep 2024 02:44:28 +0200 Subject: [PATCH 76/90] remove destination parachain transfer (#179) --- .../primitives/router/src/inbound/mod.rs | 76 +++++-------------- 1 file changed, 20 insertions(+), 56 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 10630fc2ad84..7f44c2ea2bef 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,7 +168,8 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -226,7 +227,8 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > where + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -396,20 +398,19 @@ impl< let network = Ethereum { chain_id }; let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - let (dest_para_id, beneficiary, dest_para_fee) = match destination { + let beneficiary = match destination { // Final destination is a 32-byte account on AssetHub Destination::AccountId32 { id } => - (None, Location::new(0, [AccountId32 { network: None, id }]), 0), + Location::new(0, [AccountId32 { network: None, id }]), // Final destination is a 32-byte account on a sibling of AssetHub - Destination::ForeignAccountId32 { para_id, id, fee } => - (Some(para_id), Location::new(0, [AccountId32 { network: None, id }]), fee), + Destination::ForeignAccountId32 { para_id, id, .. } => + Location::new(0, [AccountId32 { network: None, id }]), // Final destination is a 20-byte account on a sibling of AssetHub - Destination::ForeignAccountId20 { para_id, id, fee } => - (Some(para_id), Location::new(0, [AccountKey20 { network: None, key: id }]), fee), + Destination::ForeignAccountId20 { para_id, id, .. } => + Location::new(0, [AccountKey20 { network: None, key: id }]), }; - let total_fees = asset_hub_fee.saturating_add(dest_para_fee); - let total_fee_asset: Asset = (Location::parent(), total_fees).into(); + let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); let asset_loc = ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; @@ -423,61 +424,24 @@ impl< let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - let mut instructions = vec![ + let bridge_location = Location::new(2, GlobalConsensus(network)); + + let instructions = vec![ ReceiveTeleportedAsset(total_fee_asset.clone().into()), BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), UniversalOrigin(GlobalConsensus(network)), WithdrawAsset(asset.clone().into()), + // Deposit both asset and fees to beneficiary so the fees will not get + // trapped. Another benefit is when fees left more than ED on AssetHub could be + // used to create the beneficiary account in case it does not exist. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + SetTopic(message_id.into()), ]; - let bridge_location = Location::new(2, GlobalConsensus(network)); - - match dest_para_id { - Some(dest_para_id) => { - let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); - - instructions.extend(vec![ - // `SetAppendix` ensures that `fees` are not trapped in any case - SetAppendix(Xcm(vec![DepositAsset { - assets: AllCounted(2).into(), - beneficiary: bridge_location, - }])), - // Perform a reserve withdraw to send to destination chain. Leave half of the - // asset_hub_fee for the delivery cost - InitiateReserveWithdraw { - assets: Definite( - vec![asset.clone(), (Location::parent(), dest_para_fee).into()].into(), - ), - reserve: Location::new(1, [Parachain(dest_para_id)]), - xcm: vec![ - // Buy execution on target. - BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit asset and fee to beneficiary. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - // Forward message id to destination parachain. - SetTopic(message_id.into()), - ] - .into(), - }, - ]); - }, - None => { - instructions.extend(vec![ - // Deposit both asset and fees to beneficiary so the fees will not get - // trapped. Another benefit is when fees left more than ED on AssetHub could be - // used to create the beneficiary account in case it does not exist. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - ]); - }, - } - - // Forward message id to Asset Hub. - instructions.push(SetTopic(message_id.into())); - // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also // teleport fees) - Ok((instructions.into(), total_fees.into())) + Ok((instructions.into(), asset_hub_fee.into())) } } From 43e546b176835d6b023924a25b4762b252d488db Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 11 Sep 2024 09:16:25 +0800 Subject: [PATCH 77/90] Cleanup and fix CI --- .../snowbridge/primitives/router/src/inbound/mod.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 7f44c2ea2bef..c4a76bcadaac 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -401,14 +401,9 @@ where let beneficiary = match destination { // Final destination is a 32-byte account on AssetHub Destination::AccountId32 { id } => - Location::new(0, [AccountId32 { network: None, id }]), - // Final destination is a 32-byte account on a sibling of AssetHub - Destination::ForeignAccountId32 { para_id, id, .. } => - Location::new(0, [AccountId32 { network: None, id }]), - // Final destination is a 20-byte account on a sibling of AssetHub - Destination::ForeignAccountId20 { para_id, id, .. } => - Location::new(0, [AccountKey20 { network: None, key: id }]), - }; + Ok(Location::new(0, [AccountId32 { network: None, id }])), + _ => Err(ConvertMessageError::InvalidDestination), + }?; let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); @@ -424,8 +419,6 @@ where let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - let bridge_location = Location::new(2, GlobalConsensus(network)); - let instructions = vec![ ReceiveTeleportedAsset(total_fee_asset.clone().into()), BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, From 9f36c763c323636c9cd2317847e3604292dfc4ea Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 11 Sep 2024 09:28:58 +0800 Subject: [PATCH 78/90] Fix clippy --- .../emulated/tests/bridges/bridge-hub-westend/src/lib.rs | 5 ++--- .../bridges/bridge-hub-westend/src/tests/snowbridge.rs | 9 ++------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 98f640a46800..888e0d51b39d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -69,9 +69,8 @@ mod imports { BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, - PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, - WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, - WestendRelaySender as WestendSender, + PenpalBParaSender as PenpalBSender, WestendRelay as Westend, + WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 9841e9c51d15..f2cd15e71ad7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -16,15 +16,10 @@ use crate::imports::*; use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; use codec::{Decode, Encode}; -use emulated_integration_tests_common::{ - PenpalBSiblingSovereignAccount, RESERVABLE_ASSET_ID, TELEPORTABLE_ASSET_ID, -}; +use emulated_integration_tests_common::RESERVABLE_ASSET_ID; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use rococo_westend_system_emulated_network::{ - asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner, - penpal_emulated_chain::penpal_runtime::xcm_config::RelayLocation, -}; +use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, From c3e84943b60007339a16b11434010dd9660f0e27 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 11 Sep 2024 09:43:36 +0800 Subject: [PATCH 79/90] Fix fmt --- bridges/snowbridge/primitives/router/src/inbound/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index c4a76bcadaac..5cff8413af66 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -168,8 +168,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, @@ -227,8 +226,7 @@ impl< ConvertAssetId, EthereumUniversalLocation, GlobalAssetHubLocation, - > -where + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, From 516970e05e28dab353e4e445bf65d09a2a14f304 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 11 Sep 2024 09:43:54 +0800 Subject: [PATCH 80/90] Fix clippy --- .../emulated/tests/bridges/bridge-hub-westend/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 888e0d51b39d..5e0462d14882 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -52,10 +52,7 @@ mod imports { BridgeHubWestendParaPallet as BridgeHubWestendPallet, BridgeHubWestendXcmConfig, }, penpal_emulated_chain::{ - penpal_runtime::xcm_config::{ - LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, - UniversalLocation as PenpalUniversalLocation, - }, + penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, westend_emulated_chain::{ From 54f95b92a37b05a0adb85cdba065c89b815c84af Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 11 Sep 2024 17:17:47 +0800 Subject: [PATCH 81/90] Update cumulus/parachains/runtimes/constants/src/rococo.rs Co-authored-by: Adrian Catangiu --- cumulus/parachains/runtimes/constants/src/rococo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index 6c5380df2bfb..be4b5c9711cc 100644 --- a/cumulus/parachains/runtimes/constants/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -159,7 +159,7 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; - pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); + pub EthereumLocation: Location = Location::new(2, EthereumNetwork::get()); } } From 124ca471e81c69426e8ccced6f779d43a789cf2d Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 11 Sep 2024 17:18:05 +0800 Subject: [PATCH 82/90] Update cumulus/parachains/runtimes/constants/src/westend.rs Co-authored-by: Adrian Catangiu --- cumulus/parachains/runtimes/constants/src/westend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/constants/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs index a4667f83fbef..47ba8f7e97ae 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -182,6 +182,6 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; - pub EthereumLocation: Location = Location::new(2,EthereumNetwork::get()); + pub EthereumLocation: Location = Location::new(2, EthereumNetwork::get()); } } From d50996948903fc7e5469213dd7ef718cef747831 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:24:19 +0200 Subject: [PATCH 83/90] Improve tests for snowbridge-pallet-ethereum-system --- .../snowbridge/pallets/system/src/tests.rs | 108 ++++++------------ bridges/snowbridge/primitives/core/src/lib.rs | 18 ++- 2 files changed, 53 insertions(+), 73 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index b3699ec2f24d..98c937d86328 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -638,112 +638,84 @@ fn register_token_with_signed_yields_bad_origin() { let origin = RuntimeOrigin::signed([14; 32].into()); let location = Location::new(1, [Parachain(2000)]); let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 10, - name: b"Dot".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }; - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), + EthereumSystem::register_token(origin, versioned_location, Default::default()), BadOrigin ); }); } -pub struct TokenInfo { - pub location: Location, - pub metadata: AssetMetadata, - pub foreign_token_id: TokenId, +pub struct RegisterTokenTestCase { + /// Input: Location of Polkadot-native token relative to BH + pub native: Location, + /// Output: Reanchored, canonicalized location + pub reanchored: Location, + /// Output: Stable hash of reanchored location + pub foreign: TokenId, } #[test] fn register_all_tokens_succeeds() { - let assets = vec![ + let test_cases = vec![ // DOT - TokenInfo { - location: Location::parent(), - metadata: AssetMetadata { - decimals: 10, - name: b"DOT".to_vec().try_into().unwrap(), - symbol: b"DOT".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::parent(), + reanchored: Location::new(1, GlobalConsensus(Polkadot)), + foreign: hex!( "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" ) .into(), }, // GLMR (Some Polkadot parachain currency) - TokenInfo { - location: Location::new(1, [Parachain(2004)]), - metadata: AssetMetadata { - decimals: 12, - name: b"GLMR".to_vec().try_into().unwrap(), - symbol: b"GLMR".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(1, [Parachain(2004)]), + reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), + foreign: hex!( "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" ) .into(), }, // USDT - TokenInfo { - location: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - metadata: AssetMetadata { - decimals: 6, - name: b"USDT".to_vec().try_into().unwrap(), - symbol: b"USDT".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), + reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), + foreign: hex!( "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" ) .into(), }, // KSM - TokenInfo { - location: Location::new(2, [GlobalConsensus(Kusama)]), - metadata: AssetMetadata { - decimals: 12, - name: b"KSM".to_vec().try_into().unwrap(), - symbol: b"KSM".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama)]), + foreign: hex!( "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" ) .into(), }, // KAR (Some Kusama parachain currency) - TokenInfo { - location: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), - metadata: AssetMetadata { - decimals: 12, - name: b"KAR".to_vec().try_into().unwrap(), - symbol: b"KAR".to_vec().try_into().unwrap(), - }, - foreign_token_id: hex!( + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), + foreign: hex!( "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" ) .into(), }, ]; - for asset in assets.iter() { + for tc in test_cases.iter() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); - let versioned_location: Box = - Box::new(asset.location.clone().into()); - let asset_metadata = asset.metadata.clone(); + let versioned_location: VersionedLocation = tc.native.clone().into(); - assert_ok!(EthereumSystem::register_token(origin, versioned_location, asset_metadata)); + assert_ok!(EthereumSystem::register_token(origin, Box::new(versioned_location), Default::default())); - let location = asset - .location - .clone() - .reanchored(&EthereumDestination::get(), &UniversalLocation::get()) - .unwrap(); + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign.clone())); + assert_eq!(ForeignToNativeId::::get(tc.foreign.clone()), Some(tc.reanchored.clone())); System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { - location: location.into(), - foreign_token_id: asset.foreign_token_id, + location: tc.reanchored.clone().into(), + foreign_token_id: tc.foreign, })); }); } @@ -764,14 +736,8 @@ fn register_ethereum_native_token_fails() { ], ); let versioned_location: Box = Box::new(location.clone().into()); - let asset_metadata = AssetMetadata { - decimals: 18, - name: b"WETH".to_vec().try_into().unwrap(), - symbol: b"WETH".to_vec().try_into().unwrap(), - }; - assert_noop!( - EthereumSystem::register_token(origin, versioned_location, asset_metadata), + EthereumSystem::register_token(origin, versioned_location, Default::default()), Error::::LocationConversionFailed ); }); diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 01ad86a26018..7ad129a52542 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -154,7 +154,21 @@ pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId = /// Metadata to include in the instantiated ERC20 token contract #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct AssetMetadata { - pub name: BoundedVec>, - pub symbol: BoundedVec>, + pub name: BoundedVec>, + pub symbol: BoundedVec>, pub decimals: u8, } + +#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +impl Default for AssetMetadata { + fn default() -> Self { + AssetMetadata { + name: BoundedVec::truncate_from(vec![]), + symbol: BoundedVec::truncate_from(vec![]), + decimals: 0, + } + } +} + +/// Maximum length of a string field in ERC20 token metada +const METADATA_FIELD_MAX_LEN: u32 = 32; From 3c694436ab9cfb992ed666ecded40f904f1c8164 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:22:47 +0200 Subject: [PATCH 84/90] Remove redundant code --- bridges/snowbridge/pallets/system/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 8f3235279956..1e8a788b7a5a 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -628,9 +628,7 @@ pub mod pallet { let location: Location = (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; - let pays_fee = PaysFee::::No; - - Self::do_register_token(&location, metadata, pays_fee)?; + Self::do_register_token(&location, metadata, PaysFee::::No)?; Ok(PostDispatchInfo { actual_weight: Some(T::WeightInfo::register_token()), From 315070f0fe94975f15c8caed2b3192ab410fd7db Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:23:05 +0800 Subject: [PATCH 85/90] Remove tests unused --- bridges/snowbridge/primitives/router/src/inbound/tests.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 1c018f3ab0c4..e0672f3928af 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -66,9 +66,5 @@ fn test_reanchor_all_assets() { let mut reanchored_asset_with_ethereum_context = reanchored_asset.clone(); assert_ok!(reanchored_asset_with_ethereum_context.reanchor(&global_ah, ðereum_context)); assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); - // reanchor back to original location in context of BH - let mut reanchored_asset_with_bh_context = reanchored_asset.clone(); - assert_ok!(reanchored_asset_with_bh_context.reanchor(&global_ah, &bh_context)); - assert_eq!(reanchored_asset_with_bh_context, asset.clone()); } } From 37a28c4a6d486c80aac31fad62d68d5e3723f646 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:38:01 +0800 Subject: [PATCH 86/90] Rename multi_assets --- .../bridge-hub-westend/src/tests/snowbridge.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index f2cd15e71ad7..b668ac89d845 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -218,7 +218,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { )), fun: Fungible(TOKEN_AMOUNT), }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -239,7 +239,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeOrigin::signed(AssetHubWestendReceiver::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, ) @@ -337,7 +337,7 @@ fn transfer_relay_token() { type RuntimeEvent = ::RuntimeEvent; let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -353,7 +353,7 @@ fn transfer_relay_token() { RuntimeOrigin::signed(AssetHubWestendSender::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, )); @@ -500,7 +500,7 @@ fn transfer_ah_token() { // Send partial of the token, will fail if send all let assets = vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let beneficiary = VersionedLocation::V4(Location::new( 0, @@ -511,7 +511,7 @@ fn transfer_ah_token() { RuntimeOrigin::signed(AssetHubWestendSender::get()), Box::new(VersionedLocation::from(ethereum_destination)), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, )); From 22f8dea20120f758eebb0285c6b37615be5456a6 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:42:47 +0800 Subject: [PATCH 87/90] Cleanup --- .../primitives/core/src/location.rs | 23 ++----------------- .../primitives/router/src/inbound/tests.rs | 1 - 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index fe8db0e35283..aad1c9ece05c 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -97,10 +97,9 @@ impl DescribeLocation for DescribeTokenTerminal { #[cfg(test)] mod tests { use crate::TokenIdOf; - use frame_support::assert_ok; use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, InteriorLocation, Junction::*, Location, - NetworkId::*, PalletInstance, Parachain, Reanchorable, + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, }; use xcm_executor::traits::ConvertLocation; @@ -203,22 +202,4 @@ mod tests { ); } } - - #[test] - fn test_reanchor_relay_token_from_different_consensus() { - let asset_id: Location = Location::new(2, [GlobalConsensus(Rococo)]); - let ah_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); - let ethereum = Location::new(2, [GlobalConsensus(Ethereum { chain_id: 1 })]); - let mut reanchored_asset = asset_id.clone(); - assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); - assert_eq!( - reanchored_asset, - Location { parents: 1, interior: [GlobalConsensus(Rococo)].into() } - ); - let bh_context: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - let ah = Location::new(1, [GlobalConsensus(Westend), Parachain(1000)]); - let mut reanchored_asset = reanchored_asset.clone(); - assert_ok!(reanchored_asset.reanchor(&ah, &bh_context)); - assert_eq!(reanchored_asset, asset_id); - } } diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index e0672f3928af..e0e90e516be1 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -45,7 +45,6 @@ fn test_reanchor_all_assets() { let ethereum = Location::new(2, ethereum_context.clone()); let ah_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); let global_ah = Location::new(1, ah_context.clone()); - let bh_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1002)].into(); let assets = vec![ // DOT Location::new(1, []), From 1a67710130501cc245da1e6790f0f755e1d9a2a6 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 09:55:37 +0800 Subject: [PATCH 88/90] More checks --- .../tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b668ac89d845..4e9dd5a77dd7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -363,8 +363,8 @@ fn transfer_relay_token() { assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, ..}) - if *amount == TOKEN_AMOUNT, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, to, ..}) + if *amount == TOKEN_AMOUNT && *to == ethereum_sovereign.clone(), )), "native token reserved to Ethereum sovereign account." ); From 211b450f06fc8b29f44a50147f40e6544ccbeffa Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 10:04:32 +0800 Subject: [PATCH 89/90] Rename GlobalAssetHub to AssetHubFromEthereum --- bridges/snowbridge/pallets/inbound-queue/src/mock.rs | 4 ++-- .../bridge-hub-rococo/src/bridge_to_ethereum_config.rs | 4 ++-- .../bridge-hub-westend/src/bridge_to_ethereum_config.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index f5e44c2e46a8..3e67d5ab738b 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -114,7 +114,7 @@ parameter_types! { pub const InboundQueuePalletInstance: u8 = 80; pub UniversalLocation: InteriorLocation = [GlobalConsensus(Westend), Parachain(1002)].into(); - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -233,7 +233,7 @@ impl inbound_queue::Config for Test { Balance, MockTokenIdConvert, UniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type PricingParameters = Parameters; type ChannelLookup = MockChannelLookup; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index d9a3869bd6e1..be7005b5379a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -66,7 +66,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } @@ -90,7 +90,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { Balance, EthereumSystem, EthereumUniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 1bd425ab4075..dbca4166a135 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -69,7 +69,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; - pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -92,7 +92,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { Balance, EthereumSystem, EthereumUniversalLocation, - GlobalAssetHub, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; From 7a37dd4162ab5e4f0c3532644e465af64063f832 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 13 Sep 2024 10:22:50 +0800 Subject: [PATCH 90/90] Fix clippy --- .../snowbridge/pallets/system/src/tests.rs | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 98c937d86328..d0286e04abdf 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -661,46 +661,44 @@ fn register_all_tokens_succeeds() { RegisterTokenTestCase { native: Location::parent(), reanchored: Location::new(1, GlobalConsensus(Polkadot)), - foreign: hex!( - "4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2" - ) - .into(), + foreign: hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2") + .into(), }, // GLMR (Some Polkadot parachain currency) RegisterTokenTestCase { native: Location::new(1, [Parachain(2004)]), reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), - foreign: hex!( - "34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb" - ) - .into(), + foreign: hex!("34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb") + .into(), }, // USDT RegisterTokenTestCase { native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), - foreign: hex!( - "14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444" - ) - .into(), + reanchored: Location::new( + 1, + [ + GlobalConsensus(Polkadot), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + foreign: hex!("14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444") + .into(), }, // KSM RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama)]), reanchored: Location::new(1, [GlobalConsensus(Kusama)]), - foreign: hex!( - "03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852" - ) - .into(), + foreign: hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852") + .into(), }, // KAR (Some Kusama parachain currency) RegisterTokenTestCase { native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), - foreign: hex!( - "d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0" - ) - .into(), + foreign: hex!("d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0") + .into(), }, ]; for tc in test_cases.iter() { @@ -708,10 +706,14 @@ fn register_all_tokens_succeeds() { let origin = RuntimeOrigin::root(); let versioned_location: VersionedLocation = tc.native.clone().into(); - assert_ok!(EthereumSystem::register_token(origin, Box::new(versioned_location), Default::default())); + assert_ok!(EthereumSystem::register_token( + origin, + Box::new(versioned_location), + Default::default() + )); - assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign.clone())); - assert_eq!(ForeignToNativeId::::get(tc.foreign.clone()), Some(tc.reanchored.clone())); + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign)); + assert_eq!(ForeignToNativeId::::get(tc.foreign), Some(tc.reanchored.clone())); System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { location: tc.reanchored.clone().into(),