Substrate中能否从函数入参Call提取pallet及函数数据实现自定义手续费
问题解答
你完全可以从Call类型参数中获取所属pallet、调用函数名及所有入参,基于调用参数自定义手续费的需求可以实现。
核心原理
Substrate的Runtime Call本质是自动生成的聚合枚举类型:
- 每个一级枚举变体对应Runtime中集成的一个pallet
- 每个pallet对应的二级枚举变体对应该pallet下的所有可调用函数,变体字段就是该函数的入参
具体实现方式
1. 针对特定调用硬匹配(适配staking::bond场景)
直接在pre_dispatch或者你自定义的手续费计算逻辑中对call做模式匹配,直接取出对应参数计算手续费,示例代码如下:
use pallet_staking::Call as StakingCall; fn withdraw_fee( &self, who: &Self::AccountId, call: &Self::Call, info: &DispatchInfoOf<Self::Call>, len: usize ) -> Result<(BalanceOf<Self>, Imbalance<Self>), TransactionValidityError> { // 先匹配目标pallet和目标调用 let custom_fee = match call { // 匹配staking pallet的bond调用,直接取出绑定金额value RuntimeCall::Staking(StakingCall::bond { value, .. }) => { // 自定义逻辑:例如按绑定金额的千分之一收取手续费 value.saturating_mul(1u32.into()).saturating_div(1000u32.into()) }, // 其他调用走默认手续费计算逻辑 _ => { // 原有默认手续费计算逻辑,比如基于权重和交易长度计算 Self::default_fee_calculation(info, len) } }; // 后续手续费扣除逻辑沿用原有实现即可 // ... }
2. 通用获取pallet名、函数名的方法
如果需要批量处理多个调用、不想逐个硬匹配,可直接调用Call类型实现的GetCallName trait提供的方法:
call.get_pallet_name():返回调用所属pallet的字符串名称,例如"Staking"call.get_call_name():返回被调用函数的字符串名称,例如"bond"
注意事项
- 自定义手续费逻辑需要做好数值溢出防护,建议统一使用
saturating_*系列安全计算方法 - 建议设置手续费兜底规则:比如自定义计算的手续费低于最小基础手续费时,按最小基础手续费收取,避免超低额交易手续费为0的问题
- 若不需要完全重写交易手续费逻辑,仅扩展原有
transaction-paymentpallet的手续费计算规则即可,不需要替换整个pallet实现
内容的提问来源于stack exchange,提问作者Makam Aravind
相关产品推荐
相关产品推荐

