From 400f98971b1955059aa1fce982f134031b95cf4c Mon Sep 17 00:00:00 2001 From: NingBo Wang <2536935847@qq.com> Date: Wed, 7 Jun 2023 11:16:39 +0800 Subject: [PATCH] Add xcm action (#982) * Add bifrost xcm-action * Refactor xcm-action * Support xcm-action moonbeam * Match moonbeam account * Add xcm-action to bifrost kusama * Support xcm-action * Add tuples-96 feature * Fix conditional compile * Update * Remove conditional compile * Fix ASTAR MaxWeightInValid * Remove tuples-96 feature and fix bug --- Cargo.lock | 56 ++ node/primitives/src/lib.rs | 19 - node/primitives/src/traits.rs | 4 + pallets/fee-share/src/mock.rs | 2 + pallets/liquidity-mining/src/benchmarking.rs | 3 +- pallets/salp-lite/src/benchmarking.rs | 2 +- pallets/slp/src/benchmarking.rs | 1 - pallets/slp/src/lib.rs | 4 +- pallets/slp/src/mocks/mock.rs | 2 + pallets/slp/src/mocks/mock_kusama.rs | 2 + pallets/system-maker/src/mock.rs | 2 + pallets/system-staking/src/mock.rs | 2 + pallets/ve-minting/src/mock.rs | 2 + pallets/vtoken-minting/src/lib.rs | 24 +- pallets/vtoken-minting/src/mock.rs | 2 + pallets/xcm-action/Cargo.toml | 74 +++ pallets/xcm-action/src/benchmarking.rs | 107 ++++ pallets/xcm-action/src/lib.rs | 634 +++++++++++++++++++ pallets/xcm-action/src/mock.rs | 510 +++++++++++++++ pallets/xcm-action/src/tests.rs | 121 ++++ pallets/xcm-action/src/weights.rs | 404 ++++++++++++ runtime/bifrost-kusama/Cargo.toml | 7 +- runtime/bifrost-kusama/src/lib.rs | 16 + runtime/bifrost-kusama/src/xcm_config.rs | 112 +++- runtime/bifrost-polkadot/Cargo.toml | 11 +- runtime/bifrost-polkadot/src/lib.rs | 17 + runtime/bifrost-polkadot/src/xcm_config.rs | 111 +++- 27 files changed, 2215 insertions(+), 36 deletions(-) create mode 100644 pallets/xcm-action/Cargo.toml create mode 100644 pallets/xcm-action/src/benchmarking.rs create mode 100644 pallets/xcm-action/src/lib.rs create mode 100644 pallets/xcm-action/src/mock.rs create mode 100644 pallets/xcm-action/src/tests.rs create mode 100644 pallets/xcm-action/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index d8f32930e..ad9a6fb4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -870,6 +870,7 @@ dependencies = [ "bifrost-vsbond-auction", "bifrost-vstoken-conversion", "bifrost-vtoken-minting", + "bifrost-xcm-action", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -937,6 +938,7 @@ dependencies = [ "sp-consensus-aura", "sp-core", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", @@ -1089,6 +1091,7 @@ dependencies = [ "bifrost-vesting", "bifrost-vstoken-conversion", "bifrost-vtoken-minting", + "bifrost-xcm-action", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -1150,6 +1153,7 @@ dependencies = [ "sp-consensus-aura", "sp-core", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", @@ -1581,6 +1585,41 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "bifrost-xcm-action" +version = "0.8.0" +dependencies = [ + "bifrost-asset-registry", + "bifrost-runtime-common", + "bifrost-slp", + "bifrost-vtoken-minting", + "cumulus-pallet-xcm", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "node-primitives", + "orml-currencies", + "orml-tokens", + "orml-traits", + "orml-xtokens", + "pallet-balances", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", + "xcm-interface", + "xcm-simulator", + "zenlink-protocol", +] + [[package]] name = "bincode" version = "1.3.3" @@ -14826,6 +14865,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "xcm-simulator" +version = "0.9.38" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.38#097ffd245c42aeff28cf80f8a3568e1bee2e7da7" +dependencies = [ + "frame-support", + "parity-scale-codec", + "paste", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-runtime-parachains", + "sp-io", + "sp-std", + "xcm", + "xcm-executor", +] + [[package]] name = "yamux" version = "0.10.2" diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 92b750f6f..4d4109710 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -222,22 +222,3 @@ impl Default for RedeemType { Self::Native } } - -impl RedeemType { - #[cfg(feature = "with-bifrost-kusama-runtime")] - pub fn get_parachain_id(self) -> u32 { - match self { - RedeemType::Native => 2001, - RedeemType::Astar => 2007, - RedeemType::Moonbeam(_) => 2023, - } - } - #[cfg(not(feature = "with-bifrost-kusama-runtime"))] - pub fn get_parachain_id(self) -> u32 { - match self { - RedeemType::Native => 2030, - RedeemType::Astar => 2006, - RedeemType::Moonbeam(_) => 2004, - } - } -} diff --git a/node/primitives/src/traits.rs b/node/primitives/src/traits.rs index e3249b4a7..18a043406 100644 --- a/node/primitives/src/traits.rs +++ b/node/primitives/src/traits.rs @@ -146,6 +146,8 @@ pub trait VtokenMintingOperator { currency_id: CurrencyId, index: u32, ) -> Option<(AccountId, Balance, TimeUnit, RedeemType)>; + fn get_astar_parachain_id() -> u32; + fn get_moonbeam_parachain_id() -> u32; } /// Trait for Vtoken-Minting module to check whether accept redeeming or not. @@ -328,6 +330,8 @@ pub trait VtokenMintingInterface { fn vtoken_id(token_id: CurrencyId) -> Option; fn token_id(vtoken_id: CurrencyId) -> Option; fn get_minimums_redeem(vtoken_id: CurrencyId) -> Balance; + fn get_astar_parachain_id() -> u32; + fn get_moonbeam_parachain_id() -> u32; } pub trait TryConvertFrom { diff --git a/pallets/fee-share/src/mock.rs b/pallets/fee-share/src/mock.rs index b83fa0516..0c7661103 100644 --- a/pallets/fee-share/src/mock.rs +++ b/pallets/fee-share/src/mock.rs @@ -355,6 +355,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } parameter_types! { diff --git a/pallets/liquidity-mining/src/benchmarking.rs b/pallets/liquidity-mining/src/benchmarking.rs index c9fdce38b..b44bcdf8c 100644 --- a/pallets/liquidity-mining/src/benchmarking.rs +++ b/pallets/liquidity-mining/src/benchmarking.rs @@ -19,8 +19,7 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg(feature = "runtime-benchmarks")] use frame_benchmarking::{ - account, benchmarks_instance_pallet, impl_benchmark_test_suite, v1::BenchmarkError, - whitelisted_caller, + account, benchmarks_instance_pallet, impl_benchmark_test_suite, whitelisted_caller, }; use frame_support::{ assert_ok, diff --git a/pallets/salp-lite/src/benchmarking.rs b/pallets/salp-lite/src/benchmarking.rs index 878e4d518..28fc05aac 100644 --- a/pallets/salp-lite/src/benchmarking.rs +++ b/pallets/salp-lite/src/benchmarking.rs @@ -18,7 +18,7 @@ // Ensure we're `no_std` when compiling for Wasm. #[cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::assert_ok; use frame_system::RawOrigin; use node_primitives::ParaId; diff --git a/pallets/slp/src/benchmarking.rs b/pallets/slp/src/benchmarking.rs index 964d21de8..7141c7f5f 100644 --- a/pallets/slp/src/benchmarking.rs +++ b/pallets/slp/src/benchmarking.rs @@ -23,7 +23,6 @@ use frame_benchmarking::{account, benchmarks, v1::BenchmarkError, whitelisted_ca use frame_support::{assert_ok, dispatch::UnfilteredDispatchable}; use frame_system::RawOrigin; use sp_runtime::traits::{AccountIdConversion, StaticLookup, UniqueSaturatedFrom}; -use xcm::v3::prelude::*; #[allow(unused_imports)] pub use crate::{Pallet as Slp, *}; diff --git a/pallets/slp/src/lib.rs b/pallets/slp/src/lib.rs index 62784a1b7..e900dcf29 100644 --- a/pallets/slp/src/lib.rs +++ b/pallets/slp/src/lib.rs @@ -1242,7 +1242,7 @@ pub mod pallet { let dest = MultiLocation { parents: 1, interior: X2( - Parachain(redeem_type.get_parachain_id()), + Parachain(T::VtokenMinting::get_astar_parachain_id()), AccountId32 { network: None, id: user_account.encode().try_into().unwrap(), @@ -1261,7 +1261,7 @@ pub mod pallet { let dest = MultiLocation { parents: 1, interior: X2( - Parachain(redeem_type.get_parachain_id()), + Parachain(T::VtokenMinting::get_moonbeam_parachain_id()), AccountKey20 { network: None, key: evm_caller.to_fixed_bytes(), diff --git a/pallets/slp/src/mocks/mock.rs b/pallets/slp/src/mocks/mock.rs index 9aced4ab6..0d5130c84 100644 --- a/pallets/slp/src/mocks/mock.rs +++ b/pallets/slp/src/mocks/mock.rs @@ -229,6 +229,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } parameter_types! { diff --git a/pallets/slp/src/mocks/mock_kusama.rs b/pallets/slp/src/mocks/mock_kusama.rs index 3179b9237..da76ab596 100644 --- a/pallets/slp/src/mocks/mock_kusama.rs +++ b/pallets/slp/src/mocks/mock_kusama.rs @@ -241,6 +241,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } parameter_types! { diff --git a/pallets/system-maker/src/mock.rs b/pallets/system-maker/src/mock.rs index aa94aa3dd..204507cfb 100644 --- a/pallets/system-maker/src/mock.rs +++ b/pallets/system-maker/src/mock.rs @@ -362,6 +362,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } parameter_types! { diff --git a/pallets/system-staking/src/mock.rs b/pallets/system-staking/src/mock.rs index 6b0ccf647..43c54a823 100644 --- a/pallets/system-staking/src/mock.rs +++ b/pallets/system-staking/src/mock.rs @@ -222,6 +222,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } ord_parameter_types! { diff --git a/pallets/ve-minting/src/mock.rs b/pallets/ve-minting/src/mock.rs index 02933100a..10aa34b80 100644 --- a/pallets/ve-minting/src/mock.rs +++ b/pallets/ve-minting/src/mock.rs @@ -242,6 +242,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } ord_parameter_types! { diff --git a/pallets/vtoken-minting/src/lib.rs b/pallets/vtoken-minting/src/lib.rs index 43ec8fd2f..7397a0963 100644 --- a/pallets/vtoken-minting/src/lib.rs +++ b/pallets/vtoken-minting/src/lib.rs @@ -117,6 +117,12 @@ pub mod pallet { #[pallet::constant] type RelayChainToken: Get; + #[pallet::constant] + type AstarParachainId: Get; + + #[pallet::constant] + type MoonbeamParachainId: Get; + type BifrostSlp: SlpOperator; type CurrencyIdConversion: CurrencyIdConversion; @@ -920,7 +926,7 @@ pub mod pallet { let dest = MultiLocation { parents: 1, interior: X2( - Parachain(redeem_type.get_parachain_id()), + Parachain(T::AstarParachainId::get()), AccountId32 { network: None, id: account.encode().try_into().unwrap(), @@ -939,7 +945,7 @@ pub mod pallet { let dest = MultiLocation { parents: 1, interior: X2( - Parachain(redeem_type.get_parachain_id()), + Parachain(T::MoonbeamParachainId::get()), AccountKey20 { network: None, key: evm_caller.to_fixed_bytes() }, ), }; @@ -1505,6 +1511,13 @@ impl VtokenMintingOperator, AccountIdOf, ) -> Option<(AccountIdOf, BalanceOf, TimeUnit, RedeemType)> { Self::token_unlock_ledger(currency_id, index) } + + fn get_astar_parachain_id() -> u32 { + T::AstarParachainId::get() + } + fn get_moonbeam_parachain_id() -> u32 { + T::MoonbeamParachainId::get() + } } impl VtokenMintingInterface, CurrencyIdOf, BalanceOf> @@ -1562,4 +1575,11 @@ impl VtokenMintingInterface, CurrencyIdOf, BalanceO fn get_minimums_redeem(vtoken_id: CurrencyIdOf) -> BalanceOf { MinimumRedeem::::get(vtoken_id) } + + fn get_astar_parachain_id() -> u32 { + T::AstarParachainId::get() + } + fn get_moonbeam_parachain_id() -> u32 { + T::MoonbeamParachainId::get() + } } diff --git a/pallets/vtoken-minting/src/mock.rs b/pallets/vtoken-minting/src/mock.rs index 214738f81..76e2fd519 100644 --- a/pallets/vtoken-minting/src/mock.rs +++ b/pallets/vtoken-minting/src/mock.rs @@ -249,6 +249,8 @@ impl vtoken_minting::Config for Runtime { type WeightInfo = (); type OnRedeemSuccess = (); type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; } ord_parameter_types! { diff --git a/pallets/xcm-action/Cargo.toml b/pallets/xcm-action/Cargo.toml new file mode 100644 index 000000000..1f789c05d --- /dev/null +++ b/pallets/xcm-action/Cargo.toml @@ -0,0 +1,74 @@ +[package] +name = "bifrost-xcm-action" +description = "A pallet to manage the execution of XCM messages" +authors = ["hqwangningbo <2536935847@qq.com>"] +edition = "2021" +version = "0.8.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +hex-literal = "0.3.4" +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0.152", default-features = false, features = ["derive"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38",default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38",default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38",default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38",default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38",default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.38",default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38", default-features = false } +orml-traits = { version = "0.4.1-dev", default-features = false } +orml-xtokens = { version = "0.4.1-dev", default-features = false } +zenlink-protocol = { version = "*", default-features = false } + +node-primitives = { path = "../../node/primitives", default-features = false } +xcm-interface = { path = "../xcm-interface", default-features = false } +bifrost-asset-registry = { path = "../asset-registry", default-features = false } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } +bifrost-vtoken-minting = { path = "../vtoken-minting" } +bifrost-slp = { path = "../slp" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" } +orml-tokens = "0.4.1-dev" +orml-currencies = "0.4.1-dev" +xcm-executor = {git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38" } +pallet-xcm = {git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38" } +xcm-builder = {git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38" } +xcm-simulator = {git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.38" } +bifrost-runtime-common = { package = "bifrost-runtime-common", path = "../../runtime/common", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde/std", + "zenlink-protocol/std", + "orml-traits/std", + "orml-xtokens/std", + "node-primitives/std", + "cumulus-primitives-core/std", + "sp-std/std", + "bifrost-slp/std", + "bifrost-runtime-common/std", + "cumulus-pallet-xcm/std", + "sp-core/std", + "sp-runtime/std", + "bifrost-asset-registry/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] +with-bifrost-polkadot-runtime = [] diff --git a/pallets/xcm-action/src/benchmarking.rs b/pallets/xcm-action/src/benchmarking.rs new file mode 100644 index 000000000..0ebfd565c --- /dev/null +++ b/pallets/xcm-action/src/benchmarking.rs @@ -0,0 +1,107 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#![cfg(feature = "runtime-benchmarks")] + +use crate::{Pallet as XcmAction, *}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller, BenchmarkError}; +use frame_support::{assert_ok, sp_runtime::traits::UniqueSaturatedFrom, traits::EnsureOrigin}; +use frame_system::RawOrigin; +use node_primitives::{CurrencyId, TokenSymbol}; + +benchmarks! { + add_whitelist { + let origin = ::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let contract: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Root,SupportChain::Astar, contract) + + remove_whitelist { + let origin = ::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let contract: T::AccountId = whitelisted_caller(); + assert_ok!(XcmAction::::add_whitelist( + origin, + SupportChain::Astar, + contract.clone() + )); + }: _(RawOrigin::Root,SupportChain::Astar, contract) + + set_execution_fee { + let origin = ::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let contract: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Root,CurrencyId::Token2(0), 10u32.into()) + + set_transfer_to_fee { + let origin = ::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let contract: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Root,SupportChain::Astar, 10u32.into()) + + + mint { + let contract: T::AccountId = whitelisted_caller(); + assert_ok!(XcmAction::::add_whitelist( + RawOrigin::Root.into(), + SupportChain::Astar, + contract.clone() + )); + assert_ok!(XcmAction::::set_execution_fee( + RawOrigin::Root.into(), + CurrencyId::Native(TokenSymbol::BNC), + 0u32.into() + )); + let addr: [u8; 20] = hex_literal::hex!["3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"].into(); + let receiver = H160::from(addr); + let evm_caller_account_id = XcmAction::::h160_to_account_id(receiver); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &evm_caller_account_id, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + }: _(RawOrigin::Signed(contract), receiver, CurrencyId::Native(TokenSymbol::BNC), SupportChain::Astar) + + redeem { + let contract: T::AccountId = whitelisted_caller(); + assert_ok!(XcmAction::::add_whitelist( + RawOrigin::Root.into(), + SupportChain::Astar, + contract.clone() + )); + assert_ok!(XcmAction::::set_execution_fee( + RawOrigin::Root.into(), + CurrencyId::VToken(TokenSymbol::BNC), + 0u32.into() + )); + let addr: [u8; 20] = hex_literal::hex!["3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"].into(); + let receiver = H160::from(addr); + let evm_caller_account_id = XcmAction::::h160_to_account_id(receiver); + T::MultiCurrency::deposit(CurrencyId::VToken(TokenSymbol::BNC), &evm_caller_account_id, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + }: _(RawOrigin::Signed(contract), receiver, CurrencyId::VToken(TokenSymbol::BNC), SupportChain::Astar) + + + swap { + let contract: T::AccountId = whitelisted_caller(); + assert_ok!(XcmAction::::add_whitelist( + RawOrigin::Root.into(), + SupportChain::Astar, + contract.clone() + )); + assert_ok!(XcmAction::::set_execution_fee( + RawOrigin::Root.into(), + CurrencyId::Native(TokenSymbol::BNC), + 0u32.into() + )); + let addr: [u8; 20] = hex_literal::hex!["3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"].into(); + let receiver = H160::from(addr); + let evm_caller_account_id = XcmAction::::h160_to_account_id(receiver); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &evm_caller_account_id, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + }: _(RawOrigin::Signed(contract), receiver, CurrencyId::Native(TokenSymbol::BNC),CurrencyId::VToken(TokenSymbol::BNC), 0u32.into(),SupportChain::Astar) +} diff --git a/pallets/xcm-action/src/lib.rs b/pallets/xcm-action/src/lib.rs new file mode 100644 index 000000000..7701dd1f6 --- /dev/null +++ b/pallets/xcm-action/src/lib.rs @@ -0,0 +1,634 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +use bifrost_asset_registry::AssetMetadata; +use codec::{Decode, Encode, MaxEncodedLen}; +use cumulus_primitives_core::ParaId; +use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo}, + ensure, + sp_runtime::SaturatedConversion, + traits::Get, + RuntimeDebug, +}; +use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use node_primitives::{ + CurrencyId, CurrencyIdMapping, TokenSymbol, TryConvertFrom, VtokenMintingInterface, +}; +use orml_traits::{MultiCurrency, XcmTransfer}; +pub use pallet::*; +use scale_info::TypeInfo; +use sp_core::{Hasher, H160}; +use sp_runtime::{traits::BlakeTwo256, DispatchError, Saturating}; +use sp_std::vec; +use xcm::{latest::prelude::*, v3::MultiLocation}; +use zenlink_protocol::AssetBalance; + +pub mod weights; +pub use weights::WeightInfo; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub type AccountIdOf = ::AccountId; +pub type CurrencyIdOf = <::MultiCurrency as MultiCurrency< + ::AccountId, +>>::CurrencyId; +pub type BalanceOf = <::MultiCurrency as MultiCurrency>>::Balance; + +#[derive( + Encode, + Decode, + MaxEncodedLen, + Eq, + PartialEq, + Copy, + Clone, + RuntimeDebug, + PartialOrd, + Ord, + TypeInfo, +)] +pub enum SupportChain { + Astar, + Moonbeam, +} + +#[derive( + Encode, + Decode, + MaxEncodedLen, + Eq, + PartialEq, + Copy, + Clone, + RuntimeDebug, + PartialOrd, + Ord, + TypeInfo, +)] +pub enum TargetChain { + Astar(AccountId), + Moonbeam(H160), +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::{ValueQuery, *}; + use node_primitives::RedeemType; + use zenlink_protocol::{AssetId, ExportZenlink}; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type ControlOrigin: EnsureOrigin<::RuntimeOrigin>; + type MultiCurrency: MultiCurrency, CurrencyId = CurrencyId>; + + type DexOperator: ExportZenlink; + + /// The interface to call VtokenMinting module functions. + type VtokenMintingInterface: VtokenMintingInterface< + AccountIdOf, + CurrencyIdOf, + BalanceOf, + >; + + /// xtokens xcm transfer interface + type XcmTransfer: XcmTransfer, BalanceOf, CurrencyIdOf>; + + /// Convert MultiLocation to `T::CurrencyId`. + type CurrencyIdConvert: CurrencyIdMapping< + CurrencyId, + MultiLocation, + AssetMetadata>, + >; + + /// TreasuryAccount + #[pallet::constant] + type TreasuryAccount: Get>; + + #[pallet::constant] + type ParachainId: Get; + + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + AddWhitelistAccountId { + support_chain: SupportChain, + evm_contract_account_id: AccountIdOf, + }, + RemoveWhitelistAccountId { + support_chain: SupportChain, + evm_contract_account_id: AccountIdOf, + }, + XcmMint { + evm_caller: H160, + currency_id: CurrencyIdOf, + token_amount: BalanceOf, + support_chain: SupportChain, + }, + XcmMintFailed { + evm_caller: H160, + currency_id: CurrencyIdOf, + token_amount: BalanceOf, + support_chain: SupportChain, + }, + XcmSwap { + evm_caller: H160, + currency_id_in: CurrencyIdOf, + currency_id_out: CurrencyIdOf, + support_chain: SupportChain, + }, + XcmSwapFailed { + evm_caller: H160, + currency_id_in: CurrencyIdOf, + currency_id_out: CurrencyIdOf, + support_chain: SupportChain, + }, + XcmRedeem { + evm_caller: H160, + vtoken_id: CurrencyIdOf, + vtoken_amount: BalanceOf, + support_chain: SupportChain, + }, + XcmRedeemFailed { + evm_caller: H160, + vtoken_id: CurrencyIdOf, + vtoken_amount: BalanceOf, + support_chain: SupportChain, + }, + SetTransferToFee { + support_chain: SupportChain, + transfer_to_fee: BalanceOf, + }, + SetExecutionFee { + currency_id: CurrencyId, + execution_fee: BalanceOf, + }, + } + + #[pallet::error] + pub enum Error { + /// Token not found in vtoken minting + TokenNotFoundInVtokenMinting, + /// Token not found in zenlink + TokenNotFoundInZenlink, + /// Contract Account already exists in the whitelist + AccountIdAlreadyInWhitelist, + /// Contract Account is not in the whitelist + AccountIdNotInWhitelist, + /// The maximum number of whitelist addresses is 10 + ExceededWhitelistMaxNumber, + /// Execution fee not set + NotSetExecutionFee, + /// Insufficient balance to execute the fee + FreeBalanceTooLow, + } + + /// Contract whitelist + #[pallet::storage] + #[pallet::getter(fn whitelist_account_ids)] + pub type WhitelistAccountId = StorageMap< + _, + Blake2_128Concat, + SupportChain, + BoundedVec, ConstU32<10>>, + ValueQuery, + >; + + /// Charge corresponding fees for different CurrencyId + #[pallet::storage] + #[pallet::getter(fn execution_fee)] + pub type ExecutionFee = + StorageMap<_, Blake2_128Concat, CurrencyId, BalanceOf, OptionQuery>; + + /// XCM fee for transferring to Moonbeam(BNC) + #[pallet::storage] + #[pallet::getter(fn transfer_to_fee)] + pub type TransferToFee = + StorageMap<_, Blake2_128Concat, SupportChain, BalanceOf, OptionQuery>; + + #[pallet::call] + impl Pallet { + /// vtoken mint and transfer to target chain + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::mint())] + pub fn mint( + origin: OriginFor, + evm_caller: H160, + currency_id: CurrencyIdOf, + support_chain: SupportChain, + ) -> DispatchResultWithPostInfo { + let evm_contract_account_id = ensure_signed(origin)?; + let evm_caller_account_id = Self::h160_to_account_id(evm_caller); + Self::ensure_singer_on_whitelist(&evm_contract_account_id, support_chain)?; + + let target_chain = Self::match_support_chain( + support_chain, + evm_caller_account_id.clone(), + evm_caller, + )?; + + let token_amount = Self::charge_execution_fee(currency_id, &evm_caller_account_id)?; + + match T::VtokenMintingInterface::mint( + evm_caller_account_id.clone(), + currency_id, + token_amount, + ) { + Ok(_) => { + // success + let vtoken_id = T::VtokenMintingInterface::vtoken_id(currency_id) + .ok_or(Error::::TokenNotFoundInVtokenMinting)?; + let vtoken_amount = + T::MultiCurrency::free_balance(vtoken_id, &evm_caller_account_id); + + Self::transfer_to( + evm_caller_account_id.clone(), + &evm_contract_account_id, + vtoken_id, + vtoken_amount, + target_chain, + )?; + + Self::deposit_event(Event::XcmMint { + evm_caller, + currency_id, + token_amount, + support_chain, + }); + }, + Err(_) => { + Self::transfer_to( + evm_caller_account_id.clone(), + &evm_contract_account_id, + currency_id, + token_amount, + target_chain, + )?; + Self::deposit_event(Event::XcmMintFailed { + evm_caller, + currency_id, + token_amount, + support_chain, + }); + }, + }; + Ok(().into()) + } + + /// Swap and transfer to target chain + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::swap())] + pub fn swap( + origin: OriginFor, + evm_caller: H160, + currency_id_in: CurrencyIdOf, + currency_id_out: CurrencyIdOf, + currency_id_out_min: AssetBalance, + support_chain: SupportChain, + ) -> DispatchResultWithPostInfo { + let evm_contract_account_id = ensure_signed(origin)?; + let evm_caller_account_id = Self::h160_to_account_id(evm_caller); + Self::ensure_singer_on_whitelist(&evm_contract_account_id, support_chain)?; + + let target_chain = Self::match_support_chain( + support_chain, + evm_caller_account_id.clone(), + evm_caller, + )?; + + let in_asset_id: AssetId = + AssetId::try_convert_from(currency_id_in, T::ParachainId::get().into()) + .map_err(|_| Error::::TokenNotFoundInZenlink)?; + let out_asset_id: AssetId = + AssetId::try_convert_from(currency_id_out, T::ParachainId::get().into()) + .map_err(|_| Error::::TokenNotFoundInZenlink)?; + + let currency_id_in_amount = + Self::charge_execution_fee(currency_id_in, &evm_caller_account_id)?; + + let path = vec![in_asset_id, out_asset_id]; + match T::DexOperator::inner_swap_exact_assets_for_assets( + &evm_caller_account_id, + currency_id_in_amount.saturated_into(), + currency_id_out_min, + &path, + &evm_caller_account_id, + ) { + Ok(_) => { + let currency_id_out_amount = + T::MultiCurrency::free_balance(currency_id_out, &evm_caller_account_id); + + Self::transfer_to( + evm_caller_account_id.clone(), + &evm_contract_account_id, + currency_id_out, + currency_id_out_amount, + target_chain, + )?; + + Self::deposit_event(Event::XcmSwap { + evm_caller, + currency_id_in, + currency_id_out, + support_chain, + }); + }, + Err(_) => Self::transfer_to( + evm_caller_account_id.clone(), + &evm_contract_account_id, + currency_id_in, + currency_id_in_amount, + target_chain, + )?, + } + Ok(().into()) + } + + /// Redeem + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::redeem())] + pub fn redeem( + origin: OriginFor, + evm_caller: H160, + vtoken_id: CurrencyIdOf, + support_chain: SupportChain, + ) -> DispatchResultWithPostInfo { + let evm_contract_account_id = ensure_signed(origin)?; + let evm_caller_account_id = Self::h160_to_account_id(evm_caller); + Self::ensure_singer_on_whitelist(&evm_contract_account_id, support_chain)?; + + let target_chain = Self::match_support_chain( + support_chain, + evm_caller_account_id.clone(), + evm_caller, + )?; + + let vtoken_amount = Self::charge_execution_fee(vtoken_id, &evm_caller_account_id)?; + + let redeem_type = match support_chain { + SupportChain::Astar => RedeemType::Astar, + SupportChain::Moonbeam => RedeemType::Moonbeam(evm_caller), + }; + + match T::VtokenMintingInterface::xcm_action_redeem( + evm_caller_account_id.clone(), + vtoken_id, + vtoken_amount, + redeem_type, + ) { + Ok(_) => Self::deposit_event(Event::XcmRedeem { + evm_caller, + vtoken_id, + vtoken_amount, + support_chain, + }), + Err(_) => { + Self::transfer_to( + evm_caller_account_id.clone(), + &evm_contract_account_id, + vtoken_id, + vtoken_amount, + target_chain, + )?; + Self::deposit_event(Event::XcmRedeemFailed { + evm_caller, + vtoken_id, + vtoken_amount, + support_chain, + }); + }, + }; + Ok(().into()) + } + + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::add_whitelist())] + pub fn add_whitelist( + origin: OriginFor, + support_chain: SupportChain, + evm_contract_account_id: T::AccountId, + ) -> DispatchResultWithPostInfo { + // Check the validity of origin + T::ControlOrigin::ensure_origin(origin)?; + + let mut whitelist_account_ids = WhitelistAccountId::::get(&support_chain); + + ensure!( + !whitelist_account_ids.contains(&evm_contract_account_id), + Error::::AccountIdAlreadyInWhitelist + ); + whitelist_account_ids + .try_push(evm_contract_account_id.clone()) + .map_err(|_| Error::::ExceededWhitelistMaxNumber)?; + WhitelistAccountId::::insert(support_chain, whitelist_account_ids); + Self::deposit_event(Event::AddWhitelistAccountId { + support_chain, + evm_contract_account_id, + }); + Ok(().into()) + } + + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::remove_whitelist())] + pub fn remove_whitelist( + origin: OriginFor, + support_chain: SupportChain, + evm_contract_account_id: T::AccountId, + ) -> DispatchResultWithPostInfo { + // Check the validity of origin + T::ControlOrigin::ensure_origin(origin)?; + + let mut whitelist_account_ids = WhitelistAccountId::::get(&support_chain); + + ensure!( + whitelist_account_ids.contains(&evm_contract_account_id), + Error::::AccountIdNotInWhitelist + ); + whitelist_account_ids.retain(|x| *x != evm_contract_account_id); + WhitelistAccountId::::insert(support_chain, whitelist_account_ids); + Self::deposit_event(Event::RemoveWhitelistAccountId { + support_chain, + evm_contract_account_id, + }); + Ok(().into()) + } + + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::set_execution_fee())] + pub fn set_execution_fee( + origin: OriginFor, + currency_id: CurrencyId, + execution_fee: BalanceOf, + ) -> DispatchResultWithPostInfo { + // Check the validity of origin + T::ControlOrigin::ensure_origin(origin)?; + ExecutionFee::::insert(currency_id, execution_fee); + Self::deposit_event(Event::SetExecutionFee { currency_id, execution_fee }); + Ok(().into()) + } + + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::set_transfer_to_fee())] + pub fn set_transfer_to_fee( + origin: OriginFor, + support_chain: SupportChain, + transfer_to_fee: BalanceOf, + ) -> DispatchResultWithPostInfo { + // Check the validity of origin + T::ControlOrigin::ensure_origin(origin)?; + TransferToFee::::insert(support_chain, transfer_to_fee); + Self::deposit_event(Event::SetTransferToFee { support_chain, transfer_to_fee }); + Ok(().into()) + } + } +} + +impl Pallet { + /// Check if the signer is in the whitelist + fn ensure_singer_on_whitelist( + evm_contract_account_id: &T::AccountId, + support_chain: SupportChain, + ) -> DispatchResult { + let whitelist_account_ids = WhitelistAccountId::::get(&support_chain); + ensure!( + whitelist_account_ids.contains(evm_contract_account_id), + Error::::AccountIdNotInWhitelist + ); + Ok(()) + } + + /// Charge an execution fee + fn charge_execution_fee( + currency_id: CurrencyIdOf, + evm_caller_account_id: &T::AccountId, + ) -> Result, DispatchError> { + let free_balance = T::MultiCurrency::free_balance(currency_id, evm_caller_account_id); + let execution_fee = + Self::execution_fee(currency_id).ok_or(Error::::NotSetExecutionFee)?; + let minimum_balance = T::MultiCurrency::minimum_balance(currency_id); + ensure!( + free_balance > execution_fee.saturating_add(minimum_balance), + Error::::FreeBalanceTooLow + ); + T::MultiCurrency::transfer( + currency_id, + evm_caller_account_id, + &T::TreasuryAccount::get(), + execution_fee, + )?; + Ok(free_balance - execution_fee.saturating_add(minimum_balance)) + } + + fn match_support_chain( + support_chain: SupportChain, + evm_caller_account_id: T::AccountId, + evm_caller: H160, + ) -> Result, DispatchError> { + match support_chain { + SupportChain::Astar => Ok(TargetChain::Astar(evm_caller_account_id)), + SupportChain::Moonbeam => Ok(TargetChain::Moonbeam(evm_caller)), + } + } + + fn transfer_to( + caller: T::AccountId, + evm_contract_account_id: &T::AccountId, + currency_id: CurrencyIdOf, + amount: BalanceOf, + target_chain: TargetChain, + ) -> DispatchResult { + match target_chain { + TargetChain::Astar(receiver) => { + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(T::VtokenMintingInterface::get_astar_parachain_id()), + AccountId32 { network: None, id: receiver.encode().try_into().unwrap() }, + ), + }; + + T::XcmTransfer::transfer(caller, currency_id, amount, dest, Unlimited)?; + }, + TargetChain::Moonbeam(receiver) => { + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(T::VtokenMintingInterface::get_moonbeam_parachain_id()), + AccountKey20 { network: None, key: receiver.to_fixed_bytes() }, + ), + }; + + match currency_id { + CurrencyId::VToken(TokenSymbol::KSM) | + CurrencyId::VToken(TokenSymbol::MOVR) | + CurrencyId::VToken2(0) | + CurrencyId::VToken2(1) => { + T::MultiCurrency::transfer( + CurrencyId::Native(TokenSymbol::BNC), + evm_contract_account_id, + &caller, + Self::transfer_to_fee(SupportChain::Moonbeam).unwrap_or_else(|| { + BalanceOf::::saturated_from(100_000_000_000u128) + }), + )?; + let fee = CurrencyId::Native(TokenSymbol::BNC); + let fee_amount = Self::transfer_to_fee(SupportChain::Moonbeam) + .unwrap_or_else(|| BalanceOf::::saturated_from(100_000_000_000u128)); + + let assets = vec![(currency_id, amount), (fee, fee_amount)]; + + T::XcmTransfer::transfer_multicurrencies( + caller, assets, 1, dest, Unlimited, + )?; + }, + _ => { + T::XcmTransfer::transfer(caller, currency_id, amount, dest, Unlimited)?; + }, + }; + }, + }; + Ok(()) + } + + fn h160_to_account_id(address: H160) -> T::AccountId { + let mut data = [0u8; 24]; + data[0..4].copy_from_slice(b"evm:"); + data[4..24].copy_from_slice(&address[..]); + let hash = BlakeTwo256::hash(&data); + + let account_id_32 = sp_runtime::AccountId32::from(Into::<[u8; 32]>::into(hash)); + T::AccountId::decode(&mut account_id_32.as_ref()).expect("Fail to decode address") + } +} diff --git a/pallets/xcm-action/src/mock.rs b/pallets/xcm-action/src/mock.rs new file mode 100644 index 000000000..1494b57e2 --- /dev/null +++ b/pallets/xcm-action/src/mock.rs @@ -0,0 +1,510 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#![cfg(test)] + +use crate as xcm_action; +use bifrost_asset_registry::AssetIdMaps; +use bifrost_slp::{QueryId, QueryResponseManager}; +use cumulus_primitives_core::ParaId; +use frame_support::{ + construct_runtime, ord_parameter_types, + pallet_prelude::*, + parameter_types, + traits::{Everything, Nothing}, + PalletId, +}; +use frame_system::EnsureSignedBy; +use hex_literal::hex; +use node_primitives::{CurrencyId, TokenSymbol}; +use orml_traits::{location::RelativeReserveProvider, parameter_type_with_key, MultiCurrency}; +use sp_core::{blake2_256, H256}; +use sp_runtime::{ + testing::Header, + traits::{ + AccountIdConversion, BlakeTwo256, Convert, IdentityLookup, TrailingZeroInput, + UniqueSaturatedInto, + }, + AccountId32, SaturatedConversion, +}; +use sp_std::vec; +pub use xcm::latest::prelude::*; +use xcm::{ + latest::{Junction, MultiLocation}, + opaque::latest::{ + Junction::Parachain, + Junctions::{X1, X2}, + }, +}; +pub use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, + ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, +}; +use xcm_executor::XcmExecutor; +use zenlink_protocol::{ + AssetBalance, AssetId as ZenlinkAssetId, LocalAssetHandler, PairLpGenerate, ZenlinkMultiAssets, +}; + +pub type Balance = u128; +pub type Amount = i128; +pub type BlockNumber = u64; +pub type AccountId = AccountId32; + +pub const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); +pub const ALICE: AccountId = AccountId32::new([1u8; 32]); + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + Tokens: orml_tokens, + Currencies: orml_currencies, + AssetRegistry: bifrost_asset_registry, + Slp: bifrost_slp, + VtokenMinting: bifrost_vtoken_minting, + ZenlinkProtocol: zenlink_protocol, + XTokens: orml_xtokens, + XcmAction: xcm_action, + PolkadotXcm: pallet_xcm + } +); + +// Pallet system configuration +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +// Pallet balances configuration +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +impl pallet_balances::Config for Test { + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = [u8; 8]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub const GetNativeCurrencyId: CurrencyId = CurrencyId::Native(TokenSymbol::BNC); +} + +pub type AdaptedBasicCurrency = + orml_currencies::BasicCurrencyAdapter; + +impl orml_currencies::Config for Test { + type GetNativeCurrencyId = GetNativeCurrencyId; + type MultiCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type WeightInfo = (); +} + +// Pallet orml-tokens configuration +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> u128 { + 0 + }; +} +pub type ReserveIdentifier = [u8; 8]; +impl orml_tokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = u128; + type Amount = i128; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type MaxLocks = (); + type DustRemovalWhitelist = Nothing; + type ReserveIdentifier = ReserveIdentifier; + type MaxReserves = ConstU32<100_000>; +} + +// Pallet vtoken-minting configuration +parameter_types! { + pub const MaximumUnlockIdOfUser: u32 = 10; + pub const MaximumUnlockIdOfTimeUnit: u32 = 50; + pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); + pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); + pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub const RelayCurrencyId: CurrencyId = KSM; +} + +ord_parameter_types! { + pub const One: AccountId = ALICE; +} + +impl bifrost_vtoken_minting::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Currencies; + type ControlOrigin = EnsureSignedBy; + type MaximumUnlockIdOfUser = MaximumUnlockIdOfUser; + type MaximumUnlockIdOfTimeUnit = MaximumUnlockIdOfTimeUnit; + type EntranceAccount = BifrostEntranceAccount; + type ExitAccount = BifrostExitAccount; + type FeeAccount = BifrostFeeAccount; + type RelayChainToken = RelayCurrencyId; + type CurrencyIdConversion = AssetIdMaps; + type CurrencyIdRegister = AssetIdMaps; + type BifrostSlp = Slp; + type WeightInfo = (); + type OnRedeemSuccess = (); + type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; +} +// Below is the implementation of tokens manipulation functions other than native token. +pub struct LocalAssetAdaptor(PhantomData); + +impl LocalAssetHandler for LocalAssetAdaptor +where + Local: MultiCurrency, +{ + fn local_balance_of(asset_id: ZenlinkAssetId, who: &AccountId) -> AssetBalance { + let currency_id: CurrencyId = asset_id.try_into().unwrap(); + Local::free_balance(currency_id, &who).saturated_into() + } + + fn local_total_supply(asset_id: ZenlinkAssetId) -> AssetBalance { + let currency_id: CurrencyId = asset_id.try_into().unwrap(); + Local::total_issuance(currency_id).saturated_into() + } + + fn local_is_exists(asset_id: ZenlinkAssetId) -> bool { + let rs: Result = asset_id.try_into(); + match rs { + Ok(_) => true, + Err(_) => false, + } + } + + fn local_transfer( + asset_id: ZenlinkAssetId, + origin: &AccountId, + target: &AccountId, + amount: AssetBalance, + ) -> DispatchResult { + let currency_id: CurrencyId = asset_id.try_into().unwrap(); + Local::transfer(currency_id, &origin, &target, amount.unique_saturated_into())?; + + Ok(()) + } + + fn local_deposit( + asset_id: ZenlinkAssetId, + origin: &AccountId, + amount: AssetBalance, + ) -> Result { + let currency_id: CurrencyId = asset_id.try_into().unwrap(); + Local::deposit(currency_id, &origin, amount.unique_saturated_into())?; + return Ok(amount); + } + + fn local_withdraw( + asset_id: ZenlinkAssetId, + origin: &AccountId, + amount: AssetBalance, + ) -> Result { + let currency_id: CurrencyId = asset_id.try_into().unwrap(); + Local::withdraw(currency_id, &origin, amount.unique_saturated_into())?; + + Ok(amount) + } +} + +type MultiAssets = ZenlinkMultiAssets>; + +parameter_types! { + pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); + pub const GetExchangeFee: (u32, u32) = (3, 1000); // 0.3% + pub const SelfParaId: u32 = 2001; +} + +impl zenlink_protocol::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiAssetsHandler = MultiAssets; + type PalletId = ZenlinkPalletId; + type SelfParaId = SelfParaId; + + type TargetChains = (); + type WeightInfo = (); + type AssetId = ZenlinkAssetId; + type LpGenerate = PairLpGenerate; +} + +pub struct AccountIdToMultiLocation; +impl Convert for AccountIdToMultiLocation { + fn convert(account_id: AccountId) -> MultiLocation { + MultiLocation::from(Junction::AccountId32 { network: None, id: account_id.into() }) + } +} + +parameter_types! { + // One XCM operation is 200_000_000 XcmWeight, cross-chain transfer ~= 2x of transfer = 3_000_000_000 + pub UnitWeightCost: Weight = Weight::from_ref_time(200_000_000); + pub const MaxInstructions: u32 = 100; + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(2001)); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = (); + type AssetTransactor = (); + type AssetTrap = (); + type Barrier = (); + type RuntimeCall = RuntimeCall; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type OriginConverter = (); + type ResponseHandler = (); + type SubscriptionService = (); + type Trader = (); + type Weigher = FixedWeightBounds; + type XcmSender = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type AssetLocker = (); + type AssetExchanger = (); +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + None + }; +} + +parameter_types! { + pub SelfRelativeLocation: MultiLocation = MultiLocation::here(); + pub const BaseXcmWeight: Weight = Weight::from_ref_time(1000_000_000u64); + pub const MaxAssetsForTransfer: usize = 2; +} + +impl orml_xtokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = (); + type AccountIdToMultiLocation = (); + type UniversalLocation = UniversalLocation; + type SelfLocation = SelfRelativeLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = RelativeReserveProvider; +} + +ord_parameter_types! { + pub const CouncilAccount: AccountId = AccountId::from([1u8; 32]); +} +impl bifrost_asset_registry::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RegisterOrigin = EnsureSignedBy; + type WeightInfo = (); +} + +pub struct SubAccountIndexMultiLocationConvertor; +impl Convert<(u16, CurrencyId), MultiLocation> for SubAccountIndexMultiLocationConvertor { + fn convert((sub_account_index, currency_id): (u16, CurrencyId)) -> MultiLocation { + match currency_id { + CurrencyId::Token(TokenSymbol::MOVR) => MultiLocation::new( + 1, + X2( + Parachain(2023), + Junction::AccountKey20 { + network: None, + key: Slp::derivative_account_id_20( + hex!["7369626cd1070000000000000000000000000000"].into(), + sub_account_index, + ) + .into(), + }, + ), + ), + _ => MultiLocation::new( + 1, + X1(Junction::AccountId32 { + network: None, + id: Self::derivative_account_id( + ParaId::from(2001u32).into_account_truncating(), + sub_account_index, + ) + .into(), + }), + ), + } + } +} + +// Mock Utility::derivative_account_id function. +impl SubAccountIndexMultiLocationConvertor { + pub fn derivative_account_id(who: AccountId, index: u16) -> AccountId { + let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") + } +} + +pub struct ParachainId; +impl Get for ParachainId { + fn get() -> ParaId { + 2001.into() + } +} + +parameter_types! { + pub const MaxTypeEntryPerBlock: u32 = 10; + pub const MaxRefundPerBlock: u32 = 10; +} + +pub struct SubstrateResponseManager; +impl QueryResponseManager for SubstrateResponseManager { + fn get_query_response_record(_query_id: QueryId) -> bool { + Default::default() + } + fn create_query_record( + _responder: &MultiLocation, + _call_back: Option, + _timeout: u64, + ) -> u64 { + Default::default() + } + fn remove_query_record(_query_id: QueryId) -> bool { + Default::default() + } +} + +impl bifrost_slp::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type MultiCurrency = Currencies; + type ControlOrigin = EnsureSignedBy; + type WeightInfo = (); + type VtokenMinting = VtokenMinting; + type AccountConverter = SubAccountIndexMultiLocationConvertor; + type ParachainId = ParachainId; + type XcmRouter = (); + type XcmExecutor = (); + type SubstrateResponseManager = SubstrateResponseManager; + type MaxTypeEntryPerBlock = MaxTypeEntryPerBlock; + type MaxRefundPerBlock = MaxRefundPerBlock; + type OnRefund = (); + type ParachainStaking = (); + type XcmTransfer = XTokens; +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +impl pallet_xcm::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type UniversalLocation = UniversalLocation; + type SendXcmOrigin = EnsureXcmOrigin; + type Weigher = FixedWeightBounds; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = (); + type XcmTeleportFilter = Nothing; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = ConstU32<2>; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; // TODO: config after polkadot impl WeightInfo for () + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +// Pallet xcm-action configuration +parameter_types! { + pub const NativeCurrencyId: CurrencyId = CurrencyId::Native(TokenSymbol::BNC); +} + +impl xcm_action::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ControlOrigin = EnsureSignedBy; + type MultiCurrency = Currencies; + type DexOperator = ZenlinkProtocol; + type VtokenMintingInterface = VtokenMinting; + type XcmTransfer = XTokens; + type CurrencyIdConvert = AssetIdMaps; + type TreasuryAccount = BifrostFeeAccount; + type ParachainId = ParachainId; + type WeightInfo = (); +} diff --git a/pallets/xcm-action/src/tests.rs b/pallets/xcm-action/src/tests.rs new file mode 100644 index 000000000..1952d835b --- /dev/null +++ b/pallets/xcm-action/src/tests.rs @@ -0,0 +1,121 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#![cfg(test)] + +use crate::{mock::*, *}; +use frame_support::{assert_noop, assert_ok, dispatch::RawOrigin, sp_io}; +use hex_literal::hex; +use sp_core::{bounded::BoundedVec, ConstU32}; +use zenlink_protocol::AssetId; + +const EVM_ADDR: [u8; 20] = hex!["573394b77fC17F91E9E67F147A9ECe24d67C5073"]; + +#[test] +fn test_xcm_action_util() { + sp_io::TestExternalities::default().execute_with(|| { + let address = H160::from_slice(&EVM_ADDR); + let account_id: AccountId = XcmAction::h160_to_account_id(address); + assert_eq!( + account_id, + sp_runtime::AccountId32::new(hex!( + "b1c2dde9e562a738e264a554e467b30e5cd58e95ab98459946fb8e518cfe71c2" + )) + ); + let public_key: [u8; 32] = account_id.encode().try_into().unwrap(); + assert_eq!( + public_key, + hex!("b1c2dde9e562a738e264a554e467b30e5cd58e95ab98459946fb8e518cfe71c2") + ); + + assert_ok!(XcmAction::add_whitelist( + RuntimeOrigin::signed(ALICE), + SupportChain::Astar, + ALICE + )); + assert_eq!( + XcmAction::whitelist_account_ids(SupportChain::Astar), + BoundedVec::>::try_from(vec![ALICE]).unwrap() + ); + assert_noop!( + XcmAction::add_whitelist(RuntimeOrigin::signed(ALICE), SupportChain::Astar, ALICE), + Error::::AccountIdAlreadyInWhitelist + ); + assert_ok!(XcmAction::remove_whitelist( + RuntimeOrigin::signed(ALICE), + SupportChain::Astar, + ALICE + )); + assert_eq!( + XcmAction::whitelist_account_ids(SupportChain::Astar), + BoundedVec::>::default() + ); + + assert_ok!(XcmAction::set_execution_fee( + RuntimeOrigin::signed(ALICE), + CurrencyId::Token2(0), + 10 + )); + assert_eq!(XcmAction::execution_fee(CurrencyId::Token2(0)), Some(10)); + + assert_ok!(XcmAction::set_transfer_to_fee( + RuntimeOrigin::signed(ALICE), + SupportChain::Moonbeam, + 10 + )); + assert_eq!(XcmAction::transfer_to_fee(SupportChain::Moonbeam), Some(10)); + }); +} + +#[test] +fn test_zenlink() { + sp_io::TestExternalities::default().execute_with(|| { + assert_ok!(Currencies::deposit(CurrencyId::Native(TokenSymbol::BNC), &ALICE, 50)); + assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::KSM), &ALICE, 50)); + + let bnc_token: AssetId = + AssetId::try_convert_from(CurrencyId::Native(TokenSymbol::BNC), 2001).unwrap(); + let ksm_token: AssetId = + AssetId::try_convert_from(CurrencyId::Token(TokenSymbol::KSM), 2001).unwrap(); + + assert_ok!(ZenlinkProtocol::create_pair(RawOrigin::Root.into(), bnc_token, ksm_token)); + assert_ok!(ZenlinkProtocol::add_liquidity( + RawOrigin::Signed(ALICE).into(), + bnc_token, + ksm_token, + 20u128, + 20u128, + 0, + 0, + 100 + )); + assert_eq!(Currencies::free_balance(CurrencyId::Native(TokenSymbol::BNC), &ALICE), 30u128); + assert_eq!(Currencies::free_balance(CurrencyId::Token(TokenSymbol::KSM), &ALICE), 30u128); + + let path = vec![bnc_token, ksm_token]; + let balance = Currencies::free_balance(CurrencyId::Native(TokenSymbol::BNC), &ALICE); + let minimum_balance = Currencies::minimum_balance(CurrencyId::Native(TokenSymbol::BNC)); + assert_ok!(ZenlinkProtocol::swap_exact_assets_for_assets( + RawOrigin::Signed(ALICE).into(), + balance - minimum_balance, + 0, + path, + ALICE, + 100 + )); + }); +} diff --git a/pallets/xcm-action/src/weights.rs b/pallets/xcm-action/src/weights.rs new file mode 100644 index 000000000..fcac4527b --- /dev/null +++ b/pallets/xcm-action/src/weights.rs @@ -0,0 +1,404 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for bifrost_xcm_action +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `VM-16-3-ubuntu`, CPU: `Intel(R) Xeon(R) Platinum 8374C CPU @ 2.70GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bifrost-polkadot-local"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/bifrost +// benchmark +// pallet +// --chain=bifrost-polkadot-local +// --steps=50 +// --repeat=20 +// --pallet=bifrost_xcm_action +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --header=./HEADER-GPL3 +// --output=./weight.rs +// --template +// ./frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for bifrost_xcm_action. +pub trait WeightInfo { + fn add_whitelist() -> Weight; + fn remove_whitelist() -> Weight; + fn set_execution_fee() -> Weight; + fn set_transfer_to_fee() -> Weight; + fn mint() -> Weight; + fn redeem() -> Weight; + fn swap() -> Weight; +} + +/// Weights for bifrost_xcm_action using the Bifrost node and recommended hardware. +pub struct BifrostWeight(PhantomData); +impl WeightInfo for BifrostWeight { + /// Storage: XcmAction WhitelistAccountId (r:1 w:1) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn add_whitelist() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `4907` + // Minimum execution time: 20_135_000 picoseconds. + Weight::from_parts(20_866_000, 4907) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:1) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn remove_whitelist() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `5016` + // Minimum execution time: 22_755_000 picoseconds. + Weight::from_parts(23_464_000, 5016) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmAction ExecutionFee (r:0 w:1) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + fn set_execution_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `25` + // Estimated: `2018` + // Minimum execution time: 15_269_000 picoseconds. + Weight::from_parts(15_952_000, 2018) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmAction TransferToFee (r:0 w:1) + /// Proof: XcmAction TransferToFee (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) + fn set_transfer_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `25` + // Estimated: `2018` + // Minimum execution time: 15_209_000 picoseconds. + Weight::from_parts(15_853_000, 2018) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: VtokenMinting MinimumMint (r:1 w:0) + /// Proof: VtokenMinting MinimumMint (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:1) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:1) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: AssetRegistry CurrencyMetadatas (r:1 w:0) + /// Proof Skipped: AssetRegistry CurrencyMetadatas (max_values: None, max_size: None, mode: Measured) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `1345` + // Estimated: `28333` + // Minimum execution time: 130_794_000 picoseconds. + Weight::from_parts(135_588_000, 28333) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:0) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: VtokenMinting MinimumRedeem (r:1 w:0) + /// Proof: VtokenMinting MinimumRedeem (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Slp DelegationsOccupied (r:1 w:0) + /// Proof Skipped: Slp DelegationsOccupied (max_values: None, max_size: None, mode: Measured) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:0) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:0) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting OngoingTimeUnit (r:1 w:0) + /// Proof: VtokenMinting OngoingTimeUnit (max_values: None, max_size: Some(27), added: 2502, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn redeem() -> Weight { + // Proof Size summary in bytes: + // Measured: `1244` + // Estimated: `25427` + // Minimum execution time: 55_919_000 picoseconds. + Weight::from_parts(57_015_000, 25427) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:0) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn swap() -> Weight { + // Proof Size summary in bytes: + // Measured: `1070` + // Estimated: `13632` + // Minimum execution time: 37_290_000 picoseconds. + Weight::from_parts(37_964_000, 13632) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: XcmAction WhitelistAccountId (r:1 w:1) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn add_whitelist() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `4907` + // Minimum execution time: 20_135_000 picoseconds. + Weight::from_parts(20_866_000, 4907) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:1) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn remove_whitelist() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `5016` + // Minimum execution time: 22_755_000 picoseconds. + Weight::from_parts(23_464_000, 5016) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmAction ExecutionFee (r:0 w:1) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + fn set_execution_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `25` + // Estimated: `2018` + // Minimum execution time: 15_269_000 picoseconds. + Weight::from_parts(15_952_000, 2018) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmAction TransferToFee (r:0 w:1) + /// Proof: XcmAction TransferToFee (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) + fn set_transfer_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `25` + // Estimated: `2018` + // Minimum execution time: 15_209_000 picoseconds. + Weight::from_parts(15_853_000, 2018) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: VtokenMinting MinimumMint (r:1 w:0) + /// Proof: VtokenMinting MinimumMint (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:1) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:1) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: AssetRegistry CurrencyMetadatas (r:1 w:0) + /// Proof Skipped: AssetRegistry CurrencyMetadatas (max_values: None, max_size: None, mode: Measured) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn mint() -> Weight { + // Proof Size summary in bytes: + // Measured: `1345` + // Estimated: `28333` + // Minimum execution time: 130_794_000 picoseconds. + Weight::from_parts(135_588_000, 28333) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:0) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: VtokenMinting MinimumRedeem (r:1 w:0) + /// Proof: VtokenMinting MinimumRedeem (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Slp DelegationsOccupied (r:1 w:0) + /// Proof Skipped: Slp DelegationsOccupied (max_values: None, max_size: None, mode: Measured) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:0) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:0) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting OngoingTimeUnit (r:1 w:0) + /// Proof: VtokenMinting OngoingTimeUnit (max_values: None, max_size: Some(27), added: 2502, mode: MaxEncodedLen) + /// Storage: System Number (r:1 w:0) + /// Proof: System Number (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System ExecutionPhase (r:1 w:0) + /// Proof: System ExecutionPhase (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: System EventCount (r:1 w:1) + /// Proof: System EventCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Events (r:1 w:1) + /// Proof Skipped: System Events (max_values: Some(1), max_size: None, mode: Measured) + fn redeem() -> Weight { + // Proof Size summary in bytes: + // Measured: `1244` + // Estimated: `25427` + // Minimum execution time: 55_919_000 picoseconds. + Weight::from_parts(57_015_000, 25427) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: XcmAction WhitelistAccountId (r:1 w:0) + /// Proof: XcmAction WhitelistAccountId (max_values: None, max_size: Some(338), added: 2813, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:0) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: XcmAction ExecutionFee (r:1 w:0) + /// Proof: XcmAction ExecutionFee (max_values: None, max_size: Some(46), added: 2521, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:0) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn swap() -> Weight { + // Proof Size summary in bytes: + // Measured: `1070` + // Estimated: `13632` + // Minimum execution time: 37_290_000 picoseconds. + Weight::from_parts(37_964_000, 13632) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + } +} diff --git a/runtime/bifrost-kusama/Cargo.toml b/runtime/bifrost-kusama/Cargo.toml index b2fb94744..ca8fccef8 100644 --- a/runtime/bifrost-kusama/Cargo.toml +++ b/runtime/bifrost-kusama/Cargo.toml @@ -28,6 +28,7 @@ sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "pol sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -112,6 +113,7 @@ bifrost-farming = { path = "../../pallets/farming", default-features = false } bifrost-farming-rpc-runtime-api = { path = "../../pallets/farming/rpc/runtime-api", default-features = false } parachain-staking = { path = "../../pallets/parachain-staking", default-features = false } bifrost-fee-share = { path = "../../pallets/fee-share", default-features = false } +bifrost-xcm-action = { path = "../../pallets/xcm-action", default-features = false } # orml orml-currencies = { version = "0.4.1-dev", default-features = false } @@ -168,6 +170,7 @@ std = [ "pallet-tips/std", "pallet-whitelist/std", "sp-api/std", + "sp-io/std", "sp-arithmetic/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -219,6 +222,7 @@ std = [ "bifrost-cross-in-out/std", "parachain-staking/std", "bifrost-fee-share/std", + "bifrost-xcm-action/std", "zenlink-protocol/std", "zenlink-protocol-runtime-api/std", "zenlink-stable-amm/std", @@ -332,6 +336,7 @@ try-runtime = [ "bifrost-system-maker/try-runtime", "bifrost-fee-share/try-runtime", "bifrost-cross-in-out/try-runtime", + "bifrost-xcm-action/try-runtime", ] # When enabled, the runtime API will not be build. @@ -347,4 +352,4 @@ disable-runtime-api = [] on-chain-release-build = ["sp-api/disable-logging"] # Set timing constants (e.g. session period) to faster versions to speed up testing. -fast-runtime = [] \ No newline at end of file +fast-runtime = [] diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 1ab1d8cb6..16970d2a7 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -1586,6 +1586,21 @@ impl bifrost_vtoken_minting::Config for Runtime { type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; +} + +impl bifrost_xcm_action::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ControlOrigin = EitherOfDiverse; + type MultiCurrency = Currencies; + type DexOperator = ZenlinkProtocol; + type VtokenMintingInterface = VtokenMinting; + type XcmTransfer = XTokens; + type CurrencyIdConvert = AssetIdMaps; + type TreasuryAccount = BifrostTreasuryAccount; + type ParachainId = SelfParaChainId; + type WeightInfo = bifrost_xcm_action::weights::BifrostWeight; } // Below is the implementation of tokens manipulation functions other than native token. @@ -1767,6 +1782,7 @@ construct_runtime! { SystemMaker: bifrost_system_maker::{Pallet, Call, Storage, Event} = 121, FeeShare: bifrost_fee_share::{Pallet, Call, Storage, Event} = 122, CrossInOut: bifrost_cross_in_out::{Pallet, Call, Storage, Event} = 123, + XcmAction: bifrost_xcm_action::{Pallet, Call, Storage, Event} = 125, } } diff --git a/runtime/bifrost-kusama/src/xcm_config.rs b/runtime/bifrost-kusama/src/xcm_config.rs index 152565ef4..9f40b1ef5 100644 --- a/runtime/bifrost-kusama/src/xcm_config.rs +++ b/runtime/bifrost-kusama/src/xcm_config.rs @@ -21,13 +21,13 @@ use bifrost_asset_registry::{AssetIdMaps, FixedRateOfAsset}; use codec::{Decode, Encode}; pub use cumulus_primitives_core::ParaId; use frame_support::{ - parameter_types, + ensure, parameter_types, sp_runtime::traits::{CheckedConversion, Convert}, traits::Get, }; use node_primitives::{AccountId, CurrencyId, CurrencyIdMapping, TokenSymbol}; pub use polkadot_parachain::primitives::Sibling; -use sp_std::{convert::TryFrom, marker::PhantomData}; +use sp_std::{borrow::Borrow, convert::TryFrom, marker::PhantomData}; pub use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedRateOfFungible, @@ -35,7 +35,7 @@ pub use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, }; -use xcm_executor::traits::MatchesFungible; +use xcm_executor::traits::{MatchesFungible, ShouldExecute}; pub use xcm_interface::traits::{parachains, XcmBaseWeight}; // orml imports @@ -50,6 +50,7 @@ pub use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key use orml_xcm_support::{DepositToAlternative, MultiCurrencyAdapter}; use pallet_xcm::XcmPassthrough; use sp_core::bounded::BoundedVec; +use sp_io::hashing::blake2_256; use xcm::v3::prelude::*; /// Bifrost Asset Matcher @@ -241,6 +242,50 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); } +pub struct ExternalAccountConverter(PhantomData<(Network, AccountId)>); +impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> + xcm_executor::traits::Convert + for ExternalAccountConverter +{ + fn convert(location: MultiLocation) -> Result { + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "location: {:?}", + location.clone(), + ); + match location { + MultiLocation { parents: 1, interior: X2(Parachain(_id), AccountId32 { id, .. }) } => + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "AccountId32: {:?}", + id, + ), + MultiLocation { + parents: 1, + interior: X2(Parachain(_id), AccountKey20 { key, .. }), + } => log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "AccountKey20: {:?}", + key, + ), + _ => return Err(location), + }; + let hash: [u8; 32] = ("multiloc", location.borrow()).borrow().using_encoded(blake2_256); + let mut account_id = [0u8; 32]; + account_id.copy_from_slice(&hash[0..32]); + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "account_id: {:?}", + account_id, + ); + Ok(account_id.into()) + } + + fn reverse(who: AccountId) -> Result { + Err(who) + } +} + /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch RuntimeOrigin. @@ -251,6 +296,7 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + ExternalAccountConverter, ); /// This is the type we use to convert an (incoming) XCM origin into a local `RuntimeOrigin` @@ -290,11 +336,70 @@ match_types! { }; } +/// Barrier allowing a top level paid message with DescendOrigin instruction +/// first +pub const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; +pub const DEFAULT_REF_TIMR: u64 = 10_000_000_000; +pub struct AllowTopLevelPaidExecutionDescendOriginFirst(PhantomData); +impl> ShouldExecute for AllowTopLevelPaidExecutionDescendOriginFirst { + fn should_execute( + origin: &MultiLocation, + message: &mut [Instruction], + max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + log::trace!( + target: "xcm::barriers", + "AllowTopLevelPaidExecutionDescendOriginFirst origin: + {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, message, max_weight, _weight_credit, + ); + ensure!(T::contains(origin), ()); + let mut iter = message.iter_mut(); + // Make sure the first instruction is DescendOrigin + iter.next() + .filter(|instruction| matches!(instruction, DescendOrigin(_))) + .ok_or(())?; + + // Then WithdrawAsset + iter.next() + .filter(|instruction| matches!(instruction, WithdrawAsset(_))) + .ok_or(())?; + + // Then BuyExecution + let i = iter.next().ok_or(())?; + match i { + BuyExecution { weight_limit: Limited(ref mut weight), .. } => { + if weight.all_gte(max_weight) { + weight.set_ref_time(max_weight.ref_time()); + weight.set_proof_size(max_weight.proof_size()); + }; + }, + BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => { + *weight_limit = Limited(max_weight); + }, + _ => {}, + }; + + // Then Transact + let i = iter.next().ok_or(())?; + match i { + Transact { ref mut require_weight_at_most, .. } => { + let weight = Weight::from_parts(DEFAULT_REF_TIMR, DEFAULT_PROOF_SIZE); + *require_weight_at_most = weight; + Ok(()) + }, + _ => Err(()), + } + } +} + pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionDescendOriginFirst, ); pub type BifrostAssetTransactor = MultiCurrencyAdapter< @@ -558,6 +663,7 @@ impl Contains for SafeCallFilter { RuntimeCall::XcmInterface( xcm_interface::Call::transfer_statemine_assets { .. } ) | + RuntimeCall::XcmAction(..) | // TODO swap RuntimeCall::ZenlinkProtocol( zenlink_protocol::Call::add_liquidity { .. } | diff --git a/runtime/bifrost-polkadot/Cargo.toml b/runtime/bifrost-polkadot/Cargo.toml index cae9e6055..6f4d52588 100644 --- a/runtime/bifrost-polkadot/Cargo.toml +++ b/runtime/bifrost-polkadot/Cargo.toml @@ -19,7 +19,7 @@ scale-info = { version = "2.3.1", default-features = false, features = [ smallvec = "1.10.0" static_assertions = "1.1.0" # primitives -node-primitives = { default-features = false, path = "../../node/primitives" } +node-primitives = { default-features = false, path = "../../node/primitives", features=["with-bifrost-polkadot-runtime"] } sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -30,6 +30,7 @@ sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "polka sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38", default-features = false } @@ -103,6 +104,7 @@ bifrost-farming-rpc-runtime-api = { path = "../../pallets/farming/rpc/runtime-ap bifrost-fee-share = { path = "../../pallets/fee-share", default-features = false } bifrost-ve-minting = { path = "../../pallets/ve-minting", default-features = false } bifrost-ve-minting-rpc-runtime-api = { path = "../../pallets/ve-minting/rpc/runtime-api", default-features = false } +bifrost-xcm-action = { path = "../../pallets/xcm-action", default-features = false, features=["with-bifrost-polkadot-runtime"] } # orml orml-currencies = { version = "0.4.1-dev", default-features = false } @@ -161,6 +163,7 @@ std = [ "sp-std/std", "sp-session/std", "sp-transaction-pool/std", + "sp-io/std", "parachain-info/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -204,14 +207,15 @@ std = [ "zenlink-protocol-runtime-api/std", "merkle-distributor/std", "bifrost-cross-in-out/std", + "bifrost-xcm-action/std", ] with-tracing = ["frame-executive/with-tracing"] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", + "frame-benchmarking", + "frame-system-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", "pallet-collective/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bounties/runtime-benchmarks", @@ -230,6 +234,7 @@ runtime-benchmarks = [ "bifrost-vtoken-minting/runtime-benchmarks", "bifrost-ve-minting/runtime-benchmarks", "bifrost-cross-in-out/runtime-benchmarks", + "bifrost-xcm-action/runtime-benchmarks", "sp-api/disable-logging", ] diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index 57d120a4b..0c9412e7a 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -1287,6 +1287,19 @@ impl bifrost_cross_in_out::Config for Runtime { type WeightInfo = bifrost_cross_in_out::weights::BifrostWeight; } +impl bifrost_xcm_action::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ControlOrigin = EitherOfDiverse; + type MultiCurrency = Currencies; + type DexOperator = ZenlinkProtocol; + type VtokenMintingInterface = VtokenMinting; + type XcmTransfer = XTokens; + type CurrencyIdConvert = AssetIdMaps; + type TreasuryAccount = BifrostTreasuryAccount; + type ParachainId = SelfParaChainId; + type WeightInfo = bifrost_xcm_action::weights::BifrostWeight; +} + // Bifrost modules end // zenlink runtime start @@ -1380,6 +1393,8 @@ impl bifrost_vtoken_minting::Config for Runtime { type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2006>; + type MoonbeamParachainId = ConstU32<2004>; } parameter_types! { @@ -1577,6 +1592,7 @@ construct_runtime! { FeeShare: bifrost_fee_share::{Pallet, Call, Storage, Event} = 122, CrossInOut: bifrost_cross_in_out::{Pallet, Call, Storage, Event} = 123, VeMinting: bifrost_ve_minting::{Pallet, Call, Storage, Event} = 124, + XcmAction: bifrost_xcm_action::{Pallet, Call, Storage, Event} = 125, } } @@ -1639,6 +1655,7 @@ mod benches { [bifrost_slp, Slp] [bifrost_salp, Salp] [bifrost_ve_minting, VeMinting] + [bifrost_xcm_action, XcmAction] ); } diff --git a/runtime/bifrost-polkadot/src/xcm_config.rs b/runtime/bifrost-polkadot/src/xcm_config.rs index 108c0ba1e..28056058f 100644 --- a/runtime/bifrost-polkadot/src/xcm_config.rs +++ b/runtime/bifrost-polkadot/src/xcm_config.rs @@ -21,6 +21,7 @@ use bifrost_asset_registry::AssetIdMaps; use codec::{Decode, Encode}; pub use cumulus_primitives_core::ParaId; use frame_support::{ + ensure, sp_runtime::traits::{CheckedConversion, Convert}, traits::{ContainsPair, Get}, }; @@ -28,7 +29,8 @@ use node_primitives::{ AccountId, CurrencyId, CurrencyIdMapping, TokenSymbol, DOT_TOKEN_ID, GLMR_TOKEN_ID, }; pub use polkadot_parachain::primitives::Sibling; -use sp_std::{convert::TryFrom, marker::PhantomData}; +use sp_io::hashing::blake2_256; +use sp_std::{borrow::Borrow, convert::TryFrom, marker::PhantomData}; pub use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedRateOfFungible, @@ -36,7 +38,7 @@ pub use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, }; -use xcm_executor::traits::MatchesFungible; +use xcm_executor::traits::{MatchesFungible, ShouldExecute}; pub use xcm_interface::traits::{parachains, XcmBaseWeight}; // orml imports @@ -194,6 +196,50 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); } +pub struct ExternalAccountConverter(PhantomData<(Network, AccountId)>); +impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> + xcm_executor::traits::Convert + for ExternalAccountConverter +{ + fn convert(location: MultiLocation) -> Result { + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "location: {:?}", + location.clone(), + ); + match location { + MultiLocation { parents: 1, interior: X2(Parachain(_id), AccountId32 { id, .. }) } => + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "AccountId32: {:?}", + id, + ), + MultiLocation { + parents: 1, + interior: X2(Parachain(_id), AccountKey20 { key, .. }), + } => log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "AccountKey20: {:?}", + key, + ), + _ => return Err(location), + }; + let hash: [u8; 32] = ("multiloc", location.borrow()).borrow().using_encoded(blake2_256); + let mut account_id = [0u8; 32]; + account_id.copy_from_slice(&hash[0..32]); + log::trace!( + target: "xcm::ExternalAccountConverter::convert", + "account_id: {:?}", + account_id, + ); + Ok(account_id.into()) + } + + fn reverse(who: AccountId) -> Result { + Err(who) + } +} + /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch RuntimeOrigin. @@ -204,6 +250,7 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + ExternalAccountConverter, ); /// This is the type we use to convert an (incoming) XCM origin into a local `RuntimeOrigin` @@ -243,11 +290,70 @@ match_types! { }; } +/// Barrier allowing a top level paid message with DescendOrigin instruction +/// first +pub const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; +pub const DEFAULT_REF_TIMR: u64 = 10_000_000_000; +pub struct AllowTopLevelPaidExecutionDescendOriginFirst(PhantomData); +impl> ShouldExecute for AllowTopLevelPaidExecutionDescendOriginFirst { + fn should_execute( + origin: &MultiLocation, + message: &mut [Instruction], + max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + log::trace!( + target: "xcm::barriers", + "AllowTopLevelPaidExecutionDescendOriginFirst origin: + {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, message, max_weight, _weight_credit, + ); + ensure!(T::contains(origin), ()); + let mut iter = message.iter_mut(); + // Make sure the first instruction is DescendOrigin + iter.next() + .filter(|instruction| matches!(instruction, DescendOrigin(_))) + .ok_or(())?; + + // Then WithdrawAsset + iter.next() + .filter(|instruction| matches!(instruction, WithdrawAsset(_))) + .ok_or(())?; + + // Then BuyExecution + let i = iter.next().ok_or(())?; + match i { + BuyExecution { weight_limit: Limited(ref mut weight), .. } => { + if weight.all_gte(max_weight) { + weight.set_ref_time(max_weight.ref_time()); + weight.set_proof_size(max_weight.proof_size()); + }; + }, + BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => { + *weight_limit = Limited(max_weight); + }, + _ => {}, + }; + + // Then Transact + let i = iter.next().ok_or(())?; + match i { + Transact { ref mut require_weight_at_most, .. } => { + let weight = Weight::from_parts(DEFAULT_REF_TIMR, DEFAULT_PROOF_SIZE); + *require_weight_at_most = weight; + Ok(()) + }, + _ => Err(()), + } + } +} + pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionDescendOriginFirst, ); pub type BifrostAssetTransactor = MultiCurrencyAdapter< @@ -417,6 +523,7 @@ impl Contains for SafeCallFilter { RuntimeCall::XcmInterface( xcm_interface::Call::transfer_statemine_assets { .. } ) | + RuntimeCall::XcmAction(..) | // TODO swap RuntimeCall::ZenlinkProtocol( zenlink_protocol::Call::add_liquidity { .. } |