From 041fc4429cb193ddb418c994a9f30c0cde5ddc15 Mon Sep 17 00:00:00 2001 From: Sander Bosma Date: Mon, 14 Nov 2022 14:29:58 +0100 Subject: [PATCH] feat: xcm taxing --- Cargo.lock | 112 ++++----- parachain/runtime/kintsugi/src/xcm_config.rs | 224 +++++++++++++++++- .../relaychain/kusama_cross_chain_transfer.rs | 2 +- 3 files changed, 275 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51b9c112bc..a942310b55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1936,7 +1936,6 @@ dependencies = [ "mocktopus", "orml-tokens", "orml-traits", - "pallet-traits", "pallet-transaction-payment", "parity-scale-codec", "scale-info", @@ -1946,6 +1945,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "traits", ] [[package]] @@ -3703,6 +3703,7 @@ dependencies = [ "interbtc-primitives", "issue", "itertools", + "loans", "mocktopus", "module-btc-relay-rpc-runtime-api", "module-escrow-rpc-runtime-api", @@ -3724,7 +3725,6 @@ dependencies = [ "pallet-collective", "pallet-grandpa", "pallet-identity", - "pallet-loans", "pallet-membership", "pallet-multisig", "pallet-preimage", @@ -3732,7 +3732,6 @@ dependencies = [ "pallet-society", "pallet-sudo", "pallet-timestamp", - "pallet-traits", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", @@ -3762,6 +3761,7 @@ dependencies = [ "staking", "substrate-wasm-builder", "supply", + "traits", "vault-registry", "xcm-builder", ] @@ -3873,7 +3873,6 @@ dependencies = [ "pallet-society", "pallet-sudo", "pallet-timestamp", - "pallet-traits", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", @@ -3905,6 +3904,7 @@ dependencies = [ "staking", "substrate-wasm-builder", "supply", + "traits", "vault-registry", "xcm", "xcm-builder", @@ -4240,7 +4240,6 @@ dependencies = [ "pallet-session", "pallet-society", "pallet-timestamp", - "pallet-traits", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", @@ -4272,6 +4271,7 @@ dependencies = [ "staking", "substrate-wasm-builder", "supply", + "traits", "vault-registry", "xcm", "xcm-builder", @@ -5159,6 +5159,32 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb68f22743a3fb35785f1e7f844ca5a3de2dde5bd0c0ef5b372065814699b121" +[[package]] +name = "loans" +version = "1.9.3" +dependencies = [ + "currency", + "frame-benchmarking", + "frame-support", + "frame-system", + "interbtc-primitives", + "mocktopus", + "num-traits", + "orml-oracle", + "orml-tokens", + "orml-traits", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "traits", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -5970,7 +5996,6 @@ dependencies = [ "orml-tokens", "orml-traits", "pallet-timestamp", - "pallet-traits", "parity-scale-codec", "scale-info", "security", @@ -5980,6 +6005,7 @@ dependencies = [ "sp-runtime", "sp-std", "staking", + "traits", ] [[package]] @@ -6590,32 +6616,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-loans" -version = "1.9.3" -dependencies = [ - "currency", - "frame-benchmarking", - "frame-support", - "frame-system", - "interbtc-primitives", - "mocktopus", - "num-traits", - "orml-oracle", - "orml-tokens", - "orml-traits", - "pallet-timestamp", - "pallet-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-membership" version = "4.0.0-dev" @@ -6975,25 +6975,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-traits" -version = "1.9.3" -dependencies = [ - "frame-support", - "frame-system", - "interbtc-primitives", - "log", - "num-bigint 0.4.3", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" @@ -12421,6 +12402,7 @@ dependencies = [ "hex-literal 0.3.4", "interbtc-primitives", "issue", + "loans", "log", "mocktopus", "module-btc-relay-rpc-runtime-api", @@ -12445,7 +12427,6 @@ dependencies = [ "pallet-balances", "pallet-collective", "pallet-identity", - "pallet-loans", "pallet-membership", "pallet-multisig", "pallet-preimage", @@ -12454,7 +12435,6 @@ dependencies = [ "pallet-society", "pallet-sudo", "pallet-timestamp", - "pallet-traits", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", @@ -12486,6 +12466,7 @@ dependencies = [ "staking", "substrate-wasm-builder", "supply", + "traits", "vault-registry", "xcm", "xcm-builder", @@ -12523,6 +12504,7 @@ dependencies = [ "hex-literal 0.3.4", "interbtc-primitives", "issue", + "loans", "log", "mocktopus", "module-btc-relay-rpc-runtime-api", @@ -12547,7 +12529,6 @@ dependencies = [ "pallet-balances", "pallet-collective", "pallet-identity", - "pallet-loans", "pallet-membership", "pallet-multisig", "pallet-preimage", @@ -12556,7 +12537,6 @@ dependencies = [ "pallet-society", "pallet-sudo", "pallet-timestamp", - "pallet-traits", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", @@ -12588,6 +12568,7 @@ dependencies = [ "staking", "substrate-wasm-builder", "supply", + "traits", "vault-registry", "xcm", "xcm-builder", @@ -12906,6 +12887,25 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "traits" +version = "1.9.3" +dependencies = [ + "frame-support", + "frame-system", + "interbtc-primitives", + "log", + "num-bigint 0.4.3", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "trie-db" version = "0.24.0" @@ -13160,7 +13160,6 @@ dependencies = [ "orml-tokens", "orml-traits", "pallet-timestamp", - "pallet-traits", "parity-scale-codec", "pretty_assertions", "reward", @@ -13173,6 +13172,7 @@ dependencies = [ "sp-runtime", "sp-std", "staking", + "traits", "visibility", ] diff --git a/parachain/runtime/kintsugi/src/xcm_config.rs b/parachain/runtime/kintsugi/src/xcm_config.rs index 4fd33c827f..e52d160725 100644 --- a/parachain/runtime/kintsugi/src/xcm_config.rs +++ b/parachain/runtime/kintsugi/src/xcm_config.rs @@ -20,7 +20,7 @@ use xcm_builder::{ RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, }; -use xcm_executor::{Config, XcmExecutor}; +use xcm_executor::{traits::WeightBounds, Config, XcmExecutor}; use CurrencyId::ForeignAsset; parameter_types! { @@ -179,6 +179,218 @@ pub type XcmRouter = ( XcmpQueue, ); +trait Tax { + fn tax(&self) -> (Self, Self) + where + Self: Sized; +} +impl Tax for MultiAssets { + fn tax(&self) -> (Self, Self) { + let mut taxed = vec![]; + let mut tax = vec![]; + + for asset in self.inner().iter() { + match asset { + &MultiAsset { + ref id, + fun: Fungibility::Fungible(amount), + } => { + tax.push(MultiAsset { + id: id.clone(), + fun: Fungibility::Fungible(amount / 10), + }); + taxed.push(MultiAsset { + id: id.clone(), + fun: Fungibility::Fungible((amount * 9) / 10), + }); + } + x => taxed.push(x.clone()), + } + } + (taxed.into(), tax.into()) + } +} + +fn limit_buy_execution(mut xcm: Xcm<()>, holding: MultiAssets, at: &MultiLocation) -> Xcm<()> { + use xcm_executor::traits::InvertLocation; + + let mut iter = xcm.0.iter_mut(); + match iter.next() { + Some(BuyExecution { + ref mut fees, + weight_limit, + }) => { + if let Some(asset) = holding.inner().iter().find(|x| { + let ancestry = LocationInverter::::ancestry(); + let mut tmp = (*x).clone(); + match tmp.reanchored(at, &ancestry) { + Ok(reanchored) => reanchored.id == fees.id, + _ => false, + } + }) { + match (&mut fees.fun, &asset.fun) { + (Fungibility::Fungible(ref mut a), Fungibility::Fungible(b)) => { + *a = *b; + } + _ => {} + } + } + } + _ => {} + } + xcm +} + +fn transform_outbound(message: Xcm, original_weight: Weight) -> Result<(Xcm, Weight), ()> { + let transformed_message = match &message.0.as_slice() { + &[TransferReserveAsset { assets, dest, xcm }] => { + // transfer_self_reserve_asset + let (taxed, tax) = assets.tax(); + let transfer_reserve_asset = TransferReserveAsset { + assets: taxed.clone(), + dest: dest.clone(), + xcm: limit_buy_execution(xcm.clone(), taxed.clone(), &dest), + }; + if tax.is_none() { + Some(Xcm(vec![transfer_reserve_asset])) + } else { + let transfer_to_dao = TransferAsset { + assets: tax, + beneficiary: Junction::AccountId32 { + network: NetworkId::Any, + id: TreasuryAccount::get().into(), + } + .into(), + }; + Some(Xcm(vec![transfer_reserve_asset, transfer_to_dao])) + } + } + &[WithdrawAsset(assets), InitiateReserveWithdraw { + assets: reserve_assets, + reserve, + xcm, + }] => { + // transfer_self_reserve_asset + let (taxed, tax) = assets.tax(); + let withdraw = WithdrawAsset(taxed.clone()); + + let mut reserve_withdraw = InitiateReserveWithdraw { + assets: reserve_assets.clone(), + reserve: reserve.clone(), + xcm: limit_buy_execution(xcm.clone(), taxed.clone(), &reserve), + }; + + if tax.is_none() { + Some(Xcm(vec![withdraw, reserve_withdraw.clone()])) + } else { + let transfer_to_dao = TransferAsset { + assets: tax, + beneficiary: Junction::AccountId32 { + network: NetworkId::Any, + id: TreasuryAccount::get().into(), + } + .into(), + }; + Some(Xcm(vec![withdraw, transfer_to_dao, reserve_withdraw.clone()])) + } + } + _ => None, + }; + match transformed_message { + Some(mut message) => { + let new_weight = ::Weigher::weight(&mut message)?; + Ok((message, new_weight)) + } + None => Ok((message, original_weight)), + } +} + +fn transform_inbound(mut message: Xcm) -> Result, ()> { + // WithdrawAsset | ReserveAssetDeposited | ClaimAsset] + // ClearOrigin* + // BuyExecution + // transfer + // tail + + match message.0.get(0) { + Some(WithdrawAsset(assets) | ReserveAssetDeposited(assets) | ClaimAsset { assets, .. }) => { + let (_taxed, tax) = assets.tax(); + let buy_execution = message + .0 + .iter() + .enumerate() + .skip(1) + .find(|(_idx, instruction)| !matches!(instruction, ClearOrigin)); + match buy_execution { + Some((idx, BuyExecution { .. })) => { + let transfer_to_dao = DepositAsset { + assets: MultiAssetFilter::Definite(tax.clone()), + max_assets: tax.len() as u32, + beneficiary: Junction::AccountId32 { + network: NetworkId::Any, + id: TreasuryAccount::get().into(), + } + .into(), + }; + message.0.insert(idx + 1, transfer_to_dao); + Ok((message)) + } + _ => Err(()), + } + } + _ => Err(()), + } +} + +pub struct TaxingExecutor; + +use xcm::latest::ExecuteXcm; +impl ExecuteXcm for TaxingExecutor { + fn execute_xcm_in_credit( + origin: impl Into, + mut message: Xcm, + weight_limit: Weight, + weight_credit: Weight, + ) -> Outcome { + use xcm::latest::Instruction::*; + + if weight_credit > 0 { + let xcm_weight = match ::Weigher::weight(&mut message) { + Ok(x) => x, + Err(()) => return Outcome::Error(XcmError::WeightNotComputable), + }; + + if xcm_weight <= weight_credit { + return match transform_outbound(message, xcm_weight) { + Ok((transformed_message, new_weight)) => { + as ExecuteXcm>::execute_xcm_in_credit( + origin, + transformed_message, + new_weight, + new_weight, + ) + } + Err(()) => Outcome::Error(XcmError::WeightNotComputable), + }; + } + } else { + return match transform_inbound(message) { + Ok(transformed_message) => { + as ExecuteXcm>::execute_xcm_in_credit( + origin, + transformed_message, + weight_limit, + weight_credit, + ) + } + Err(()) => Outcome::Error(XcmError::WeightNotComputable), + } + } + + todo!() + } +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -187,7 +399,7 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = XcmRouter; type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; + type XcmExecutor = TaxingExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; @@ -198,12 +410,12 @@ impl pallet_xcm::Config for Runtime { impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; + type XcmExecutor = TaxingExecutor; } impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; + type XcmExecutor = TaxingExecutor; type ChannelInfo = ParachainSystem; type VersionWrapper = PolkadotXcm; type ExecuteOverweightOrigin = EnsureRoot; @@ -214,7 +426,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { impl cumulus_pallet_dmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; + type XcmExecutor = TaxingExecutor; type ExecuteOverweightOrigin = frame_system::EnsureRoot; } @@ -350,7 +562,7 @@ impl orml_xtokens::Config for Runtime { type CurrencyIdConvert = CurrencyIdConvert; type AccountIdToMultiLocation = AccountIdToMultiLocation; type SelfLocation = SelfLocation; - type XcmExecutor = XcmExecutor; + type XcmExecutor = TaxingExecutor; type Weigher = FixedWeightBounds; type BaseXcmWeight = UnitWeightCost; type LocationInverter = ::LocationInverter; diff --git a/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs b/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs index 444f09d3bb..cb31866d4a 100644 --- a/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs +++ b/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs @@ -237,7 +237,7 @@ fn transfer_to_relay_chain() { ::WeightToFee::weight_to_fee(&used_weight); assert_eq!( kusama_runtime::Balances::free_balance(&AccountId::from(BOB)), - KSM.one() - fee + ((KSM.one() - fee)*9) / 10 ); // UI uses 165940672 - make sure that that's an overestimation