Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xcm trap assets ed #1654

Merged
merged 9 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions runtime/acala/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use sp_runtime::{
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, DispatchResult, FixedPointNumber, Perbill, Percent, Permill, Perquintill,
};
use sp_std::prelude::*;
use sp_std::{marker::PhantomData, prelude::*};
#[cfg(feature = "std")]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
Expand All @@ -58,7 +58,8 @@ use module_relaychain::RelayChainCallBuilder;
use module_support::{DispatchableTask, ForeignAssetIdMapping};
use module_transaction_payment::{Multiplier, TargetedFeeAdjustment};
use orml_traits::{
create_median_value_data_provider, parameter_type_with_key, DataFeeder, DataProviderExtended, MultiCurrency,
create_median_value_data_provider, parameter_type_with_key, DataFeeder, DataProviderExtended, GetByKey,
MultiCurrency,
};
use pallet_transaction_payment::RuntimeDispatchInfo;

Expand Down Expand Up @@ -95,6 +96,7 @@ pub use pallet_staking::StakerStatus;
pub use pallet_timestamp::Call as TimestampCall;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
use xcm_executor::traits::DropAssets;

pub use authority::AuthorityConfigImpl;
pub use constants::{fee::*, time::*};
Expand Down Expand Up @@ -728,6 +730,17 @@ impl DataFeeder<CurrencyId, Price, AccountId> for AggregatedDataProvider {
}
}

pub struct ExistentialDepositsForDropAssets;
impl ExistentialDepositsForDropAssets {
fn get(currency_id: &CurrencyId) -> Balance {
if currency_id == &GetNativeCurrencyId::get() {
NativeTokenExistentialDeposit::get()
} else {
<ExistentialDeposits as GetByKey<CurrencyId, Balance>>::get(currency_id)
}
}
}

parameter_type_with_key! {
pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance {
match currency_id {
Expand Down Expand Up @@ -1429,6 +1442,40 @@ impl TakeRevenue for ToTreasury {
}
}

pub struct AcalaDropAssets<X, T>(PhantomData<(X, T)>);
impl<X, T> DropAssets for AcalaDropAssets<X, T>
where
X: DropAssets,
T: TakeRevenue,
{
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
zqhxuyuan marked this conversation as resolved.
Show resolved Hide resolved
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 = CurrencyIdConvert::convert(location);
// burn asset(do nothing here) if convert result is None
if let Some(currency_id) = currency_id {
zqhxuyuan marked this conversation as resolved.
Show resolved Hide resolved
let ed = ExistentialDepositsForDropAssets::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
}
}

pub type Trader = (
FixedRateOfFungible<DotPerSecond, ToTreasury>,
FixedRateOfFungible<AusdPerSecond, ToTreasury>,
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 @@ -150,6 +150,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
57 changes: 53 additions & 4 deletions runtime/karura/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use sp_runtime::{
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, DispatchResult, FixedPointNumber, Perbill, Percent, Permill, Perquintill,
};
use sp_std::prelude::*;
use sp_std::{marker::PhantomData, prelude::*};
#[cfg(feature = "std")]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
Expand All @@ -59,7 +59,8 @@ use module_support::{DispatchableTask, ForeignAssetIdMapping};
use module_transaction_payment::{Multiplier, TargetedFeeAdjustment};

use orml_traits::{
create_median_value_data_provider, parameter_type_with_key, DataFeeder, DataProviderExtended, MultiCurrency,
create_median_value_data_provider, parameter_type_with_key, DataFeeder, DataProviderExtended, GetByKey,
MultiCurrency,
};
use pallet_transaction_payment::RuntimeDispatchInfo;

Expand All @@ -76,7 +77,10 @@ pub use xcm_builder::{
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
TakeRevenue, TakeWeightCredit,
};
pub use xcm_executor::{traits::WeightTrader, Assets, Config, XcmExecutor};
pub use xcm_executor::{
traits::{DropAssets, WeightTrader},
Assets, Config, XcmExecutor,
};

/// Weights for pallets used in the runtime.
mod weights;
Expand Down Expand Up @@ -738,6 +742,17 @@ impl DataFeeder<CurrencyId, Price, AccountId> for AggregatedDataProvider {
}
}

pub struct ExistentialDepositsForDropAssets;
impl ExistentialDepositsForDropAssets {
fn get(currency_id: &CurrencyId) -> Balance {
if currency_id == &GetNativeCurrencyId::get() {
NativeTokenExistentialDeposit::get()
} else {
<ExistentialDeposits as GetByKey<CurrencyId, Balance>>::get(currency_id)
}
}
}

parameter_type_with_key! {
pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance {
match currency_id {
Expand Down Expand Up @@ -1468,6 +1483,40 @@ impl TakeRevenue for ToTreasury {
}
}

pub struct AcalaDropAssets<X, T>(PhantomData<(X, T)>);
impl<X, T> DropAssets for AcalaDropAssets<X, T>
where
X: DropAssets,
T: TakeRevenue,
{
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 = CurrencyIdConvert::convert(location);
// burn asset(do nothing here) if convert result is None
if let Some(currency_id) = currency_id {
let ed = ExistentialDepositsForDropAssets::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
}
}

parameter_types! {
pub BncPerSecond: (AssetId, u128) = (
MultiLocation::new(
Expand Down Expand Up @@ -1513,7 +1562,7 @@ 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>;
type AssetClaims = PolkadotXcm;
type SubscriptionService = PolkadotXcm;
}
Expand Down
Loading