Rust区块链交易支付模块pallet-transaction-payment的使用,Substrate链上交易费用处理与支付功能

Transaction Payment Pallet

这个pallet提供了支付交易被包含所需绝对最低金额的基本逻辑。这包括:

  • 权重费用:与交易消耗的权重量成比例的费用。
  • 长度费用:与交易编码长度成比例的费用。
  • 小费:可选的小费。小费提高交易的优先级,使其有更高机会被交易队列包含。

此外,这个pallet允许配置:

  • 通过[Config::WeightToFee]配置一个单位权重到一个单位费用的映射。
  • 通过基于上一个区块结束时链的最终状态定义乘数,来更新下一个区块的费用。这可以通过[Config::FeeMultiplierUpdate]配置。

许可证:Apache-2.0

安装: 运行以下Cargo命令在您的项目目录中:

cargo add pallet-transaction-payment

或在您的Cargo.toml中添加以下行:

pallet-transaction-payment = "42.0.0"

完整示例demo:

// 引入必要的依赖和模块
use frame_support::{pallet_prelude::*, traits::Currency};
use frame_system::pallet_prelude::*;
use sp_runtime::{
    traits::{Convert, Saturating, Zero},
    FixedPointNumber, FixedU128,
};

// 定义pallet
pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use super::*;

    // Pallet声明
    #[pallet::pallet]
    pub struct Pallet<T>(_);

    // 配置trait
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// 运行时事件类型
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
        
        /// 货币类型
        type Currency: Currency<Self::AccountId>;
        
        /// 权重到费用的转换器
        type WeightToFee: Convert<Weight, BalanceOf<Self>>;
        
        /// 费用乘数更新机制
        type FeeMultiplierUpdate: Get<FixedU128>;
    }

    // 存储项
    #[pallet::storage]
    #[pallet::getter(fn next_fee_multiplier)]
    pub type NextFeeMultiplier<T> = StorageValue<_, FixedU128, ValueQuery>;

    // 事件
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// 交易费用已支付
        TransactionFeePaid {
            who: T::AccountId,
            actual_fee: BalanceOf<T>,
            tip: BalanceOf<T>,
        },
    }

    // 错误
    #[pallet::error]
    pub enum Error<T> {
        /// 费用计算错误
        FeeCalculationFailed,
    }

    // Pallet实现
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// 处理交易支付
        #[pallet::call_index(0)]
        #[pallet::weight(0)]
        pub fn pay_transaction_fee(
            origin: OriginFor<T>,
            weight: Weight,
            tip: BalanceOf<T>,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;

            // 计算权重费用
            let weight_fee = T::WeightToFee::convert(weight);
            
            // 计算总费用(权重费用 + 小费)
            let total_fee = weight_fee.saturating_add(tip);
            
            // 从账户扣除费用
            T::Currency::withdraw(&who, total_fee, WithdrawReasons::FEE, ExistenceRequirement::KeepAlive)?;
            
            // 发出事件
            Self::deposit_event(Event::TransactionFeePaid {
                who,
                actual_fee: weight_fee,
                tip,
            });

            Ok(())
        }
    }

    // 实现交易支付API
    impl<T: Config> Pallet<T> {
        /// 计算交易费用
        pub fn compute_fee(
            weight: Weight,
            tip: BalanceOf<T>,
        ) -> Result<BalanceOf<T>, Error<T>> {
            let weight_fee = T::WeightToFee::convert(weight);
            Ok(weight_fee.saturating_add(tip))
        }

        /// 更新费用乘数
        pub fn update_fee_multiplier() {
            let new_multiplier = T::FeeMultiplierUpdate::get();
            NextFeeMultiplier::<T>::put(new_multiplier);
        }
    }
}

// 运行时配置示例
pub struct Runtime;
impl frame_system::Config for Runtime {
    type AccountId = u64;
    type RuntimeEvent = ();
    // 其他必要的关联类型...
}

impl Config for Runtime {
    type RuntimeEvent = ();
    type Currency = (); // 需要实现具体的Currency类型
    type WeightToFee = (); // 需要实现具体的Convert trait
    type FeeMultiplierUpdate = (); // 需要实现具体的Get trait
}

1 回复

Rust区块链交易支付模块:pallet-transaction-payment

模块概述

pallet-transaction-payment是Substrate框架中的核心模块,用于处理区块链网络中的交易费用计算和支付。该模块实现了交易费用的自动计算、手续费支付以及小费机制,确保网络资源得到合理补偿。

核心功能

  • 交易费用计算:基于交易长度和权重自动计算费用
  • 手续费支付:从用户账户扣除交易费用
  • 小费机制:允许用户支付额外费用优先处理交易
  • 费用调整:支持动态费用调整机制

使用方法

1. 引入依赖

在Cargo.toml中添加依赖:

[dependencies]
pallet-transaction-payment = { version = "4.0.0", default-features = false }

2. 运行时配置

在runtime/src/lib.rs中配置模块:

impl pallet_transaction_payment::Config for Runtime {
    type Event = Event;
    type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
    type WeightToFee = IdentityFee<Balance>;
    type LengthToFee = IdentityFee<Balance>;
    type FeeMultiplierUpdate = ();
}

3. 交易费用计算示例

use frame_support::weights::{Weight, DispatchClass};
use pallet_transaction_payment::Pallet as TransactionPayment;

// 计算交易费用
fn calculate_transaction_fee() {
    let call = Call::System(frame_system::Call::remark { remark: vec![] });
    let info = call.get_dispatch_info();
    
    let len = 10; // 交易长度
    let fee = TransactionPayment::<Runtime>::compute_fee(len, &info, 0);
    println!("Transaction fee: {}", fee);
}

4. 自定义费用策略

// 自定义权重到费用转换
struct CustomWeightToFee;
impl Convert<Weight, Balance> for CustomWeightToFee {
    fn convert(weight: Weight) -> Balance {
        // 1 weight = 100 balance
        weight.saturating_mul(100).into()
    }
}

// 配置自定义费用策略
impl pallet_transaction_payment::Config for Runtime {
    type WeightToFee = CustomWeightToFee;
    // ... 其他配置
}

5. 支付小费示例

use sp_runtime::Perbill;

// 支付10%小费
let tip = Perbill::from_percent(10) * calculated_fee;
let call = Call::Balances(pallet_balances::Call::transfer {
    dest: recipient,
    value: amount,
});

let signed_extension = (
    frame_system::CheckNonZeroSender::<Runtime>::new(),
    frame_system::CheckSpecVersion::<Runtime>::new(),
    // ... 其他signed extensions
    pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
);

重要参数配置

parameter_types! {
    pub const TransactionByteFee: Balance = 1 * MICROCENTS;
    pub const OperationalFeeMultiplier: u8 = 5;
}

impl pallet_transaction_payment::Config for Runtime {
    type TransactionByteFee = TransactionByteFee;
    type OperationalFeeMultiplier = OperationalFeeMultiplier;
}

完整示例demo

//! 完整交易支付模块示例
use frame_support::{
    parameter_types,
    traits::{Currency, Imbalance, OnUnbalanced},
    weights::{Weight, DispatchClass, IdentityFee},
};
use frame_system as system;
use sp_runtime::{
    traits::{Convert, Zero, SaturatedConversion},
    Perbill,
};
use pallet_balances as balances;
use pallet_transaction_payment;

// 定义运行时类型
pub type BlockNumber = u32;
pub type AccountId = u64;
pub type Balance = u128;

// 参数类型定义
parameter_types! {
    pub const BlockHashCount: BlockNumber = 2400;
    pub const MaximumBlockWeight: Weight = 1024;
    pub const MaximumBlockLength: u32 = 2 * 1024;
    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
    pub const TransactionByteFee: Balance = 1;
    pub const OperationalFeeMultiplier: u8 = 5;
}

// 系统配置
impl system::Config for Runtime {
    type BaseCallFilter = ();
    type Origin = Origin;
    type Call = Call;
    type Index = u64;
    type BlockNumber = BlockNumber;
    type Hash = sp_core::H256;
    type Hashing = sp_runtime::traits::BlakeTwo256;
    type AccountId = AccountId;
    type Lookup = sp_runtime::traits::IdentityLookup<AccountId>;
    type Header = sp_runtime::testing::Header;
    type Event = Event;
    type BlockHashCount = BlockHashCount;
    type MaximumBlockWeight = MaximumBlockWeight;
    type DbWeight = ();
    type BlockExecutionWeight = ();
    type ExtrinsicBaseWeight = ();
    type MaximumExtrinsicWeight = MaximumBlockWeight;
    type MaximumBlockLength = MaximumBlockLength;
    type AvailableBlockRatio = AvailableBlockRatio;
    type Version = ();
    type PalletInfo = PalletInfo;
    type AccountData = balances::AccountData<Balance>;
    type OnNewAccount = ();
    type OnKilledAccount = ();
    type SystemWeightInfo = ();
}

// 余额配置
impl balances::Config for Runtime {
    type Balance = Balance;
    type DustRemoval = ();
    type Event = Event;
    type ExistentialDeposit = ExistentialDeposit;
    type AccountStore = system::Pallet<Runtime>;
    type WeightInfo = ();
    type MaxLocks = ();
}

parameter_types! {
    pub const ExistentialDeposit: Balance = 1;
}

// 自定义权重到费用转换器
pub struct CustomWeightToFee;
impl Convert<Weight, Balance> for CustomWeightToFee {
    fn convert(weight: Weight) -> Balance {
        // 自定义转换逻辑:1 weight = 100 balance
        weight.saturating_mul(100).saturated_into()
    }
}

// 交易支付配置
impl pallet_transaction_payment::Config for Runtime {
    type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>;
    type TransactionByteFee = TransactionByteFee;
    type WeightToFee = CustomWeightToFee;
    type FeeMultiplierUpdate = ();
    type OperationalFeeMultiplier = OperationalFeeMultiplier;
}

// 运行时结构体
#[frame_support::runtime]
mod runtime {
    pub struct Runtime;
}

// 使用示例
fn main() {
    // 初始化运行时
    let runtime = Runtime::new();
    
    // 示例1:计算交易费用
    let call = Call::System(system::Call::remark { remark: vec![1, 2, 3] });
    let dispatch_info = call.get_dispatch_info();
    
    let transaction_length = 15; // 交易字节长度
    let fee = pallet_transaction_payment::Pallet::<Runtime>::compute_fee(
        transaction_length,
        &dispatch_info,
        0 // 无小费
    );
    
    println!("计算出的交易费用: {}", fee);
    
    // 示例2:支付带小费的交易
    let tip_amount = Perbill::from_percent(15) * fee; // 15%小费
    println!("小费金额: {}", tip_amount);
    
    // 示例3:验证自定义费用策略
    let test_weight = Weight::from_ref_time(100);
    let custom_fee = CustomWeightToFee::convert(test_weight);
    println!("自定义权重转换结果: {} weight = {} balance", test_weight, custom_fee);
}

注意事项

  1. 确保余额模块正确配置
  2. 费用计算需要考虑网络拥堵情况
  3. 小费机制可提高交易优先级
  4. 测试时注意费用计算的一致性

该模块是Substrate链的必要组件,正确处理交易费用对网络的安全性和可持续性至关重要。

回到顶部