Skip to content

Commit

Permalink
Test USDT transaction payment
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed Oct 4, 2024
1 parent b1f0a8d commit 9331229
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 26 deletions.
4 changes: 4 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 integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,15 @@ pallet-democracy.workspace = true
pallet-scheduler.workspace = true
pallet-treasury.workspace = true
frame-metadata-hash-extension.workspace = true

pallet-asset-tx-payment.workspace = true
# Runtimes
polkadot-runtime.workspace = true
asset-hub-polkadot-runtime.workspace = true
polimec-runtime.workspace = true
penpal-runtime = { path = "penpal", default-features = false }
sp-consensus-aura.workspace = true
pallet-aura.workspace = true
pallet-session.workspace = true

[features]
default = [ "development-settings", "instant-mode", "std" ]
Expand Down Expand Up @@ -140,6 +143,10 @@ std = [
"xcm-builder/std",
"xcm-executor/std",
"xcm/std",
"pallet-asset-tx-payment/std",
"pallet-aura/std",
"pallet-session/std",
"sp-consensus-aura/std"
]
development-settings = [ "polimec-runtime/development-settings" ]
runtime-benchmarks = [
Expand Down Expand Up @@ -179,5 +186,6 @@ runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"pallet-asset-tx-payment/runtime-benchmarks"
]

18 changes: 7 additions & 11 deletions integration-tests/src/tests/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,16 @@ fn dispenser_signed_extensions_pass_for_new_account() {
frame_system::CheckEra::<PolimecRuntime>::from(Era::mortal(0u64, 0u64)),
pallet_dispenser::extensions::CheckNonce::<PolimecRuntime>::from(0u32),
frame_system::CheckWeight::<PolimecRuntime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<PolimecRuntime>::from(0u64.into()).into(),
pallet_asset_tx_payment::ChargeAssetTxPayment::<PolimecRuntime>::from(0u64.into(), None).into(),
frame_metadata_hash_extension::CheckMetadataHash::<PolimecRuntime>::new(true),
);
assert_err!(
extra.validate(&who, &paid_call, &paid_call.get_dispatch_info(), 0),
TransactionValidityError::Invalid(Payment)
);
assert_err!(
extra.clone().pre_dispatch(&who, &paid_call, &paid_call.get_dispatch_info(), 0),
TransactionValidityError::Invalid(Payment)
);

assert_ok!(extra.validate(&who, &free_call, &free_call.get_dispatch_info(), 0));
assert_ok!(extra.pre_dispatch(&who, &free_call, &free_call.get_dispatch_info(), 0));
// `InitialPayment` struct from pallet_asset_tx_payment doesn't implement Debug and PartialEq to compare to a specific Error or use assert_ok!
assert!(extra.validate(&who, &paid_call, &paid_call.get_dispatch_info(), 0).is_err());
assert!(extra.clone().pre_dispatch(&who, &paid_call, &paid_call.get_dispatch_info(), 0).is_err());

assert!(extra.validate(&who, &free_call, &free_call.get_dispatch_info(), 0).is_ok());
assert!(extra.pre_dispatch(&who, &free_call, &free_call.get_dispatch_info(), 0).is_ok());
});
}

Expand Down
179 changes: 178 additions & 1 deletion integration-tests/src/tests/transaction_payment.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,181 @@
use crate::{
constants, constants::PricesBuilder, polimec, PolimecAccountId, PolimecBalances, PolimecCall, PolimecForeignAssets,
PolimecNet, PolimecOrigin, PolimecRuntime, PolimecSystem,
};
use frame_support::{
dispatch::GetDispatchInfo,
traits::{fungible::Mutate as FungibleMutate, fungibles, fungibles::Mutate as FungiblesMutate},
};
use macros::generate_accounts;
use pallet_funding::{AcceptedFundingAsset, PriceProviderOf};
use pallet_transaction_payment::FeeDetails;
use parity_scale_codec::Encode;
use polimec_common::{ProvideAssetPrice, PLMC_DECIMALS, PLMC_FOREIGN_ID, USD_DECIMALS};
use polimec_runtime::Header;
use sp_arithmetic::{FixedPointNumber, FixedU128};
use sp_core::H256;
use sp_runtime::{
traits::{Dispatchable, Header as HeaderT, SignedExtension},
DigestItem,
};
use xcm_emulator::TestExt;
generate_accounts!(ALICE, AUTHOR);
use frame_support::traits::fungible::Inspect;

// Setup code inspired by pallet-authorship tests
fn seal_header(mut header: Header, aura_index: u64) -> Header {
{
let digest = header.digest_mut();
digest.logs.push(DigestItem::PreRuntime(sp_consensus_aura::AURA_ENGINE_ID, aura_index.encode()));
digest.logs.push(DigestItem::Seal(sp_consensus_aura::AURA_ENGINE_ID, aura_index.encode()));
}

header
}

fn create_header(number: u32, parent_hash: H256, state_root: H256) -> Header {
Header::new(number, Default::default(), state_root, parent_hash, Default::default())
}
// Make sure to run this inside externalities environment. Can only be called one time per test.
fn set_author(aura_index: u64) {
let mut header = seal_header(create_header(1, Default::default(), [1; 32].into()), aura_index);

header.digest_mut().pop(); // pop the seal off.
PolimecSystem::reset_events();
PolimecSystem::initialize(&1, &Default::default(), header.digest());
}

#[test]
fn fee_paid_with_foreign_assets() {
todo!();
polimec::set_prices(
PricesBuilder::new().usdt(FixedU128::from_float(1.0f64)).plmc(FixedU128::from_float(0.5f64)).build(),
);

PolimecNet::execute_with(|| {
let alice: PolimecAccountId = ALICE.into();

let (block_author, _) = &constants::collators::invulnerables()[1];
// Block author's aura index is 1
set_author(1u64);
assert_eq!(polimec_runtime::Authorship::author(), Some(block_author.clone()));

let usdt_id = AcceptedFundingAsset::USDT.id();
let usdt_decimals = <PolimecForeignAssets as fungibles::metadata::Inspect<PolimecAccountId>>::decimals(usdt_id);
let usdt_unit = 10u128.pow(usdt_decimals as u32);
let plmc_decimals = PLMC_DECIMALS;
let plmc_unit = 10u128.pow(plmc_decimals as u32);

PolimecBalances::set_balance(&alice, 0u128);
PolimecForeignAssets::set_balance(usdt_id, &alice, 100 * usdt_unit);
// Fees are usually very small, so we need to give the treasury an ED.
PolimecForeignAssets::set_balance(
usdt_id,
&polimec_runtime::BlockchainOperationTreasury::get(),
100 * usdt_unit,
);
// Block author doesn't need to have any balance, as the tip is bigger than ED.

let paid_call = PolimecCall::System(frame_system::Call::remark { remark: vec![69, 69] });

type TxPaymentExtension = pallet_asset_tx_payment::ChargeAssetTxPayment<PolimecRuntime>;
let signed_extension =
pallet_asset_tx_payment::ChargeAssetTxPayment::<PolimecRuntime>::from(10 * plmc_unit, Some(usdt_id));

let dispatch_info = paid_call.get_dispatch_info();
let FeeDetails { inclusion_fee, tip } =
polimec_runtime::TransactionPayment::compute_fee_details(0u32, &dispatch_info, 10u128 * plmc_unit);
let expected_plmc_fee = inclusion_fee.expect("call should charge a fee").inclusion_fee();
let expected_plmc_tip = tip;

let plmc_price_decimal_aware =
<PriceProviderOf<PolimecRuntime>>::get_decimals_aware_price(PLMC_FOREIGN_ID, USD_DECIMALS, plmc_decimals)
.expect("Price irretrievable");

// USDT should be configured with the same decimals as our underlying USD unit, and we set the price to 1USD at the beginning of this test.
let expected_usd_fee = plmc_price_decimal_aware.saturating_mul_int(expected_plmc_fee);
let expected_usd_tip = plmc_price_decimal_aware.saturating_mul_int(expected_plmc_tip);

let prev_alice_usdt_balance = PolimecForeignAssets::balance(usdt_id, alice.clone());
let prev_alice_plmc_balance = PolimecBalances::balance(&alice);
let prev_blockchain_operation_treasury_usdt_balance =
PolimecForeignAssets::balance(usdt_id, polimec_runtime::BlockchainOperationTreasury::get());
let prev_blockchain_operation_treasury_plmc_balance =
PolimecBalances::balance(&polimec_runtime::BlockchainOperationTreasury::get());
let prev_block_author_usdt_balance = PolimecForeignAssets::balance(usdt_id, block_author.clone());
let prev_block_author_plmc_balance = PolimecBalances::balance(&block_author.clone());

let pre = signed_extension.pre_dispatch(&alice, &paid_call, &dispatch_info, 8).unwrap();
let post_info = paid_call.clone().dispatch(PolimecOrigin::signed(alice.clone())).expect("call dispatch failed");

TxPaymentExtension::post_dispatch(Some(pre), &dispatch_info, &post_info, 0usize, &Ok(())).unwrap();

let post_alice_usdt_balance = PolimecForeignAssets::balance(usdt_id, alice.clone());
let post_alice_plmc_balance = PolimecBalances::balance(&alice);
let post_blockchain_operation_treasury_usdt_balance =
PolimecForeignAssets::balance(usdt_id, polimec_runtime::BlockchainOperationTreasury::get());
let post_blockchain_operation_treasury_plmc_balance =
PolimecBalances::balance(&polimec_runtime::BlockchainOperationTreasury::get());
let post_block_author_usdt_balance = PolimecForeignAssets::balance(usdt_id, block_author.clone());
let post_block_author_plmc_balance = PolimecBalances::balance(&block_author.clone());

assert_eq!(prev_alice_usdt_balance - post_alice_usdt_balance, expected_usd_fee + expected_usd_tip);
assert_eq!(post_alice_plmc_balance - prev_alice_plmc_balance, 0u128);
assert_eq!(
post_blockchain_operation_treasury_usdt_balance - prev_blockchain_operation_treasury_usdt_balance,
expected_usd_fee
);
assert_eq!(
post_blockchain_operation_treasury_plmc_balance - prev_blockchain_operation_treasury_plmc_balance,
0u128
);
assert_eq!(post_block_author_usdt_balance - prev_block_author_usdt_balance, expected_usd_tip);
assert_eq!(post_block_author_plmc_balance - prev_block_author_plmc_balance, 0u128);

// * Now we check if the same behavior but using PLMC as a fee asset, produces the same results. (2plmc=1usdt) *
PolimecBalances::set_balance(&alice, 100 * plmc_unit);
PolimecForeignAssets::set_balance(usdt_id, &alice, 0u128);
// Fees are usually very small, so we need to give the treasury an ED.
PolimecBalances::set_balance(&polimec_runtime::BlockchainOperationTreasury::get(), 100 * plmc_unit);
// Block author doesn't need to have any balance, as the tip is bigger than ED.

// Now we set the fee asset to None, so the fee is paid with PLMC
let signed_extension =
pallet_asset_tx_payment::ChargeAssetTxPayment::<PolimecRuntime>::from(10 * plmc_unit, None);

let prev_alice_usdt_balance = PolimecForeignAssets::balance(usdt_id, alice.clone());
let prev_alice_plmc_balance = PolimecBalances::balance(&alice);
let prev_blockchain_operation_treasury_usdt_balance =
PolimecForeignAssets::balance(usdt_id, polimec_runtime::BlockchainOperationTreasury::get());
let prev_blockchain_operation_treasury_plmc_balance =
PolimecBalances::balance(&polimec_runtime::BlockchainOperationTreasury::get());
let prev_block_author_usdt_balance = PolimecForeignAssets::balance(usdt_id, block_author.clone());
let prev_block_author_plmc_balance = PolimecBalances::balance(&block_author.clone());

let pre = signed_extension.pre_dispatch(&alice, &paid_call, &dispatch_info, 8).unwrap();
let post_info = paid_call.dispatch(PolimecOrigin::signed(alice.clone())).expect("call dispatch failed");

TxPaymentExtension::post_dispatch(Some(pre), &dispatch_info, &post_info, 0usize, &Ok(())).unwrap();

let post_alice_usdt_balance = PolimecForeignAssets::balance(usdt_id, alice.clone());
let post_alice_plmc_balance = PolimecBalances::balance(&alice);
let post_blockchain_operation_treasury_usdt_balance =
PolimecForeignAssets::balance(usdt_id, polimec_runtime::BlockchainOperationTreasury::get());
let post_blockchain_operation_treasury_plmc_balance =
PolimecBalances::balance(&polimec_runtime::BlockchainOperationTreasury::get());
let post_block_author_usdt_balance = PolimecForeignAssets::balance(usdt_id, block_author.clone());
let post_block_author_plmc_balance = PolimecBalances::balance(&block_author.clone());

assert_eq!(post_alice_usdt_balance - prev_alice_usdt_balance, 0u128);
assert_eq!(prev_alice_plmc_balance - post_alice_plmc_balance, expected_plmc_fee + expected_plmc_tip);
assert_eq!(
post_blockchain_operation_treasury_usdt_balance - prev_blockchain_operation_treasury_usdt_balance,
0u128
);
assert_eq!(
post_blockchain_operation_treasury_plmc_balance - prev_blockchain_operation_treasury_plmc_balance,
expected_plmc_fee
);
assert_eq!(post_block_author_usdt_balance - prev_block_author_usdt_balance, 0u128);
assert_eq!(post_block_author_plmc_balance - prev_block_author_plmc_balance, expected_plmc_tip);
});
}
2 changes: 0 additions & 2 deletions nodes/parachain/src/chain_spec/common.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use crate::chain_spec::{get_account_id_from_seed, Extensions};
use cumulus_primitives_core::ParaId;
use frame_support::traits::fungible::Inspect;
#[cfg(not(feature = "runtime-benchmarks"))]
use itertools::Itertools;
#[cfg(not(feature = "runtime-benchmarks"))]
use polimec_runtime::MinCandidateStk;
use polimec_runtime::{
pallet_parachain_staking,
pallet_parachain_staking::{
inflation::{perbill_annual_to_perbill_round, BLOCKS_PER_YEAR},
InflationInfo, Range,
Expand Down
1 change: 1 addition & 0 deletions runtimes/polimec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"pallet-asset-tx-payment/runtime-benchmarks"
]

try-runtime = [
Expand Down
15 changes: 5 additions & 10 deletions runtimes/polimec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
use frame_support::{
construct_runtime,
genesis_builder_helper::{build_state, get_preset},
instances::Instance1,
ord_parameter_types, parameter_types,
traits::{
fungible::{Credit, HoldConsideration, Inspect},
Expand Down Expand Up @@ -94,8 +93,7 @@ use polimec_common::{ProvideAssetPrice, USD_UNIT};
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
use sp_runtime::{
traits::{DispatchInfoOf, PostDispatchInfoOf},
transaction_validity::{InvalidTransaction, TransactionValidityError},
transaction_validity::{InvalidTransaction},
};
#[cfg(feature = "runtime-benchmarks")]
mod benchmark_helpers;
Expand Down Expand Up @@ -152,10 +150,7 @@ pub type SignedExtra = (
frame_system::CheckWeight<Runtime>,
// TODO: Use parity's implementation once
// https://github.com/paritytech/polkadot-sdk/pull/3993 is available.
pallet_dispenser::extensions::SkipCheckIfFeeless<
Runtime,
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
>,
pallet_dispenser::extensions::SkipCheckIfFeeless<Runtime, pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>>,
frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
);

Expand Down Expand Up @@ -906,9 +901,9 @@ where
frame_system::CheckWeight::<Runtime>::new(),
// TODO: Use parity's implementation once
// https://github.com/paritytech/polkadot-sdk/pull/3993 is available.
pallet_dispenser::extensions::SkipCheckIfFeeless::from(
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
),
pallet_dispenser::extensions::SkipCheckIfFeeless::from(pallet_asset_tx_payment::ChargeAssetTxPayment::<
Runtime,
>::from(tip, None)),
frame_metadata_hash_extension::CheckMetadataHash::<Runtime>::new(true),
);
let raw_payload = generic::SignedPayload::new(call, extra)
Expand Down
3 changes: 3 additions & 0 deletions runtimes/shared-configuration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ std = [
"sp-arithmetic/std",
"sp-runtime/std",
"sp-std/std",
"pallet-asset-tx-payment/std"
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
Expand All @@ -76,6 +77,7 @@ runtime-benchmarks = [
"parachains-common/runtime-benchmarks",
"polimec-common/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"pallet-asset-tx-payment/runtime-benchmarks"
]
try-runtime = [
"frame-support/try-runtime",
Expand All @@ -89,5 +91,6 @@ try-runtime = [
"pallet-treasury?/try-runtime",
"polimec-common/try-runtime",
"sp-runtime/try-runtime",
"pallet-asset-tx-payment/try-runtime"
]
development-settings = []
2 changes: 1 addition & 1 deletion runtimes/shared-configuration/src/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ where
// Calculate how much refund we should return.
let (final_fee, refund) = paid.split(converted_fee);
// Split the tip from the fee
let (final_fee_minus_tip, final_tip) = final_fee.split(converted_tip);
let (final_tip, final_fee_minus_tip) = final_fee.split(converted_tip);

let _ = <Runtime::Fungibles as fungibles::Balanced<Runtime::AccountId>>::resolve(who, refund);

Expand Down

0 comments on commit 9331229

Please sign in to comment.