Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer Polkadot-native assets to Ethereum #5546

Merged
merged 115 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
a8ca172
Polkadot assets on ethereum (#128)
yrong Sep 2, 2024
a13e3f9
Fix breaking test
yrong Sep 2, 2024
94f1d1b
Disable register PNA
yrong Sep 2, 2024
664e3dc
Split half of the asset_hub_fee for delivery cost on AH
yrong Sep 2, 2024
5ef32c8
Explicit to use XCM v4 as a storage key
yrong Sep 2, 2024
37e28ed
Fix test
yrong Sep 2, 2024
e44ea71
Add prdoc
yrong Sep 2, 2024
071ffbe
Fix fmt
yrong Sep 2, 2024
db71d27
Fix for compatibility
yrong Sep 2, 2024
07f8e82
PNA system pallet cleanups (#173)
vgeddes Sep 2, 2024
489624e
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 2, 2024
b09a176
Merge branch 'master' into polkadot-native-assets
yrong Sep 2, 2024
7566c01
Fix building error
yrong Sep 3, 2024
ae0f674
Fix test
yrong Sep 3, 2024
26263ac
Fix test
yrong Sep 3, 2024
7ee4832
Fix doc
yrong Sep 3, 2024
46cb352
SetAppendix with fee not trapped
yrong Sep 3, 2024
d96c452
semver fixes
claravanstaden Sep 3, 2024
f9fdb21
Merge branch 'master' into polkadot-native-assets
claravanstaden Sep 3, 2024
c80a5cb
EnsureRootOrSigned
yrong Sep 3, 2024
b394c4e
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 3, 2024
9caa782
Update bridges/snowbridge/primitives/router/src/inbound/mod.rs
yrong Sep 3, 2024
92df2b5
Update bridges/snowbridge/primitives/core/src/lib.rs
yrong Sep 3, 2024
c699503
Revert "EnsureRootOrSigned"
yrong Sep 3, 2024
2feffd8
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 3, 2024
bce32ea
Update bridges/snowbridge/primitives/core/src/lib.rs
yrong Sep 3, 2024
e5cb159
Use reference instead
yrong Sep 3, 2024
5b76dc7
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 3, 2024
ee7c1e3
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 3, 2024
51f38b3
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 3, 2024
cfb9269
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 3, 2024
263b622
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 3, 2024
0306436
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 3, 2024
f339995
Add comment for the xcm format
yrong Sep 3, 2024
91707b7
Fix building
yrong Sep 3, 2024
2bbee55
Fix format
yrong Sep 3, 2024
4090be1
Support more foreign locations
yrong Sep 4, 2024
2561fb6
Revert "Support more foreign locations"
alistair-singh Sep 4, 2024
06bc7af
Use describe family and decribe Account terminals
alistair-singh Sep 4, 2024
8092a1f
separate to new file
alistair-singh Sep 4, 2024
0ae39b9
all cases added
alistair-singh Sep 4, 2024
3da4cbb
Use full junction name
yrong Sep 5, 2024
991de7c
Add SetFeesMode
yrong Sep 5, 2024
77cca93
Reanchor test
yrong Sep 5, 2024
ff870fe
Update bridges/snowbridge/pallets/inbound-queue/src/lib.rs
yrong Sep 5, 2024
f3127cb
Change ParaId to 1002
yrong Sep 5, 2024
6e0a769
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 5, 2024
34c655b
Fix parent count allow 1 only
yrong Sep 5, 2024
a01e9fc
deprecate DescribeHere
alistair-singh Sep 5, 2024
d5b9b22
Improve comments
yrong Sep 5, 2024
58dc7d2
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 5, 2024
4351389
make register token relative
alistair-singh Sep 5, 2024
8555304
Merge branch 'master' into polkadot-native-assets
yrong Sep 5, 2024
3191ea9
Update bridges/snowbridge/primitives/core/src/location.rs
yrong Sep 5, 2024
57b9171
Update bridges/snowbridge/pallets/system/src/lib.rs
yrong Sep 5, 2024
0abc9ca
Tx fee waived for root
yrong Sep 5, 2024
f53f3e7
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 5, 2024
41dc959
Fix benchmark
yrong Sep 5, 2024
b7cf507
Revamp test to use relative location
yrong Sep 6, 2024
5fdc036
do not allow changes to token_id locations once set
alistair-singh Sep 6, 2024
b395eaf
fix syntax
alistair-singh Sep 6, 2024
c0ce942
Use Blake2_* hasher instead
yrong Sep 6, 2024
441e2a2
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 6, 2024
afdb9e2
Merge branch 'master' into polkadot-native-assets
yrong Sep 6, 2024
c94da51
Fix to reanchor in context of Ethereum
yrong Sep 7, 2024
547b071
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 7, 2024
176c7af
Update bridges/snowbridge/pallets/system/src/tests.rs
yrong Sep 7, 2024
0944d8a
Use Polkadot as RelayNetwork in tests
yrong Sep 7, 2024
c19acaa
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 7, 2024
0fa95ac
Refactor relative locations tests
yrong Sep 7, 2024
df60eb8
More tests
yrong Sep 7, 2024
ad9ad23
Fix typo
yrong Sep 7, 2024
388d1be
Update bridges/snowbridge/primitives/router/src/inbound/mod.rs
yrong Sep 7, 2024
7f110c2
Rename var
yrong Sep 7, 2024
d0ac1bf
Update bridges/snowbridge/primitives/core/src/location.rs
yrong Sep 7, 2024
9634c02
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 7, 2024
d657113
Make test asset non sufficient
yrong Sep 7, 2024
5be8f82
Bump package as minor
yrong Sep 7, 2024
8d1c9d7
Improve comments
yrong Sep 7, 2024
9761118
Revert "Bump package as minor"
yrong Sep 7, 2024
62f13bb
Merge branch 'master' into polkadot-native-assets
yrong Sep 7, 2024
100c779
Revert "Revert "Bump package as minor""
yrong Sep 9, 2024
de6d3ee
Ignore validate check
yrong Sep 9, 2024
116c421
Rename as EthereumUniversalLocation be more clear
yrong Sep 9, 2024
5203dc0
Switch to using half the fee for local exec and half for transport
yrong Sep 9, 2024
5c00652
Merge branch 'master' into polkadot-native-assets
yrong Sep 9, 2024
2ca1f3a
Merge branch 'master' into polkadot-native-assets
yrong Sep 10, 2024
3779af2
Update bridges/snowbridge/primitives/router/src/inbound/mod.rs
yrong Sep 10, 2024
96c01dc
Update bridges/snowbridge/pallets/system/src/tests.rs
yrong Sep 10, 2024
28a246d
Merge branch 'master' into polkadot-native-assets
yrong Sep 10, 2024
a802127
Use full location of Ethereum
yrong Sep 10, 2024
baf5b27
Fix test
yrong Sep 10, 2024
330edf6
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 10, 2024
ed6fc8a
Fix fmt
yrong Sep 10, 2024
5870827
Remove tests unused
yrong Sep 10, 2024
7ca2baf
Merge branch 'master' into polkadot-native-assets
yrong Sep 10, 2024
947201d
remove destination parachain transfer (#179)
alistair-singh Sep 11, 2024
317762e
Merge branch 'master' into polkadot-native-assets
yrong Sep 11, 2024
43e546b
Cleanup and fix CI
yrong Sep 11, 2024
9f36c76
Fix clippy
yrong Sep 11, 2024
c3e8494
Fix fmt
yrong Sep 11, 2024
516970e
Fix clippy
yrong Sep 11, 2024
54f95b9
Update cumulus/parachains/runtimes/constants/src/rococo.rs
yrong Sep 11, 2024
124ca47
Update cumulus/parachains/runtimes/constants/src/westend.rs
yrong Sep 11, 2024
64cd74a
Merge branch 'master' into polkadot-native-assets
yrong Sep 12, 2024
d509969
Improve tests for snowbridge-pallet-ethereum-system
vgeddes Sep 12, 2024
3c69443
Remove redundant code
vgeddes Sep 12, 2024
315070f
Remove tests unused
yrong Sep 13, 2024
37a28c4
Rename multi_assets
yrong Sep 13, 2024
22f8dea
Cleanup
yrong Sep 13, 2024
f7a6e08
Merge branch 'master' into polkadot-native-assets
yrong Sep 13, 2024
1a67710
More checks
yrong Sep 13, 2024
5e16ab3
Merge branch 'polkadot-native-assets' of https://github.com/Snowfork/…
yrong Sep 13, 2024
211b450
Rename GlobalAssetHub to AssetHubFromEthereum
yrong Sep 13, 2024
7a37dd4
Fix clippy
yrong Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 13 additions & 16 deletions bridges/snowbridge/pallets/inbound-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -62,9 +61,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};

Expand All @@ -86,6 +84,7 @@ pub mod pallet {

use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sp_core::H256;

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -276,12 +275,12 @@ pub mod pallet {
T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?;
}

// Decode payload into VersionMessage
yrong marked this conversation as resolved.
Show resolved Hide resolved
let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref())
.map_err(|_| Error::<T>::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::<T>::InvalidPayload.into()),
};
let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?;

log::info!(
target: LOG_TARGET,
Expand Down Expand Up @@ -323,12 +322,10 @@ pub mod pallet {
impl<T: Config> Pallet<T> {
pub fn do_convert(
message_id: H256,
message: inbound::VersionedMessage,
message: VersionedMessage,
) -> Result<(Xcm<()>, BalanceOf<T>), Error<T>> {
let (mut xcm, fee) =
T::MessageConverter::convert(message).map_err(|e| Error::<T>::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::<T>::ConvertMessage(e))?;
Ok((xcm, fee))
}

Expand Down
20 changes: 18 additions & 2 deletions bridges/snowbridge/pallets/inbound-queue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -112,6 +112,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();
yrong marked this conversation as resolved.
Show resolved Hide resolved
pub GlobalAssetHub: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]);
}

#[cfg(feature = "runtime-benchmarks")]
Expand Down Expand Up @@ -205,6 +208,16 @@ impl TransactAsset for SuccessfulTransactor {
}
}

pub struct MockTokenIdConvert;
impl MaybeEquivalence<TokenId, Location> for MockTokenIdConvert {
fn convert(_id: &TokenId) -> Option<Location> {
Some(Location::parent())
}
fn convert_back(_loc: &Location) -> Option<TokenId> {
None
}
}

impl inbound_queue::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Verifier = MockVerifier;
Expand All @@ -218,6 +231,9 @@ impl inbound_queue::Config for Test {
InboundQueuePalletInstance,
AccountId,
Balance,
MockTokenIdConvert,
UniversalLocation,
GlobalAssetHub,
>;
type PricingParameters = Parameters;
type ChannelLookup = MockChannelLookup;
Expand Down
10 changes: 4 additions & 6 deletions bridges/snowbridge/pallets/outbound-queue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
}
23 changes: 23 additions & 0 deletions bridges/snowbridge/pallets/system/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@ mod benchmarks {
Ok(())
}

#[benchmark]
fn register_token() -> Result<(), BenchmarkError> {
let caller: T::AccountId = whitelisted_caller();

let amount: BalanceOf<T> =
(10_000_000_000_000_u128).saturated_into::<u128>().saturated_into();

T::Token::mint_into(&caller, amount)?;

let relay_token_asset_id: Location = Location::new(1, [GlobalConsensus(Westend)]);
yrong marked this conversation as resolved.
Show resolved Hide resolved
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::Root, asset, asset_metadata);

Ok(())
}

impl_benchmark_test_suite!(
SnowbridgeControl,
crate::mock::new_test_ext(true),
Expand Down
99 changes: 94 additions & 5 deletions bridges/snowbridge/pallets/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#![cfg_attr(not(feature = "std"), no_std)]
//!
//! ## 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.

yrong marked this conversation as resolved.
Show resolved Hide resolved
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;

Expand All @@ -63,13 +70,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;
Expand Down Expand Up @@ -99,7 +109,7 @@ where
}

/// Hash the location to produce an agent id
fn agent_id_of<T: Config>(location: &Location) -> Result<H256, DispatchError> {
pub fn agent_id_of<T: Config>(location: &Location) -> Result<H256, DispatchError> {
T::AgentIdOf::convert_location(location).ok_or(Error::<T>::LocationConversionFailed.into())
}

Expand Down Expand Up @@ -211,6 +221,11 @@ pub mod pallet {
PricingParametersChanged {
params: PricingParametersOf<T>,
},
/// Register Polkadot-native token as a wrapped ERC20 token on Ethereum
RegisterToken {
asset_id: VersionedLocation,
token_id: H256,
yrong marked this conversation as resolved.
Show resolved Hide resolved
},
}

#[pallet::error]
Expand All @@ -226,6 +241,7 @@ pub mod pallet {
InvalidTokenTransferFees,
InvalidPricingParameters,
InvalidUpgradeParameters,
TokenExists,
}

/// The set of registered agents
Expand All @@ -243,6 +259,18 @@ pub mod pallet {
pub type PricingParameters<T: Config> =
StorageValue<_, PricingParametersOf<T>, ValueQuery, T::DefaultPricingParameters>;

/// Lookup table for foreign to native token ID
#[pallet::storage]
#[pallet::getter(fn tokens)]
yrong marked this conversation as resolved.
Show resolved Hide resolved
pub type ForeignToNativeId<T: Config> =
StorageMap<_, Twox64Concat, TokenId, xcm::v4::Location, OptionQuery>;

/// Lookup table for native to foreign token ID
#[pallet::storage]
#[pallet::getter(fn location_tokens)]
yrong marked this conversation as resolved.
Show resolved Hide resolved
pub type NativeToForeignId<T: Config> =
StorageMap<_, Twox64Concat, xcm::v4::Location, TokenId, OptionQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
Expand Down Expand Up @@ -574,6 +602,33 @@ pub mod pallet {
});
Ok(())
}

/// 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
acatangiu marked this conversation as resolved.
Show resolved Hide resolved
yrong marked this conversation as resolved.
Show resolved Hide resolved
/// - `metadata`: Metadata(name,symbol,decimals) of the asset
yrong marked this conversation as resolved.
Show resolved Hide resolved
#[pallet::call_index(10)]
#[pallet::weight(T::WeightInfo::register_token())]
pub fn register_token(
origin: OriginFor<T>,
location: Box<VersionedLocation>,
metadata: AssetMetadata,
) -> DispatchResult {
ensure_root(origin)?;
yrong marked this conversation as resolved.
Show resolved Hide resolved

let location: Location =
(*location).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;

let pays_fee = PaysFee::<T>::No;

Self::do_register_token(location, metadata, pays_fee)?;

Ok(())
acatangiu marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -663,6 +718,31 @@ pub mod pallet {
let secondary_exists = Channels::<T>::contains_key(SECONDARY_GOVERNANCE_CHANNEL);
primary_exists && secondary_exists
}

pub(crate) fn do_register_token(
location: Location,
yrong marked this conversation as resolved.
Show resolved Hide resolved
metadata: AssetMetadata,
pays_fee: PaysFee<T>,
) -> Result<(), DispatchError> {
// Record the token id or fail if it has already been created
let token_id = TokenIdOf::convert_location(&location)
acatangiu marked this conversation as resolved.
Show resolved Hide resolved
.ok_or(Error::<T>::LocationConversionFailed)?;

ForeignToNativeId::<T>::insert(token_id, location.clone());
NativeToForeignId::<T>::insert(location.clone(), token_id);
acatangiu marked this conversation as resolved.
Show resolved Hide resolved

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::<T>::RegisterToken { asset_id: location.into(), token_id });

Ok(())
}
}

impl<T: Config> StaticLookup for Pallet<T> {
Expand All @@ -684,4 +764,13 @@ pub mod pallet {
PricingParameters::<T>::get()
}
}

impl<T: Config> MaybeEquivalence<TokenId, Location> for Pallet<T> {
fn convert(foreign_id: &TokenId) -> Option<Location> {
ForeignToNativeId::<T>::get(foreign_id)
}
fn convert_back(location: &Location) -> Option<TokenId> {
NativeToForeignId::<T>::get(location)
}
}
}
Loading
Loading