mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 12:51:02 +00:00
Statemint runtimes to accept sufficient assets as xcm fee payment (#1278)
* point to my branch * girazoki-add-TakeFirstAssetTrader-to-utility * Commit lock * point at custom branch * add new trader to statemine runtimes * compiles * Back to master * Update last tomls * Imports up * remove non-needing imports * FMT * log messages properly * Use TakeRevenue instead of HandleCredit * Introduce xcm fee handler * check total supply in tests * FMT * fix test * Start decoupling balance calculation into different traits * Make traits a bit more generic * PR suggestions * add import * import well * Place xcmfeesassethandler into parachains common * fix tests * config parameters * Min amount to fee receiver * Make minimum amount for block author to be at least the ED * Doc in AssetFeeAsExistentialDepositMultiplier * saturating sub * make sure we dont enter twice * FMT * fmt again * adapt tests * Add doc and struct for weight refund * Doc * More doc * PR suggestions * store all info related to asset payment as multiasset * return AssetNotFound instead of TooExpensive * Use asset transactor to deposit fee * uninstall from statemint * R for RUntime and CON for BalanceConverter * Rework logic to avoid unnecesary match and error * Rework ED check, also in case of refund * rework typo * In case refund makes drop below ED, just refund the difference * fix test westmint * clone id * move test imports to preamble * move test imports to preamble * test-utils with builderS * lock file updated * remove unused imports Co-authored-by: Stephen Shelton <steve@brewcraft.org> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: joepetrowski <joe@parity.io>
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
use asset_test_utils::{ExtBuilder, RuntimeHelper};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok, traits::PalletInfo, weights::WeightToFee as WeightToFeeT,
|
||||
};
|
||||
use parachains_common::{AccountId, AuraId};
|
||||
pub use westmint_runtime::{
|
||||
constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit,
|
||||
Runtime, SessionKeys, System,
|
||||
};
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_executor::traits::WeightTrader;
|
||||
|
||||
pub const ALICE: [u8; 32] = [1u8; 32];
|
||||
|
||||
#[test]
|
||||
fn test_asset_xcm_trader() {
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(vec![AccountId::from(ALICE)])
|
||||
.with_session_keys(vec![(
|
||||
AccountId::from(ALICE),
|
||||
AccountId::from(ALICE),
|
||||
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
|
||||
)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// We need root origin to create a sufficient asset
|
||||
// We set existential deposit to be identical to the one for Balances first
|
||||
assert_ok!(Assets::force_create(
|
||||
RuntimeHelper::<Runtime>::root_origin(),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
true,
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
// We first mint enough asset for the account to exist for assets
|
||||
assert_ok!(Assets::mint(
|
||||
RuntimeHelper::<Runtime>::origin_of(AccountId::from(ALICE)),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
|
||||
|
||||
// Set Alice as block author, who will receive fees
|
||||
RuntimeHelper::<Runtime>::run_to_block(2, Some(AccountId::from(ALICE)));
|
||||
|
||||
// We are going to buy 4e9 weight
|
||||
let bought = 4_000_000_000u64;
|
||||
|
||||
// lets calculate amount needed
|
||||
let amount_needed = WeightToFee::weight_to_fee(&bought);
|
||||
|
||||
let asset_multilocation = MultiLocation::new(
|
||||
0,
|
||||
X2(
|
||||
PalletInstance(
|
||||
<Runtime as frame_system::Config>::PalletInfo::index::<Assets>().unwrap()
|
||||
as u8,
|
||||
),
|
||||
GeneralIndex(1),
|
||||
),
|
||||
);
|
||||
|
||||
let asset: MultiAsset = (asset_multilocation, amount_needed).into();
|
||||
|
||||
// Make sure buy_weight does not return an error
|
||||
assert_ok!(trader.buy_weight(bought, asset.into()));
|
||||
|
||||
// Drop trader
|
||||
drop(trader);
|
||||
|
||||
// Make sure author(Alice) has received the amount
|
||||
assert_eq!(
|
||||
Assets::balance(1, AccountId::from(ALICE)),
|
||||
ExistentialDeposit::get() + amount_needed
|
||||
);
|
||||
|
||||
// We also need to ensure the total supply increased
|
||||
assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + amount_needed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_asset_xcm_trader_with_refund() {
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(vec![AccountId::from(ALICE)])
|
||||
.with_session_keys(vec![(
|
||||
AccountId::from(ALICE),
|
||||
AccountId::from(ALICE),
|
||||
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
|
||||
)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// We need root origin to create a sufficient asset
|
||||
// We set existential deposit to be identical to the one for Balances first
|
||||
assert_ok!(Assets::force_create(
|
||||
RuntimeHelper::<Runtime>::root_origin(),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
true,
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
// We first mint enough asset for the account to exist for assets
|
||||
assert_ok!(Assets::mint(
|
||||
RuntimeHelper::<Runtime>::origin_of(AccountId::from(ALICE)),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
|
||||
|
||||
// Set Alice as block author, who will receive fees
|
||||
RuntimeHelper::<Runtime>::run_to_block(2, Some(AccountId::from(ALICE)));
|
||||
|
||||
// We are going to buy 4e9 weight
|
||||
let bought = 4_000_000_000u64;
|
||||
let asset_multilocation = MultiLocation::new(
|
||||
0,
|
||||
X2(
|
||||
PalletInstance(
|
||||
<Runtime as frame_system::Config>::PalletInfo::index::<Assets>().unwrap()
|
||||
as u8,
|
||||
),
|
||||
GeneralIndex(1),
|
||||
),
|
||||
);
|
||||
|
||||
// lets calculate amount needed
|
||||
let amount_bought = WeightToFee::weight_to_fee(&bought);
|
||||
|
||||
let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into();
|
||||
|
||||
// Make sure buy_weight does not return an error
|
||||
assert_ok!(trader.buy_weight(bought, asset.clone().into()));
|
||||
|
||||
// Make sure again buy_weight does return an error
|
||||
assert_noop!(trader.buy_weight(bought, asset.into()), XcmError::NotWithdrawable);
|
||||
|
||||
// We actually use half of the weight
|
||||
let weight_used = bought / 2;
|
||||
|
||||
// Make sure refurnd works.
|
||||
let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used));
|
||||
|
||||
assert_eq!(
|
||||
trader.refund_weight(bought - weight_used),
|
||||
Some((asset_multilocation, amount_refunded).into())
|
||||
);
|
||||
|
||||
// Drop trader
|
||||
drop(trader);
|
||||
|
||||
// We only should have paid for half of the bought weight
|
||||
let fees_paid = WeightToFee::weight_to_fee(&weight_used);
|
||||
|
||||
assert_eq!(
|
||||
Assets::balance(1, AccountId::from(ALICE)),
|
||||
ExistentialDeposit::get() + fees_paid
|
||||
);
|
||||
|
||||
// We also need to ensure the total supply increased
|
||||
assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + fees_paid);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() {
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(vec![AccountId::from(ALICE)])
|
||||
.with_session_keys(vec![(
|
||||
AccountId::from(ALICE),
|
||||
AccountId::from(ALICE),
|
||||
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
|
||||
)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// We need root origin to create a sufficient asset
|
||||
// We set existential deposit to be identical to the one for Balances first
|
||||
assert_ok!(Assets::force_create(
|
||||
RuntimeHelper::<Runtime>::root_origin(),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
true,
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
|
||||
|
||||
// Set Alice as block author, who will receive fees
|
||||
RuntimeHelper::<Runtime>::run_to_block(2, Some(AccountId::from(ALICE)));
|
||||
|
||||
// We are going to buy 4e9 weight
|
||||
let bought = 500_000_000u64;
|
||||
|
||||
let asset_multilocation = MultiLocation::new(
|
||||
0,
|
||||
X2(
|
||||
PalletInstance(
|
||||
<Runtime as frame_system::Config>::PalletInfo::index::<Assets>().unwrap()
|
||||
as u8,
|
||||
),
|
||||
GeneralIndex(1),
|
||||
),
|
||||
);
|
||||
|
||||
let amount_bought = WeightToFee::weight_to_fee(&bought);
|
||||
|
||||
assert!(
|
||||
amount_bought < ExistentialDeposit::get(),
|
||||
"we are testing what happens when the amount does not exceed ED"
|
||||
);
|
||||
|
||||
let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into();
|
||||
|
||||
// Buy weight should return an error
|
||||
assert_noop!(trader.buy_weight(bought, asset.into()), XcmError::TooExpensive);
|
||||
|
||||
// not credited since the ED is higher than this value
|
||||
assert_eq!(Assets::balance(1, AccountId::from(ALICE)), 0);
|
||||
|
||||
// We also need to ensure the total supply did not increase
|
||||
assert_eq!(Assets::total_supply(1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_buying_ed_refund_does_not_refund() {
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(vec![AccountId::from(ALICE)])
|
||||
.with_session_keys(vec![(
|
||||
AccountId::from(ALICE),
|
||||
AccountId::from(ALICE),
|
||||
SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
|
||||
)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// We need root origin to create a sufficient asset
|
||||
// We set existential deposit to be identical to the one for Balances first
|
||||
assert_ok!(Assets::force_create(
|
||||
RuntimeHelper::<Runtime>::root_origin(),
|
||||
1,
|
||||
AccountId::from(ALICE).into(),
|
||||
true,
|
||||
ExistentialDeposit::get()
|
||||
));
|
||||
|
||||
let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
|
||||
|
||||
// Set Alice as block author, who will receive fees
|
||||
RuntimeHelper::<Runtime>::run_to_block(2, Some(AccountId::from(ALICE)));
|
||||
|
||||
let bought = 500_000_000u64;
|
||||
|
||||
let asset_multilocation = MultiLocation::new(
|
||||
0,
|
||||
X2(
|
||||
PalletInstance(
|
||||
<Runtime as frame_system::Config>::PalletInfo::index::<Assets>().unwrap()
|
||||
as u8,
|
||||
),
|
||||
GeneralIndex(1),
|
||||
),
|
||||
);
|
||||
|
||||
let amount_bought = WeightToFee::weight_to_fee(&bought);
|
||||
|
||||
assert!(
|
||||
amount_bought < ExistentialDeposit::get(),
|
||||
"we are testing what happens when the amount does not exceed ED"
|
||||
);
|
||||
|
||||
// We know we will have to buy at least ED, so lets make sure first it will
|
||||
// fail with a payment of less than ED
|
||||
let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into();
|
||||
assert_noop!(trader.buy_weight(bought, asset.into()), XcmError::TooExpensive);
|
||||
|
||||
// Now lets buy ED at least
|
||||
let asset: MultiAsset = (asset_multilocation.clone(), ExistentialDeposit::get()).into();
|
||||
|
||||
// Buy weight should work
|
||||
assert_ok!(trader.buy_weight(bought, asset.into()));
|
||||
|
||||
// Should return None. We have a specific check making sure we dont go below ED for
|
||||
// drop payment
|
||||
assert_eq!(trader.refund_weight(bought), None);
|
||||
|
||||
// Drop trader
|
||||
drop(trader);
|
||||
|
||||
// Make sure author(Alice) has received the amount
|
||||
assert_eq!(Assets::balance(1, AccountId::from(ALICE)), ExistentialDeposit::get());
|
||||
|
||||
// We also need to ensure the total supply increased
|
||||
assert_eq!(Assets::total_supply(1), ExistentialDeposit::get());
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user