From 696850df2a5c99a7c64ea95277956a6c77307bc8 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sat, 27 Apr 2024 11:07:05 +0200 Subject: [PATCH] Handle incoming relay chain asset transfers (#99) * MultiCurrency Asset Transactor * fee payment with rc token * prettier * handle unknown tokens * fmt * some improvements * fix block-production.zndsl * upgrade version of pallet-collator-selection --------- Co-authored-by: cuteolaf --- Cargo.lock | 33 +++++++ Cargo.toml | 4 +- README.md | 23 ++++- e2e_tests/custom-fee-payment.js | 87 +++++++++++++++---- node/src/chain_spec.rs | 4 +- runtime/regionx/Cargo.toml | 4 + runtime/regionx/src/lib.rs | 5 ++ runtime/regionx/src/xcm_config.rs | 73 ++++++++++++---- zombienet_tests/0001-block-production.zndsl | 2 +- zombienet_tests/0003-custom-fee-payment.toml | 4 +- zombienet_tests/0003-custom-fee-payment.zndsl | 6 +- 11 files changed, 197 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7796cb9..8e7052c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5980,6 +5980,22 @@ dependencies = [ "staging-xcm", ] +[[package]] +name = "orml-unknown-tokens" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c26e74a7407bd6bccc8d123a4f6d60e5b01188c135804dc1e48ca98c49dbf6" +dependencies = [ + "frame-support", + "frame-system", + "orml-xcm-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "staging-xcm", +] + [[package]] name = "orml-utilities" version = "0.7.0" @@ -5996,6 +6012,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "orml-xcm-support" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb61790b9ce5698f7026bbf9ebd932df782a425b3551e9dd04300cb4eace179" +dependencies = [ + "frame-support", + "orml-traits", + "parity-scale-codec", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", +] + [[package]] name = "pallet-asset-rate" version = "7.0.0" @@ -9412,6 +9443,8 @@ dependencies = [ "orml-currencies", "orml-tokens", "orml-traits", + "orml-unknown-tokens", + "orml-xcm-support", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-aura", diff --git a/Cargo.toml b/Cargo.toml index a1e9c22e..a0a01304 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ cumulus-pallet-xcmp-queue = { version = "0.7.0", default-features = false } cumulus-primitives-core = { version = "0.7.0", default-features = false } cumulus-primitives-timestamp = { version = "0.7.0", default-features = false } cumulus-primitives-utility = { version = "0.7.3", default-features = false } -pallet-collator-selection = { version = "9.0.0", default-features = false } +pallet-collator-selection = { version = "9.0.2", default-features = false } parachain-info = { version = "0.7.0", package = "staging-parachain-info", default-features = false } parachains-common = { version = "7.0.0", default-features = false } sp-timestamp = { version = "26.0.0", default-features = false } @@ -141,6 +141,8 @@ orml-asset-registry = { version = "0.7.0", default-features = false } orml-currencies = { version = "0.7.0", default-features = false } orml-tokens = { version = "0.7.0", default-features = false } orml-traits = { version = "0.7.0", default-features = false } +orml-unknown-tokens = { version = "0.7.0", default-features = false } +orml-xcm-support = { version = "0.7.0", default-features = false } # Polytope Labs ismp = { git="https://github.com/Szegoo/hyperbridge.git", branch="fix-to-string", default-features = false } diff --git a/README.md b/README.md index 19a4f421..42604cb2 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,23 @@ export PATH=/home//RegionX-Node/:$PATH ``` -4. Run the test: +4. Run the tests: + + - block production - ``` - zombienet-linux -p native test ./zombienet_tests/0001-smoke-test.zndsl - ``` + + ``` + zombienet-linux -p native test ./zombienet_tests/0001-block-production.zndsl + ``` + + - native fee payment + + ``` + zombienet-linux -p native test ./zombienet_tests/0002-native-fee-payment.zndsl + ``` + + - custom fee payment + + ``` + zombienet-linux -p native test ./zombienet_tests/0003-custom-fee-payment.zndsl + ``` \ No newline at end of file diff --git a/e2e_tests/custom-fee-payment.js b/e2e_tests/custom-fee-payment.js index 95c52c01..9e1c058b 100644 --- a/e2e_tests/custom-fee-payment.js +++ b/e2e_tests/custom-fee-payment.js @@ -1,12 +1,17 @@ -const { ApiPromise, WsProvider } = require("@polkadot/api"); +const { ApiPromise, WsProvider, Keyring } = require("@polkadot/api"); const { submitExtrinsic } = require("./common"); -const ASSET_ID = 42; +const RELAY_ASSET_ID = 1; async function run(nodeName, networkInfo, _jsArgs) { - const { wsUri } = networkInfo.nodesByName[nodeName]; - const api = await ApiPromise.create({ - provider: new WsProvider(wsUri), + const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName]; + const { wsUri: rococoUri } = networkInfo.nodesByName["rococo-validator01"]; + + const rococoApi = await ApiPromise.create({ + provider: new WsProvider(rococoUri), + }); + const regionXApi = await ApiPromise.create({ + provider: new WsProvider(regionXUri), signedExtensions: { ChargeAssetTxPayment: { extrinsic: { @@ -22,27 +27,73 @@ async function run(nodeName, networkInfo, _jsArgs) { const keyring = new zombie.Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); + const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); + await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {}); + const assetMetadata = { - decimals: 10, - name: "DOT", - symbol: "DOT", - existentialDeposit: 10n**3n, + decimals: 12, + name: "ROC", + symbol: "ROC", + existentialDeposit: 10n ** 3n, location: null, - additional: null + additional: null, }; const assetSetupCalls = [ - api.tx.assetRegistry.registerAsset(assetMetadata, ASSET_ID), - api.tx.assetRate.create(ASSET_ID, 1000000000000000000n), // 1 on 1 - api.tx.tokens.setBalance(alice.address, ASSET_ID, 10n**12n, 0), + regionXApi.tx.assetRegistry.registerAsset(assetMetadata, RELAY_ASSET_ID), + regionXApi.tx.assetRate.create(RELAY_ASSET_ID, 1_000_000_000_000_000_000n), // 1 on 1 + regionXApi.tx.tokens.setBalance( + alice.address, + RELAY_ASSET_ID, + 10n ** 12n, + 0, + ), ]; - const batchCall = api.tx.utility.batch(assetSetupCalls); - const sudo = api.tx.sudo.sudo(batchCall); + const batchCall = regionXApi.tx.utility.batch(assetSetupCalls); + const sudoCall = regionXApi.tx.sudo.sudo(batchCall); + + await submitExtrinsic(alice, sudoCall, {}); - await submitExtrinsic(alice, sudo, {}); + const receiverKeypair = new Keyring(); + receiverKeypair.addFromAddress(alice.address); + + const feeAssetItem = 0; + const weightLimit = "Unlimited"; + const reserveTransfer = rococoApi.tx.xcmPallet.limitedReserveTransferAssets( + { V3: { parents: 0, interior: { X1: { Parachain: 2000 } } } }, //dest + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + chain: "Any", + id: receiverKeypair.pairs[0].publicKey, + }, + }, + }, + }, + }, //beneficiary + { + V3: [ + { + id: { + Concrete: { parents: 0, interior: "Here" }, + }, + fun: { + Fungible: 10n ** 9n, + }, + }, + ], + }, //asset + feeAssetItem, + weightLimit, + ); + await submitExtrinsic(alice, reserveTransfer, {}); - const remarkCall = api.tx.system.remark("0x44"); - await submitExtrinsic(alice, remarkCall, {assetId: ASSET_ID}); + // Try to pay for fees with relay chain asset. + const remarkCall = regionXApi.tx.system.remark("0x44"); + await submitExtrinsic(alice, remarkCall, { assetId: RELAY_ASSET_ID }); } module.exports = { run }; diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index d2ea3b91..9429cff1 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -79,7 +79,7 @@ pub fn session_keys(keys: AuraId) -> regionx_runtime::SessionKeys { pub fn development_config(id: u32) -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "M4X".into()); + properties.insert("tokenSymbol".into(), "REGX".into()); properties.insert("tokenDecimals".into(), 12.into()); // TODO: chose an ss58Format properties.insert("ss58Format".into(), 42.into()); @@ -130,7 +130,7 @@ pub fn development_config(id: u32) -> ChainSpec ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "M4X".into()); + properties.insert("tokenSymbol".into(), "REGX".into()); properties.insert("tokenDecimals".into(), 12.into()); // TODO: chose an ss58Format properties.insert("ss58Format".into(), 42.into()); diff --git a/runtime/regionx/Cargo.toml b/runtime/regionx/Cargo.toml index 2aa5edf7..595d5b3a 100644 --- a/runtime/regionx/Cargo.toml +++ b/runtime/regionx/Cargo.toml @@ -35,6 +35,8 @@ orml-asset-registry = { workspace = true } orml-currencies = { workspace = true } orml-tokens = { workspace = true } orml-traits = { workspace = true } +orml-unknown-tokens = { workspace = true } +orml-xcm-support = { workspace = true } # Substrate frame-benchmarking = { workspace = true, optional = true } @@ -125,6 +127,8 @@ std = [ "orml-currencies/std", "orml-tokens/std", "orml-traits/std", + "orml-unknown-tokens/std", + "orml-xcm-support/std", "pallet-aura/std", "pallet-authorship/std", "pallet-asset-rate/std", diff --git a/runtime/regionx/src/lib.rs b/runtime/regionx/src/lib.rs index b7826898..9322704a 100644 --- a/runtime/regionx/src/lib.rs +++ b/runtime/regionx/src/lib.rs @@ -441,6 +441,10 @@ impl pallet_asset_rate::Config for Runtime { type BenchmarkHelper = (); } +impl orml_unknown_tokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + impl pallet_asset_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Tokens; @@ -709,6 +713,7 @@ construct_runtime!( Tokens: orml_tokens = 14, Currencies: orml_currencies = 15, AssetRate: pallet_asset_rate = 16, + UnknownTokens: orml_unknown_tokens = 17, // Governance Sudo: pallet_sudo = 20, diff --git a/runtime/regionx/src/xcm_config.rs b/runtime/regionx/src/xcm_config.rs index 51f55038..7d55ee28 100644 --- a/runtime/regionx/src/xcm_config.rs +++ b/runtime/regionx/src/xcm_config.rs @@ -14,25 +14,30 @@ // along with RegionX. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId, Balance, Balances, Currencies, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, UnknownTokens, + WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, traits::{ConstU32, Everything, Nothing}, + PalletId, }; use frame_system::EnsureRoot; +use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter}; use pallet_xcm::XcmPassthrough; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; +use regionx_primitives::assets::{REGX_ASSET_ID, RELAY_CHAIN_ASSET_ID}; +use sp_runtime::traits::{AccountIdConversion, Convert}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + FrameTransactionalProcessor, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::XcmExecutor; @@ -55,20 +60,54 @@ pub type LocationToAccountId = ( AccountId32Aliases, ); +parameter_types! { + // The account which receives multi-currency tokens from failed attempts to deposit them + pub Alternative: AccountId = PalletId(*b"xcm/alte").into_account_truncating(); +} + /// Means for transacting assets on this chain. -pub type LocalAssetTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // 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): +pub type FungiblesAssetTransactor = MultiCurrencyAdapter< + Currencies, + UnknownTokens, + IsNativeConcrete, AccountId, - // We don't track any teleports. - (), + LocationToAccountId, + AssetId, + AssetIdConverter, + DepositToAlternative, >; +pub struct AssetIdConverter; +impl Convert> for AssetIdConverter { + fn convert(id: AssetId) -> Option { + match id { + RELAY_CHAIN_ASSET_ID => Some(MultiLocation::parent()), + REGX_ASSET_ID => Some(MultiLocation::here()), + _ => None, + } + } +} + +impl Convert> for AssetIdConverter { + fn convert(location: MultiLocation) -> Option { + match location { + MultiLocation { parents: 1, interior: Here } => Some(RELAY_CHAIN_ASSET_ID), + MultiLocation { parents: 0, interior: Here } => Some(REGX_ASSET_ID), + _ => None, + } + } +} + +impl Convert> for AssetIdConverter { + fn convert(asset: MultiAsset) -> Option { + if let MultiAsset { id: Concrete(location), .. } = asset { + Self::convert(location) + } else { + None + } + } +} + /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can /// biases the kind of local `Origin` it will become. @@ -127,7 +166,7 @@ impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; + type AssetTransactor = FungiblesAssetTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = (); // Teleporting is disabled. diff --git a/zombienet_tests/0001-block-production.zndsl b/zombienet_tests/0001-block-production.zndsl index 2153b9d7..30f2bec5 100644 --- a/zombienet_tests/0001-block-production.zndsl +++ b/zombienet_tests/0001-block-production.zndsl @@ -1,5 +1,5 @@ Description: Block production smoke test -Network: ./0001-smoke-test.toml +Network: ./0001-block-production.toml Creds: config alice: is up diff --git a/zombienet_tests/0003-custom-fee-payment.toml b/zombienet_tests/0003-custom-fee-payment.toml index c74fbef6..c1ad77e6 100644 --- a/zombienet_tests/0003-custom-fee-payment.toml +++ b/zombienet_tests/0003-custom-fee-payment.toml @@ -6,11 +6,11 @@ chain = "rococo-local" command = "polkadot" [[relaychain.nodes]] - name = "alice" + name = "rococo-validator01" validator = true [[relaychain.nodes]] - name = "bob" + name = "rococo-validator02" validator = true [[parachains]] diff --git a/zombienet_tests/0003-custom-fee-payment.zndsl b/zombienet_tests/0003-custom-fee-payment.zndsl index 36561db3..51b8d605 100644 --- a/zombienet_tests/0003-custom-fee-payment.zndsl +++ b/zombienet_tests/0003-custom-fee-payment.zndsl @@ -2,9 +2,9 @@ Description: Native currency fee payment Network: ./0003-custom-fee-payment.toml Creds: config -alice: is up -bob: is up +rococo-validator01: is up +rococo-validator02: is up -alice: parachain 2000 is registered within 225 seconds +rococo-validator01: parachain 2000 is registered within 225 seconds regionx-collator01: js-script ../e2e_tests/custom-fee-payment.js return is 0 within 400 seconds