Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
111 changes: 111 additions & 0 deletions runtime/src/fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use frame_support::dispatch::DispatchInfo;
use frame_support::pallet_prelude::*;
use frame_support::traits::{Currency, Imbalance};
use pallet_transaction_payment::{OnChargeTransaction, RuntimeDispatchInfo};
use sp_runtime::traits::DispatchInfoOf;
use sp_runtime::SaturatedConversion;

use crate::{Balance, RuntimeCall, Vec, DOLLARS};

pub(crate) fn special_fee_for_all_calls(calls: Vec<RuntimeCall>) -> Option<Balance> {
calls
.iter()
.filter_map(|call: &RuntimeCall| special_fee_for_call(call))
.reduce(|a, b| a + b)
}

pub(crate) fn special_fee_for_call(call: &RuntimeCall) -> Option<Balance> {
// 20 SXT fee per table
let fee_per_table = DOLLARS * 20;

match call.clone() {
RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => {
special_fee_for_all_calls(calls)
}
RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => {
special_fee_for_all_calls(calls)
}
RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => {
special_fee_for_all_calls(calls)
}
RuntimeCall::Tables(pallet_tables::Call::create_tables { tables }) => {
Some(fee_per_table.saturating_mul(tables.len().saturated_into()))
}
RuntimeCall::Tables(pallet_tables::Call::create_tables_with_snapshot_and_commitment {
tables,
..
}) => Some(fee_per_table.saturating_mul(tables.len().saturated_into())),
RuntimeCall::Tables(pallet_tables::Call::create_namespace { .. }) => Some(fee_per_table),
_ => None,
}
}

/// A wrapper for custom gas fees that takes an Inner fallback
pub struct CustomGasFees<C, Inner>(sp_std::marker::PhantomData<(C, Inner)>);

impl<T, C, Fallback> OnChargeTransaction<T> for CustomGasFees<C, Fallback>
where
T: frame_system::Config<RuntimeCall = RuntimeCall>,
T: pallet_transaction_payment::Config,
C: Currency<T::AccountId, Balance = Balance>,
Fallback:
OnChargeTransaction<T, LiquidityInfo = Option<C::NegativeImbalance>, Balance = Balance>,
{
type Balance = Balance;
type LiquidityInfo = Option<C::NegativeImbalance>;

fn withdraw_fee(
who: &T::AccountId,
call: &T::RuntimeCall,
info: &DispatchInfoOf<T::RuntimeCall>,
fee: Self::Balance,
tip: Self::Balance,
) -> Result<Self::LiquidityInfo, TransactionValidityError> {
// Calculate any special fees from table or namespace creation
if let Some(special_fee) = special_fee_for_call(call) {
// Attempt to perform the withdrawal, throwing an error if the account doesn't have the
// available funds
Ok(Some(
<C as Currency<_>>::withdraw(
who,
special_fee,
frame_support::traits::WithdrawReasons::FEE,
frame_support::traits::ExistenceRequirement::KeepAlive,
)
.map_err(|_| {
TransactionValidityError::Invalid(
sp_runtime::transaction_validity::InvalidTransaction::Payment,
)
})?,
))
} else {
// Calculate and withdraw the base fee for the transaction
Fallback::withdraw_fee(who, call, info, fee, tip)
}
}

fn correct_and_deposit_fee(
who: &<T>::AccountId,
dispatch_info: &sp_runtime::traits::DispatchInfoOf<<T>::RuntimeCall>,
post_info: &sp_runtime::traits::PostDispatchInfoOf<<T>::RuntimeCall>,
corrected_fee: Self::Balance,
tip: Self::Balance,
already_withdrawn: Self::LiquidityInfo,
) -> Result<(), TransactionValidityError> {
// Force the user to always pay the estimate so that custom fees are respected
let final_fee = if let Some(ref paid) = already_withdrawn {
paid.peek()
} else {
corrected_fee
};

Fallback::correct_and_deposit_fee(
who,
dispatch_info,
post_info,
final_fee,
tip,
already_withdrawn,
)
}
}
63 changes: 56 additions & 7 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

mod tests;

mod fees;

use fees::*;

#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

Expand All @@ -13,8 +17,9 @@ use alloc::vec;
use alloc::vec::Vec;

use frame_election_provider_support::{generate_solution_type, onchain, SequentialPhragmen};
use frame_support::dispatch::DispatchClass;
use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo};
use frame_support::genesis_builder_helper::{build_state, get_preset};
use frame_support::pallet_prelude::TransactionValidityError;
use frame_support::traits::VariantCountOf;
pub use frame_support::traits::{
ConstBool,
Expand All @@ -39,11 +44,12 @@ pub use frame_support::{construct_runtime, derive_impl, parameter_types, Storage
pub use frame_system::Call as SystemCall;
use frame_system::EnsureRoot;
pub use pallet_balances::Call as BalancesCall;
use pallet_balances::NegativeImbalance;
use pallet_election_provider_multi_phase::GeometricDepositBase;
use pallet_grandpa::AuthorityId as GrandpaId;
pub use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
pub use pallet_timestamp::Call as TimestampCall;
use pallet_transaction_payment::{CurrencyAdapter, Multiplier};
use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, Multiplier, RuntimeDispatchInfo};
use proof_of_sql_commitment_map::generic_over_commitment::ConcreteType;
use proof_of_sql_commitment_map::{AnyCommitmentScheme, PerCommitmentScheme, TableCommitmentBytes};
use sp_api::impl_runtime_apis;
Expand Down Expand Up @@ -381,7 +387,7 @@ parameter_types! {

impl pallet_transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type OnChargeTransaction = CustomGasFees<Balances, CurrencyAdapter<Balances, ()>>;
type WeightToFee = ConstantMultiplier<Balance, WeightFeePerRefTime>;
type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
type FeeMultiplierUpdate = ();
Expand Down Expand Up @@ -980,6 +986,7 @@ pub type Executive = frame_executive::Executive<
Migrations,
>;

use pallet_transaction_payment::InclusionFee;
#[cfg(feature = "runtime-benchmarks")]
mod benches {
frame_benchmarking::define_benchmarks!(
Expand Down Expand Up @@ -1201,13 +1208,31 @@ impl_runtime_apis! {
uxt: <Block as BlockT>::Extrinsic,
len: u32,
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
TransactionPayment::query_info(uxt, len)
if let Some(special_fee) = special_fee_for_call(&uxt.function) {
let mut out = TransactionPayment::query_info(uxt, len);
out.partial_fee = special_fee;
out
} else {
TransactionPayment::query_info(uxt, len)
}
}
fn query_fee_details(
uxt: <Block as BlockT>::Extrinsic,
len: u32,
) -> pallet_transaction_payment::FeeDetails<Balance> {
TransactionPayment::query_fee_details(uxt, len)
if let Some(special_fee) = special_fee_for_call(&uxt.function) {
FeeDetails {
inclusion_fee: Some(
InclusionFee {
base_fee: special_fee,
len_fee: 0,
adjusted_weight_fee: 0,
}),
tip: 0,
}
} else {
TransactionPayment::query_fee_details(uxt, len)
}
}
fn query_weight_to_fee(weight: Weight) -> Balance {
TransactionPayment::weight_to_fee(weight)
Expand All @@ -1224,13 +1249,37 @@ impl_runtime_apis! {
call: RuntimeCall,
len: u32,
) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
TransactionPayment::query_call_info(call, len)
if let Some(special_fee) = special_fee_for_call(&call) {
let dispatch_info = <RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
let DispatchInfo { weight, class, .. } = dispatch_info;

RuntimeDispatchInfo {
weight,
class,
partial_fee: special_fee,
}

} else {
TransactionPayment::query_call_info(call, len)
}
}
fn query_call_fee_details(
call: RuntimeCall,
len: u32,
) -> pallet_transaction_payment::FeeDetails<Balance> {
TransactionPayment::query_call_fee_details(call, len)
if let Some(special_fee) = special_fee_for_call(&call) {
FeeDetails {
inclusion_fee: Some(
InclusionFee {
base_fee: special_fee,
len_fee: 0,
adjusted_weight_fee: 0,
}),
tip: 0,
}
} else {
TransactionPayment::query_call_fee_details(call, len)
}
}
fn query_weight_to_fee(weight: Weight) -> Balance {
TransactionPayment::weight_to_fee(weight)
Expand Down