You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Substrate如何在transaction-payment pallet中获取用户调用参数自定义手续费

基于用户调用参数自定义Substrate交易手续费的实现方法

完全可以实现你要的需求,不需要依赖默认的交易长度、权重规则计算手续费。

实现原理

Substrate的transaction-payment pallet的手续费计算逻辑是完全可插拔的,没有硬编码在pallet内部。而且交易在进入函数执行阶段之前,节点已经可以拿到完整的交易Call结构体、签名信息等所有用户传入的参数,你可以通过自定义扩展的方式获取这些参数,覆盖默认的手续费计算逻辑。

具体实现方案

最常用的方式是自定义SignedExtension扩展:

  • SignedExtension是Substrate内置的交易预检查机制,运行时机早于交易函数执行,天生可以访问交易的全部入参。
  • 你只需要在自定义SignedExtension的validate方法中匹配当前交易的调用类型,提取你需要的手续费相关参数,直接用这个参数作为最终手续费即可。

示例代码参考:

// 导入必要依赖
use frame_support::dispatch::{DispatchInfo, Dispatchable};
use sp_runtime::{
    traits::{SignedExtension, TransactionValidity, TransactionValidityError},
    transaction_validity::ValidTransaction,
    Encode, Decode, TypeInfo,
};
use frame_system::Config;

// 自定义手续费计算扩展
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CustomFeeExtension<T>(sp_std::marker::PhantomData<T>);

impl<T: Config + pallet_transaction_payment::Config> SignedExtension for CustomFeeExtension<T>
where
    T::Call: Dispatchable<Info = DispatchInfo>,
{
    const IDENTIFIER: &'static str = "CustomFeeExtension";
    type AccountId = T::AccountId;
    type Call = T::Call;
    type AdditionalSigned = ();
    type Pre = ();

    fn additional_signed(&self) -> Result<(), TransactionValidityError> {
        Ok(())
    }

    fn validate(
        &self,
        _who: &Self::AccountId,
        call: &Self::Call,
        _info: &DispatchInfoOf<Self::Call>,
        _len: usize,
    ) -> TransactionValidity {
        // 匹配你的业务Pallet的对应调用,提取用户传入的手续费参数
        if let Call::YourPallet(pallet_call) = call {
            match pallet_call {
                // 这里以transfer调用为例,假设它有一个custom_fee参数是用户指定的手续费
                YourPalletCall::transfer { custom_fee, .. } |
                YourPalletCall::other_need_custom_fee_call { custom_fee, .. } => {
                    // 直接将用户传入的custom_fee设置为本次交易的手续费
                    pallet_transaction_payment::NextFee::<T>::put(custom_fee);
                }
            }
        }
        Ok(ValidTransaction::default())
    }
}

完成扩展开发后只需要做两个配置即可生效:

  • 把自定义的CustomFeeExtension添加到Runtime的SignedExtra列表中,确保它会在交易验证阶段运行。
  • 替换pallet_transaction_payment的FeeCalculator关联类型,自定义计算逻辑直接读取你刚才写入的NextFee值,完全舍弃默认的长度、权重计算规则即可。

注意点

  • 你用于计算手续费的参数会自动被纳入交易签名的覆盖范围,不用担心用户篡改参数少交手续费。
  • 如果只有部分调用需要自定义手续费,只需要在validate方法中匹配对应的调用类型即可,其余调用可以继续走默认的手续费计算逻辑。

内容的提问来源于stack exchange,提问作者Makam Aravind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 05:36:08