Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions pallets/transaction-fee/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ frame-system.workspace = true
log.workspace = true
pallet-balances = { workspace = true, default-features = false }
pallet-alpha-assets = { workspace = true, default-features = false }
pallet-evm.workspace = true
pallet-subtensor.workspace = true
pallet-subtensor-swap.workspace = true
pallet-transaction-payment.workspace = true
smallvec.workspace = true
sp-core.workspace = true
sp-runtime.workspace = true
sp-std.workspace = true
substrate-fixed.workspace = true
Expand Down Expand Up @@ -52,6 +54,7 @@ std = [
"pallet-balances/std",
"pallet-crowdloan/std",
"pallet-drand/std",
"pallet-evm/std",
"pallet-evm-chain-id/std",
"pallet-grandpa/std",
"pallet-preimage/std",
Expand All @@ -60,6 +63,7 @@ std = [
"pallet-subtensor-swap/std",
"pallet-transaction-payment/std",
"scale-info/std",
"sp-core/std",
"sp-runtime/std",
"sp-std/std",
"sp-consensus-aura/std",
Expand All @@ -80,6 +84,7 @@ runtime-benchmarks = [
"pallet-balances/runtime-benchmarks",
"pallet-crowdloan/runtime-benchmarks",
"pallet-drand/runtime-benchmarks",
"pallet-evm/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
"pallet-preimage/runtime-benchmarks",
"pallet-scheduler/runtime-benchmarks",
Expand Down
81 changes: 81 additions & 0 deletions pallets/transaction-fee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use frame_support::{
},
weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial},
};
use pallet_evm::{
AddressMapping, BalanceConverter, Config as EvmConfig, EvmBalance, OnChargeEVMTransaction,
};

// Runtime
use sp_runtime::{
Expand All @@ -29,6 +32,7 @@ use subtensor_swap_interface::SwapHandler;
// Misc
use core::marker::PhantomData;
use smallvec::smallvec;
use sp_core::H160;
use sp_runtime::traits::SaturatedConversion;
use sp_std::vec::Vec;
use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance};
Expand Down Expand Up @@ -229,6 +233,8 @@ pub enum WithdrawnFee<T: frame_system::Config, F: Balanced<AccountIdOf<T>>> {
///
pub struct SubtensorTxFeeHandler<F, OU>(PhantomData<(F, OU)>);

pub struct SubtensorEvmFeeHandler<F, OU>(PhantomData<(F, OU)>);

/// This implementation contains the list of calls that require paying transaction
/// fees in Alpha
impl<F, OU> SubtensorTxFeeHandler<F, OU> {
Expand Down Expand Up @@ -439,3 +445,78 @@ where
F::minimum_balance()
}
}

impl<T, F, OU> OnChargeEVMTransaction<T> for SubtensorEvmFeeHandler<F, OU>
where
T: EvmConfig + pallet_subtensor::Config,
F: Balanced<T::AccountId>,
OU: OnUnbalanced<Credit<T::AccountId, F>>,
T::AddressMapping: AddressMapping<T::AccountId>,
<F as Inspect<T::AccountId>>::Balance: From<TaoBalance> + Into<TaoBalance>,
{
type LiquidityInfo = Option<Credit<T::AccountId, F>>;

fn withdraw_fee(
who: &H160,
fee: EvmBalance,
) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
if fee.into_u256().is_zero() {
return Ok(None);
}

let account_id = <T::AddressMapping as AddressMapping<T::AccountId>>::into_account_id(*who);
let fee_sub = T::BalanceConverter::into_substrate_balance(fee)
.ok_or(pallet_evm::Error::<T>::FeeOverflow)?;

let imbalance = F::withdraw(
&account_id,
TaoBalance::from(fee_sub.into_u64_saturating()).into(),
Precision::Exact,
frame_support::traits::tokens::Preservation::Preserve,
frame_support::traits::tokens::Fortitude::Polite,
)
.map_err(|_| pallet_evm::Error::<T>::BalanceLow)?;

Ok(Some(imbalance))
}

fn correct_and_deposit_fee(
who: &H160,
corrected_fee: EvmBalance,
base_fee: EvmBalance,
already_withdrawn: Self::LiquidityInfo,
) -> Self::LiquidityInfo {
if let Some(paid) = already_withdrawn {
let account_id =
<T::AddressMapping as AddressMapping<T::AccountId>>::into_account_id(*who);
let corrected_fee_sub = T::BalanceConverter::into_substrate_balance(corrected_fee)
.unwrap_or_else(|| 0u64.into());
let refund_amount = paid
.peek()
.saturating_sub(TaoBalance::from(corrected_fee_sub.into_u64_saturating()).into());
let refund_imbalance = F::deposit(&account_id, refund_amount, Precision::BestEffort)
.unwrap_or_else(|_| Debt::<T::AccountId, F>::zero());
let adjusted_paid = paid
.offset(refund_imbalance)
.same()
.unwrap_or_else(|_| Credit::<T::AccountId, F>::zero());
let base_fee_sub = T::BalanceConverter::into_substrate_balance(base_fee)
.unwrap_or_else(|| 0u64.into());
let (base_fee_credit, tip) =
adjusted_paid.split(TaoBalance::from(base_fee_sub.into_u64_saturating()).into());
OU::on_unbalanced(base_fee_credit);
return Some(tip);
}

None
}

fn pay_priority_fee(tip: Self::LiquidityInfo) {
if let Some(tip) = tip {
let author = <T::AddressMapping as AddressMapping<T::AccountId>>::into_account_id(
pallet_evm::Pallet::<T>::find_author(),
);
let _ = F::resolve(&author, tip);
}
}
}
8 changes: 5 additions & 3 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier};
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};
use subtensor_transaction_fee::{SubtensorTxFeeHandler, TransactionFeeHandler};
use subtensor_transaction_fee::{
SubtensorEvmFeeHandler, SubtensorTxFeeHandler, TransactionFeeHandler,
};

use core::marker::PhantomData;

Expand Down Expand Up @@ -272,7 +274,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 406,
spec_version: 407,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down Expand Up @@ -1400,7 +1402,7 @@ impl pallet_evm::Config for Runtime {
type ChainId = ConfigurableChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type OnChargeTransaction = ();
type OnChargeTransaction = SubtensorEvmFeeHandler<Balances, TransactionFeeHandler<Runtime>>;
type OnCreate = ();
type FindAuthor = FindAuthorTruncated<Aura>;
type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
Expand Down
94 changes: 94 additions & 0 deletions runtime/tests/evm_transaction_fee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#![allow(clippy::expect_used, clippy::unwrap_used)]

use codec::Encode;
use frame_support::traits::fungible::Inspect;
use node_subtensor_runtime::{Aura, Balances, BuildStorage, Runtime, RuntimeGenesisConfig};
use pallet_evm::{AddressMapping, EvmBalance, OnChargeEVMTransaction};
use sp_consensus_aura::{AURA_ENGINE_ID, sr25519::AuthorityId as AuraId};
use sp_core::H160;
use sp_core::sr25519;
use subtensor_runtime_common::{AuthorshipInfo, TaoBalance};

fn new_test_ext() -> sp_io::TestExternalities {
sp_tracing::try_init_simple();
let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig {
..Default::default()
}
.build_storage()
.unwrap()
.into();
ext.execute_with(|| frame_system::Pallet::<Runtime>::set_block_number(1));
ext
}

fn add_balance_to_coldkey_account(coldkey: &sp_core::crypto::AccountId32, tao: TaoBalance) {
let credit = pallet_subtensor::Pallet::<Runtime>::mint_tao(tao);
let _ = pallet_subtensor::Pallet::<Runtime>::spend_tao(coldkey, credit, tao).unwrap();
}

fn initialize_block_with_aura_authority(authority: AuraId, slot: u64) {
Aura::change_authorities(vec![authority].try_into().unwrap());
let digest = sp_runtime::Digest {
logs: vec![sp_runtime::DigestItem::PreRuntime(
AURA_ENGINE_ID,
slot.encode(),
)],
};
frame_system::Pallet::<Runtime>::initialize(&1u32.into(), &Default::default(), &digest);
}

#[test]
fn evm_fee_refund_does_not_change_total_issuance() {
new_test_ext().execute_with(|| {
initialize_block_with_aura_authority(AuraId::from(sr25519::Public::from_raw([1u8; 32])), 0);

let evm_addr = H160::from_low_u64_be(7);
let account_id = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(evm_addr);
let substrate_author = <Runtime as AuthorshipInfo<sp_runtime::AccountId32>>::author()
.expect("aura digest should provide a substrate block author");
let evm_author =
<Runtime as pallet_evm::Config>::AddressMapping::into_account_id(pallet_evm::Pallet::<
Runtime,
>::find_author(
));

add_balance_to_coldkey_account(&account_id, 1_000_000_000u64.into());
add_balance_to_coldkey_account(&substrate_author, 1_000_000_000u64.into());
add_balance_to_coldkey_account(&evm_author, 1_000_000_000u64.into());

let balances_issuance_before = Balances::total_issuance();
let subtensor_issuance_before = pallet_subtensor::Pallet::<Runtime>::get_total_issuance();
let balance_before = Balances::total_balance(&account_id);

assert_eq!(balances_issuance_before, subtensor_issuance_before);

let withdrawn =
<<Runtime as pallet_evm::Config>::OnChargeTransaction as OnChargeEVMTransaction<
Runtime,
>>::withdraw_fee(&evm_addr, EvmBalance::from(10_000_000_000u128))
.unwrap();

let tip =
<<Runtime as pallet_evm::Config>::OnChargeTransaction as OnChargeEVMTransaction<
Runtime,
>>::correct_and_deposit_fee(
&evm_addr,
EvmBalance::from(5_000_000_000u128),
EvmBalance::from(3_000_000_000u128),
withdrawn,
);

<<Runtime as pallet_evm::Config>::OnChargeTransaction as OnChargeEVMTransaction<
Runtime,
>>::pay_priority_fee(tip);

assert_eq!(
Balances::total_issuance(),
pallet_subtensor::Pallet::<Runtime>::get_total_issuance()
);
assert_eq!(
Balances::total_balance(&account_id),
balance_before - TaoBalance::from(5)
);
});
}
Loading