difftreelog
Merge pull request #285 from UniqueNetwork/xcm-token-transfer-karura
in: master
Xcm token transfer with Karura
5 files changed
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -333,3 +333,62 @@
*/
```
+
+
+## Karura token transfer
+
+To get started, you need to open inbound and outbound hrmp channels.
+Next, we need to register our asset at Karura.
+
+```
+assetRegistry -> registerForeignAsset(location, metadata)
+location:
+ V0(X2(Parent, Parachain(PARA_ID)))
+metadata:
+ name OPL
+ symbol OPL
+ decimals 18
+minimalBalance 1
+```
+
+Next, we can send tokens from Opal to Karura:
+```
+polkadotXcm -> reserveTransferAssets
+dest:
+ V0(X2(Parent, Parachain(<KARURA_PARA_ID>)))
+beneficiary:
+ X1(AccountId(Any, <ACCOUNT>))
+assets:
+ V1(Concrete(0,Here), Fungible(<AMOUNT>))
+feeAssetItem:
+ 0
+weightLimit:
+ <LIMIT>
+```
+
+The result will be displayed in ChainState
+tokens -> accounts
+
+
+To send tokens from Karura to Opal:
+
+```
+xtokens -> transfer
+
+currencyId:
+ ForeingAsset
+ <TOKEN_ID>
+
+amount:
+ <AMOUNT>
+dest:
+ V1
+ (
+ Parents:1,
+ X2(Parachain(<KARURA_PARA_ID>), AccountId(Any, <ACCOUNT>)
+ )
+destWeight:
+ <WEIGHT>
+
+
+```
\ No newline at end of file
launch-config.jsondiffbeforeafterboth--- a/launch-config.json
+++ b/launch-config.json
@@ -9,7 +9,8 @@
"rpcPort": 9843,
"port": 30444,
"flags": [
- "-lparachain::candidate_validation=debug"
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
]
},
{
@@ -18,7 +19,8 @@
"rpcPort": 9854,
"port": 30555,
"flags": [
- "-lparachain::candidate_validation=debug"
+ "-lparachain::candidate_validation=debug",
+ "-lxcm=trace"
]
},
{
@@ -38,6 +40,15 @@
"flags": [
"-lparachain::candidate_validation=debug"
]
+ },
+ {
+ "name": "eve",
+ "wsPort": 9888,
+ "rpcPort": 9887,
+ "port": 30888,
+ "flags": [
+ "-lparachain::candidate_validation=debug"
+ ]
}
],
"genesis": {
@@ -67,7 +78,8 @@
"flags": [
"--rpc-cors=all",
"--unsafe-rpc-external",
- "--unsafe-ws-external"
+ "--unsafe-ws-external",
+ "-lxcm=trace"
]
},
{
@@ -78,13 +90,46 @@
"flags": [
"--rpc-cors=all",
"--unsafe-rpc-external",
- "--unsafe-ws-external"
+ "--unsafe-ws-external",
+ "-lxcm=trace"
]
}
]
- }
+ },
+ {
+ "bin": "../Acala/target/release/acala",
+ "id": "2000",
+ "chain": "karura-dev",
+ "balance": "1000000000000000000000",
+ "nodes": [
+ {
+ "wsPort": 9946,
+ "port": 31202,
+ "name": "alice",
+ "flags": [
+ "--rpc-cors=all",
+ "--unsafe-rpc-external",
+ "--unsafe-ws-external",
+ "-lxcm=trace"
+ ]
+ }
+ ]
+ }
],
"simpleParachains": [],
- "hrmpChannels": [],
+ "hrmpChannels": [
+ {
+ "sender": 2000,
+ "recipient": 1000,
+ "maxCapacity": 8,
+ "maxMessageSize": 512
+ },
+ {
+ "sender": 1000,
+ "recipient": 2000,
+ "maxCapacity": 8,
+ "maxMessageSize": 512
+ }
+ ],
"finalization": false
}
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -23,7 +23,8 @@
use sp_runtime::{
Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,
traits::{
- AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,
+ AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,
+ AccountIdConversion, Zero,
},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, MultiSignature, RuntimeAppPublic,
@@ -45,8 +46,9 @@
dispatch::DispatchResult,
PalletId, parameter_types, StorageValue, ConsensusEngineId,
traits::{
- Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem,
- LockIdentifier, OnUnbalanced, Randomness, FindAuthor,
+ tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
+ Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
+ OnUnbalanced, Randomness, FindAuthor,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -69,8 +71,9 @@
use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};
use fp_rpc::TransactionStatus;
use sp_runtime::{
- traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},
+ traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
transaction_validity::TransactionValidityError,
+ SaturatedConversion,
};
// pub use pallet_timestamp::Call as TimestampCall;
@@ -82,12 +85,23 @@
use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
- EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,
- ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,
- SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
- SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,
+ EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser,
+ ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
+ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
+};
+use xcm_executor::{Config, XcmExecutor, Assets};
+use sp_std::{marker::PhantomData};
+
+use xcm::latest::{
+ // Xcm,
+ AssetId::{Concrete},
+ Fungibility::Fungible as XcmFungible,
+ MultiAsset,
+ Error as XcmError,
};
-use xcm_executor::{Config, XcmExecutor};
+use xcm_executor::traits::{MatchesFungible, WeightTrader};
+//use xcm_executor::traits::MatchesFungible;
+use sp_runtime::traits::CheckedConversion;
// mod chain_extension;
// use crate::chain_extension::{NFTExtension, Imbalance};
@@ -616,12 +630,22 @@
AccountId32Aliases<RelayNetwork, AccountId>,
);
+pub struct OnlySelfCurrency;
+impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {
+ fn matches_fungible(a: &MultiAsset) -> Option<B> {
+ match (&a.id, &a.fun) {
+ (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount),
+ _ => None,
+ }
+ }
+}
+
/// Means for transacting assets on this chain.
pub type LocalAssetTransactor = CurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
- IsConcrete<RelayLocation>,
+ OnlySelfCurrency,
// Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
@@ -677,6 +701,88 @@
// ^^^ Parent & its unit plurality gets free execution
);
+pub struct UsingOnlySelfCurrencyComponents<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+>(
+ Weight,
+ Currency::Balance,
+ PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,
+);
+impl<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+ > WeightTrader
+ for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+ fn new() -> Self {
+ Self(0, Zero::zero(), PhantomData)
+ }
+
+ fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
+ let amount = WeightToFee::calc(&weight);
+ let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;
+
+ // location to this parachain through relay chain
+ let option1: xcm::v1::AssetId = Concrete(MultiLocation {
+ parents: 1,
+ interior: X1(Parachain(ParachainInfo::parachain_id().into())),
+ });
+ // direct location
+ let option2: xcm::v1::AssetId = Concrete(MultiLocation {
+ parents: 0,
+ interior: Here,
+ });
+
+ let required = if payment.fungible.contains_key(&option1) {
+ (option1, u128_amount).into()
+ } else if payment.fungible.contains_key(&option2) {
+ (option2, u128_amount).into()
+ } else {
+ (Concrete(MultiLocation::default()), u128_amount).into()
+ };
+
+ let unused = payment
+ .checked_sub(required)
+ .map_err(|_| XcmError::TooExpensive)?;
+ self.0 = self.0.saturating_add(weight);
+ self.1 = self.1.saturating_add(amount);
+ Ok(unused)
+ }
+
+ fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
+ let weight = weight.min(self.0);
+ let amount = WeightToFee::calc(&weight);
+ self.0 -= weight;
+ self.1 = self.1.saturating_sub(amount);
+ let amount: u128 = amount.saturated_into();
+ if amount > 0 {
+ Some((AssetId::get(), amount).into())
+ } else {
+ None
+ }
+ }
+}
+impl<
+ WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
+ AssetId: Get<MultiLocation>,
+ AccountId,
+ Currency: CurrencyT<AccountId>,
+ OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
+ > Drop
+ for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+{
+ fn drop(&mut self) {
+ OnUnbalanced::on_unbalanced(Currency::issue(self.1));
+ }
+}
+
pub struct XcmConfig;
impl Config for XcmConfig {
type Call = Call;
@@ -689,7 +795,13 @@
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
- type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;
+ type Trader = UsingOnlySelfCurrencyComponents<
+ IdentityFee<Balance>,
+ RelayLocation,
+ AccountId,
+ Balances,
+ (),
+ >;
type ResponseHandler = (); // Don't handle responses for now.
type SubscriptionService = PolkadotXcm;
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -64,6 +64,7 @@
"testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",
"testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",
"testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",
+ "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",
"testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
"testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
"testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",
tests/src/xcmTransfer.test.tsdiffbeforeafterbothno changes