Skip to content

Commit

Permalink
Xcm trap assets ed (#1654)
Browse files Browse the repository at this point in the history
* DropAssets trait impl

* drop assets implements

* clean

* other runtime

* refactor ExistentialDepositsForDropAssets

* clippy and first time refactor

* add sibling trap tests

* move AcalaDropAssets to common

* add docs
  • Loading branch information
zqhxuyuan authored Dec 1, 2021
1 parent 3d6dbc9 commit 9a27cfd
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion runtime/acala/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub use primitives::{
AuctionId, AuthoritysOriginId, Balance, BlockNumber, CurrencyId, DataProviderId, EraIndex, Hash, Moment, Nonce,
ReserveIdentifier, Share, Signature, TokenSymbol, TradingPair,
};
use runtime_common::AcalaDropAssets;
pub use runtime_common::{
cent, dollar, microcent, millicent, EnsureRootOrAllGeneralCouncil, EnsureRootOrAllTechnicalCommittee,
EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, EnsureRootOrHalfHomaCouncil,
Expand Down Expand Up @@ -1452,7 +1453,14 @@ impl xcm_executor::Config for XcmConfig {
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
type Trader = Trader;
type ResponseHandler = PolkadotXcm;
type AssetTrap = PolkadotXcm;
type AssetTrap = AcalaDropAssets<
PolkadotXcm,
ToTreasury,
CurrencyIdConvert,
GetNativeCurrencyId,
NativeTokenExistentialDeposit,
ExistentialDeposits,
>;
type AssetClaims = PolkadotXcm;
type SubscriptionService = PolkadotXcm;
}
Expand Down
8 changes: 8 additions & 0 deletions runtime/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ module-transaction-payment = { path = "../../modules/transaction-payment", defau
module-nft = { path = "../../modules/nft", default-features = false }
module-dex = { path = "../../modules/dex", default-features = false }

xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false }

[dev-dependencies]
serde_json = "1.0.64"
hex-literal = "0.3.1"
Expand Down Expand Up @@ -87,6 +91,10 @@ std = [
"module-transaction-payment/std",
"module-nft/std",
"module-dex/std",

"xcm/std",
"xcm-executor/std",
"xcm-builder/std",
]
with-ethereum-compatibility = [
"module-evm/with-ethereum-compatibility",
Expand Down
73 changes: 73 additions & 0 deletions runtime/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#![cfg_attr(not(feature = "std"), no_std)]

use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::traits::Get;
use frame_support::{
parameter_types,
traits::Contains,
Expand Down Expand Up @@ -49,6 +50,7 @@ mod homa;
pub use homa::*;

pub mod precompile;
use orml_traits::GetByKey;
pub use precompile::{
AllPrecompiles, DexPrecompile, MultiCurrencyPrecompile, NFTPrecompile, OraclePrecompile, ScheduleCallPrecompile,
StateRentPrecompile,
Expand All @@ -57,6 +59,10 @@ pub use primitives::{
currency::{TokenInfo, ACA, AUSD, BNC, DOT, KAR, KSM, KUSD, LDOT, LKSM, PHA, RENBTC, VSKSM},
AccountId,
};
use sp_std::{marker::PhantomData, prelude::*};
pub use xcm::latest::prelude::*;
pub use xcm_builder::TakeRevenue;
pub use xcm_executor::{traits::DropAssets, Assets};

pub type TimeStampedPrice = orml_oracle::TimestampedValue<Price, primitives::Moment>;

Expand Down Expand Up @@ -331,6 +337,73 @@ pub enum RelayChainSubAccountId {
HomaLite = 0,
}

/// `DropAssets` implementation support asset amount lower thant ED handled by `TakeRevenue`.
///
/// parameters type:
/// - `NC`: native currency_id type.
/// - `NB`: the ExistentialDeposit amount of native currency_id.
/// - `GK`: the ExistentialDeposit amount of tokens.
pub struct AcalaDropAssets<X, T, C, NC, NB, GK>(PhantomData<(X, T, C, NC, NB, GK)>);
impl<X, T, C, NC, NB, GK> DropAssets for AcalaDropAssets<X, T, C, NC, NB, GK>
where
X: DropAssets,
T: TakeRevenue,
C: Convert<MultiLocation, Option<CurrencyId>>,
NC: Get<CurrencyId>,
NB: Get<Balance>,
GK: GetByKey<CurrencyId, Balance>,
{
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
let multi_assets: Vec<MultiAsset> = assets.into();
let mut asset_traps: Vec<MultiAsset> = vec![];
for asset in multi_assets {
if let MultiAsset {
id: Concrete(location),
fun: Fungible(amount),
} = asset.clone()
{
let currency_id = C::convert(location);
// burn asset(do nothing here) if convert result is None
if let Some(currency_id) = currency_id {
let ed = ExistentialDepositsForDropAssets::<NC, NB, GK>::get(&currency_id);
if amount < ed {
T::take_revenue(asset);
} else {
asset_traps.push(asset);
}
}
}
}
if !asset_traps.is_empty() {
X::drop_assets(origin, asset_traps.into());
}
0
}
}

/// `ExistentialDeposit` for tokens, give priority to match native token, then handled by
/// `ExistentialDeposits`.
///
/// parameters type:
/// - `NC`: native currency_id type.
/// - `NB`: the ExistentialDeposit amount of native currency_id.
/// - `GK`: the ExistentialDeposit amount of tokens.
pub struct ExistentialDepositsForDropAssets<NC, NB, GK>(PhantomData<(NC, NB, GK)>);
impl<NC, NB, GK> ExistentialDepositsForDropAssets<NC, NB, GK>
where
NC: Get<CurrencyId>,
NB: Get<Balance>,
GK: GetByKey<CurrencyId, Balance>,
{
fn get(currency_id: &CurrencyId) -> Balance {
if currency_id == &NC::get() {
NB::get()
} else {
GK::get(currency_id)
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::setup::*;

use frame_support::assert_ok;

use karura_runtime::AssetRegistry;
use karura_runtime::{AssetRegistry, KaruraTreasuryAccount};
use module_asset_registry::AssetMetadata;
use orml_traits::MultiCurrency;
use xcm_emulator::TestExt;
Expand Down Expand Up @@ -475,3 +475,178 @@ fn test_asset_registry_module() {
);
});
}

#[test]
fn trap_assets_larger_than_ed_works() {
TestNet::reset();

let mut kar_treasury_amount = 0;
let (ksm_asset_amount, kar_asset_amount) = (dollar(KSM), dollar(KAR));
let trader_weight_to_treasury: u128 = 96_000_000;

Karura::execute_with(|| {
assert_ok!(Tokens::deposit(KSM, &AccountId::from(DEFAULT), 100 * dollar(KSM)));
let _ = pallet_balances::Pallet::<Runtime>::deposit_creating(&AccountId::from(DEFAULT), 100 * dollar(KAR));

kar_treasury_amount = Currencies::free_balance(KAR, &KaruraTreasuryAccount::get());
});

let assets: MultiAsset = (Parent, ksm_asset_amount).into();
KusamaNet::execute_with(|| {
let xcm = vec![
WithdrawAsset(assets.clone().into()),
BuyExecution {
fees: assets,
weight_limit: Limited(dollar(KSM) as u64),
},
WithdrawAsset(
(
(Parent, X2(Parachain(2000), GeneralKey(KAR.encode()))),
kar_asset_amount,
)
.into(),
),
];
assert_ok!(pallet_xcm::Pallet::<kusama_runtime::Runtime>::send_xcm(
Here,
Parachain(2000).into(),
Xcm(xcm),
));
});
Karura::execute_with(|| {
assert!(System::events()
.iter()
.any(|r| matches!(r.event, Event::PolkadotXcm(pallet_xcm::Event::AssetsTrapped(_, _, _)))));

assert_eq!(
trader_weight_to_treasury,
Currencies::free_balance(KSM, &KaruraTreasuryAccount::get())
);
assert_eq!(
kar_treasury_amount,
Currencies::free_balance(KAR, &KaruraTreasuryAccount::get())
);
});
}

#[test]
fn trap_assets_lower_than_ed_works() {
TestNet::reset();

let mut kar_treasury_amount = 0;
let (ksm_asset_amount, kar_asset_amount) = (cent(KSM) / 100, cent(KAR));

Karura::execute_with(|| {
assert_ok!(Tokens::deposit(KSM, &AccountId::from(DEFAULT), dollar(KSM)));
let _ = pallet_balances::Pallet::<Runtime>::deposit_creating(&AccountId::from(DEFAULT), dollar(KAR));
kar_treasury_amount = Currencies::free_balance(KAR, &KaruraTreasuryAccount::get());
});

let assets: MultiAsset = (Parent, ksm_asset_amount).into();
KusamaNet::execute_with(|| {
let xcm = vec![
WithdrawAsset(assets.clone().into()),
BuyExecution {
fees: assets,
weight_limit: Limited(dollar(KSM) as u64),
},
WithdrawAsset(
(
(Parent, X2(Parachain(2000), GeneralKey(KAR.encode()))),
kar_asset_amount,
)
.into(),
),
// two asset left in holding register, they both lower than ED, so goes to treasury.
];
assert_ok!(pallet_xcm::Pallet::<kusama_runtime::Runtime>::send_xcm(
Here,
Parachain(2000).into(),
Xcm(xcm),
));
});

Karura::execute_with(|| {
assert_eq!(
System::events()
.iter()
.find(|r| matches!(r.event, Event::PolkadotXcm(pallet_xcm::Event::AssetsTrapped(_, _, _)))),
None
);

assert_eq!(
ksm_asset_amount,
Currencies::free_balance(KSM, &KaruraTreasuryAccount::get())
);
assert_eq!(
kar_asset_amount,
Currencies::free_balance(KAR, &KaruraTreasuryAccount::get()) - kar_treasury_amount
);
});
}

#[test]
fn sibling_trap_assets_works() {
TestNet::reset();

let mut kar_treasury_amount = 0;
let (bnc_asset_amount, kar_asset_amount) = (cent(BNC) / 10, cent(KAR));

fn sibling_account() -> AccountId {
use sp_runtime::traits::AccountIdConversion;
polkadot_parachain::primitives::Sibling::from(2001).into_account()
}

Karura::execute_with(|| {
assert_ok!(Tokens::deposit(BNC, &sibling_account(), dollar(BNC)));
let _ = pallet_balances::Pallet::<Runtime>::deposit_creating(&sibling_account(), dollar(KAR));
kar_treasury_amount = Currencies::free_balance(KAR, &KaruraTreasuryAccount::get());
});

Sibling::execute_with(|| {
let assets: MultiAsset = (
(Parent, X2(Parachain(2000), GeneralKey(KAR.encode()))),
kar_asset_amount,
)
.into();
let xcm = vec![
WithdrawAsset(assets.clone().into()),
BuyExecution {
fees: assets,
weight_limit: Unlimited,
},
WithdrawAsset(
(
(
Parent,
X2(Parachain(2001), GeneralKey(parachains::bifrost::BNC_KEY.to_vec())),
),
bnc_asset_amount,
)
.into(),
),
];
assert_ok!(pallet_xcm::Pallet::<Runtime>::send_xcm(
Here,
(Parent, Parachain(2000)),
Xcm(xcm),
));
});

Karura::execute_with(|| {
assert_eq!(
System::events()
.iter()
.find(|r| matches!(r.event, Event::PolkadotXcm(pallet_xcm::Event::AssetsTrapped(_, _, _)))),
None
);
assert_eq!(
Currencies::free_balance(KAR, &KaruraTreasuryAccount::get()) - kar_treasury_amount,
kar_asset_amount
);
assert_eq!(
Currencies::free_balance(BNC, &KaruraTreasuryAccount::get()),
bnc_asset_amount
);
});
}
1 change: 1 addition & 0 deletions runtime/integration-tests/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ const ORACLE3: [u8; 32] = [2u8; 32];
const ORACLE4: [u8; 32] = [3u8; 32];
const ORACLE5: [u8; 32] = [4u8; 32];

pub const DEFAULT: [u8; 32] = [0u8; 32];
pub const ALICE: [u8; 32] = [4u8; 32];
pub const BOB: [u8; 32] = [5u8; 32];
pub const CHARLIE: [u8; 32] = [6u8; 32];
Expand Down
10 changes: 9 additions & 1 deletion runtime/karura/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub use primitives::{
AuctionId, AuthoritysOriginId, Balance, BlockNumber, CurrencyId, DataProviderId, EraIndex, Hash, Moment, Nonce,
ReserveIdentifier, Share, Signature, TokenSymbol, TradingPair,
};
use runtime_common::AcalaDropAssets;
pub use runtime_common::{
cent, dollar, microcent, millicent, EnsureRootOrAllGeneralCouncil, EnsureRootOrAllTechnicalCommittee,
EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, EnsureRootOrHalfHomaCouncil,
Expand Down Expand Up @@ -1515,7 +1516,14 @@ impl xcm_executor::Config for XcmConfig {
type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
type Trader = Trader;
type ResponseHandler = PolkadotXcm;
type AssetTrap = PolkadotXcm;
type AssetTrap = AcalaDropAssets<
PolkadotXcm,
ToTreasury,
CurrencyIdConvert,
GetNativeCurrencyId,
NativeTokenExistentialDeposit,
ExistentialDeposits,
>;
type AssetClaims = PolkadotXcm;
type SubscriptionService = PolkadotXcm;
}
Expand Down
Loading

0 comments on commit 9a27cfd

Please sign in to comment.