difftreelog
Merge pull request #285 from UniqueNetwork/xcm-token-transfer-karura
in: master
Xcm token transfer with Karura
5 files changed
README.mddiffbeforeafterboth333*/333*/334334335```335```336336337338## Karura token transfer339340To get started, you need to open inbound and outbound hrmp channels.341Next, we need to register our asset at Karura.342343```344assetRegistry -> registerForeignAsset(location, metadata)345location:346 V0(X2(Parent, Parachain(PARA_ID))) 347metadata:348 name OPL349 symbol OPL350 decimals 18351minimalBalance 1352```353354Next, we can send tokens from Opal to Karura:355```356polkadotXcm -> reserveTransferAssets357dest:358 V0(X2(Parent, Parachain(<KARURA_PARA_ID>))) 359beneficiary:360 X1(AccountId(Any, <ACCOUNT>))361assets:362 V1(Concrete(0,Here), Fungible(<AMOUNT>))363feeAssetItem: 364 0 365weightLimit:366 <LIMIT>367``` 368369The result will be displayed in ChainState 370tokens -> accounts 371372373To send tokens from Karura to Opal:374375```376xtokens -> transfer377378currencyId:379 ForeingAsset380 <TOKEN_ID>381382amount:383 <AMOUNT>384dest:385 V1386 (387 Parents:1, 388 X2(Parachain(<KARURA_PARA_ID>), AccountId(Any, <ACCOUNT>)389 )390destWeight:391 <WEIGHT>392 393394```launch-config.jsondiffbeforeafterboth9 "rpcPort": 9843,9 "rpcPort": 9843,10 "port": 30444,10 "port": 30444,11 "flags": [11 "flags": [12 "-lparachain::candidate_validation=debug"12 "-lparachain::candidate_validation=debug",13 "-lxcm=trace"13 ]14 ]14 },15 },15 {16 {18 "rpcPort": 9854,19 "rpcPort": 9854,19 "port": 30555,20 "port": 30555,20 "flags": [21 "flags": [21 "-lparachain::candidate_validation=debug"22 "-lparachain::candidate_validation=debug",23 "-lxcm=trace"22 ]24 ]23 },25 },24 {26 {38 "flags": [40 "flags": [39 "-lparachain::candidate_validation=debug"41 "-lparachain::candidate_validation=debug"40 ]42 ]41 }43 },44 {45 "name": "eve",46 "wsPort": 9888,47 "rpcPort": 9887,48 "port": 30888,49 "flags": [50 "-lparachain::candidate_validation=debug"51 ]52 }42 ],53 ],43 "genesis": {54 "genesis": {44 "runtime": {55 "runtime": {67 "flags": [78 "flags": [68 "--rpc-cors=all",79 "--rpc-cors=all",69 "--unsafe-rpc-external",80 "--unsafe-rpc-external",70 "--unsafe-ws-external"81 "--unsafe-ws-external",82 "-lxcm=trace"71 ]83 ]72 },84 },73 {85 {78 "flags": [90 "flags": [79 "--rpc-cors=all",91 "--rpc-cors=all",80 "--unsafe-rpc-external",92 "--unsafe-rpc-external",81 "--unsafe-ws-external"93 "--unsafe-ws-external",94 "-lxcm=trace"82 ]95 ]83 }96 }84 ]97 ]85 }98 },99 {100 "bin": "../Acala/target/release/acala",101 "id": "2000",102 "chain": "karura-dev",103 "balance": "1000000000000000000000",104 "nodes": [105 {106 "wsPort": 9946,107 "port": 31202,108 "name": "alice",109 "flags": [110 "--rpc-cors=all",111 "--unsafe-rpc-external",112 "--unsafe-ws-external",113 "-lxcm=trace"114 ]115 }116 ]117 }86 ],118 ],87 "simpleParachains": [],119 "simpleParachains": [],88 "hrmpChannels": [],120 "hrmpChannels": [121 {122 "sender": 2000,123 "recipient": 1000,124 "maxCapacity": 8,125 "maxMessageSize": 512126 },127 {128 "sender": 1000,129 "recipient": 2000,130 "maxCapacity": 8,131 "maxMessageSize": 512132 }133 ],89 "finalization": false134 "finalization": false90}135}runtime/src/lib.rsdiffbeforeafterboth24 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,24 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,25 traits::{25 traits::{26 AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,26 AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,27 AccountIdConversion, Zero,27 },28 },28 transaction_validity::{TransactionSource, TransactionValidity},29 transaction_validity::{TransactionSource, TransactionValidity},29 ApplyExtrinsicResult, MultiSignature, RuntimeAppPublic,30 ApplyExtrinsicResult, MultiSignature, RuntimeAppPublic,45 dispatch::DispatchResult,46 dispatch::DispatchResult,46 PalletId, parameter_types, StorageValue, ConsensusEngineId,47 PalletId, parameter_types, StorageValue, ConsensusEngineId,47 traits::{48 traits::{48 Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem,49 tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,49 LockIdentifier, OnUnbalanced, Randomness, FindAuthor,50 Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,51 OnUnbalanced, Randomness, FindAuthor,50 },52 },69use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};71use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};70use fp_rpc::TransactionStatus;72use fp_rpc::TransactionStatus;71use sp_runtime::{73use sp_runtime::{72 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},74 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},73 transaction_validity::TransactionValidityError,75 transaction_validity::TransactionValidityError,76 SaturatedConversion,74};77};757876// pub use pallet_timestamp::Call as TimestampCall;79// pub use pallet_timestamp::Call as TimestampCall;82use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};85use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};83use xcm_builder::{86use xcm_builder::{84 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,87 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,85 EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,88 EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser,86 ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,89 ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,87 SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,90 SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,88 SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,89};91};90use xcm_executor::{Config, XcmExecutor};92use xcm_executor::{Config, XcmExecutor, Assets};93use sp_std::{marker::PhantomData};9495use xcm::latest::{96 // Xcm,97 AssetId::{Concrete},98 Fungibility::Fungible as XcmFungible,99 MultiAsset,100 Error as XcmError,101};102use xcm_executor::traits::{MatchesFungible, WeightTrader};103//use xcm_executor::traits::MatchesFungible;104use sp_runtime::traits::CheckedConversion;9110592// mod chain_extension;106// mod chain_extension;93// use crate::chain_extension::{NFTExtension, Imbalance};107// use crate::chain_extension::{NFTExtension, Imbalance};616 AccountId32Aliases<RelayNetwork, AccountId>,630 AccountId32Aliases<RelayNetwork, AccountId>,617);631);632633pub struct OnlySelfCurrency;634impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {635 fn matches_fungible(a: &MultiAsset) -> Option<B> {636 match (&a.id, &a.fun) {637 (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount),638 _ => None,639 }640 }641}618642619/// Means for transacting assets on this chain.643/// Means for transacting assets on this chain.620pub type LocalAssetTransactor = CurrencyAdapter<644pub type LocalAssetTransactor = CurrencyAdapter<621 // Use this currency:645 // Use this currency:622 Balances,646 Balances,623 // Use this currency when it is a fungible asset matching the given location or name:647 // Use this currency when it is a fungible asset matching the given location or name:624 IsConcrete<RelayLocation>,648 OnlySelfCurrency,625 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:649 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:626 LocationToAccountId,650 LocationToAccountId,627 // Our chain's account ID type (we can't get away without mentioning it explicitly):651 // Our chain's account ID type (we can't get away without mentioning it explicitly):677 // ^^^ Parent & its unit plurality gets free execution701 // ^^^ Parent & its unit plurality gets free execution678);702);703704pub struct UsingOnlySelfCurrencyComponents<705 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,706 AssetId: Get<MultiLocation>,707 AccountId,708 Currency: CurrencyT<AccountId>,709 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,710>(711 Weight,712 Currency::Balance,713 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,714);715impl<716 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,717 AssetId: Get<MultiLocation>,718 AccountId,719 Currency: CurrencyT<AccountId>,720 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,721 > WeightTrader722 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>723{724 fn new() -> Self {725 Self(0, Zero::zero(), PhantomData)726 }727728 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {729 let amount = WeightToFee::calc(&weight);730 let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;731732 // location to this parachain through relay chain733 let option1: xcm::v1::AssetId = Concrete(MultiLocation {734 parents: 1,735 interior: X1(Parachain(ParachainInfo::parachain_id().into())),736 });737 // direct location738 let option2: xcm::v1::AssetId = Concrete(MultiLocation {739 parents: 0,740 interior: Here,741 });742743 let required = if payment.fungible.contains_key(&option1) {744 (option1, u128_amount).into()745 } else if payment.fungible.contains_key(&option2) {746 (option2, u128_amount).into()747 } else {748 (Concrete(MultiLocation::default()), u128_amount).into()749 };750751 let unused = payment752 .checked_sub(required)753 .map_err(|_| XcmError::TooExpensive)?;754 self.0 = self.0.saturating_add(weight);755 self.1 = self.1.saturating_add(amount);756 Ok(unused)757 }758759 fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {760 let weight = weight.min(self.0);761 let amount = WeightToFee::calc(&weight);762 self.0 -= weight;763 self.1 = self.1.saturating_sub(amount);764 let amount: u128 = amount.saturated_into();765 if amount > 0 {766 Some((AssetId::get(), amount).into())767 } else {768 None769 }770 }771}772impl<773 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,774 AssetId: Get<MultiLocation>,775 AccountId,776 Currency: CurrencyT<AccountId>,777 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,778 > Drop779 for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>780{781 fn drop(&mut self) {782 OnUnbalanced::on_unbalanced(Currency::issue(self.1));783 }784}679785680pub struct XcmConfig;786pub struct XcmConfig;681impl Config for XcmConfig {787impl Config for XcmConfig {689 type LocationInverter = LocationInverter<Ancestry>;795 type LocationInverter = LocationInverter<Ancestry>;690 type Barrier = Barrier;796 type Barrier = Barrier;691 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;797 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;692 type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;798 type Trader = UsingOnlySelfCurrencyComponents<799 IdentityFee<Balance>,800 RelayLocation,801 AccountId,802 Balances,803 (),804 >;693 type ResponseHandler = (); // Don't handle responses for now.805 type ResponseHandler = (); // Don't handle responses for now.694 type SubscriptionService = PolkadotXcm;806 type SubscriptionService = PolkadotXcm;tests/package.jsondiffbeforeafterboth64 "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",64 "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",65 "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",65 "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",66 "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",66 "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",67 "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",67 "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",68 "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",68 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",69 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",69 "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",70 "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",tests/src/xcmTransfer.test.tsdiffbeforeafterbothno changes