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

如何在执行Solidity合约函数时收取交易费并合并两次签名为单笔交易

解决方案

你只需要调整合约逻辑,将费用收取逻辑内嵌到add方法中,即可实现用户单笔签名完成费用支付+函数调用的需求,修改方案如下:

1. 修改Solidity合约代码

首先在合约中内置收款账户和费用扣除逻辑,把add方法改为可接收ETH的payable类型,调用时自动完成费用划转:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Calculate {
  uint private c = 123;
  // 收款账户地址,部署时传入即可
  address public immutable fundAcct;
  // 1% 费率
  uint256 public constant FEE_PERCENT = 1;

  constructor(address _fundAcct) {
    fundAcct = _fundAcct;
    getC();
  }

  function getC() public view returns (uint) {
    return c;
  }

  // 新增payable修饰符,支持调用时附带ETH
  function add(uint _a, uint _b) public payable {
    // 计算应付手续费
    uint256 fee = msg.value * FEE_PERCENT / 100;
    // 手续费转账到指定账户,用call方式兼容性更好
    (bool success, ) = fundAcct.call{value: fee}("");
    require(success, "Fee transfer failed");
    
    // 可选:如果用户多转了ETH,剩余部分退回给调用者
    if(msg.value > fee) {
      (bool refundSuccess, ) = msg.sender.call{value: msg.value - fee}("");
      require(refundSuccess, "Refund failed");
    }

    c = _a + _b;
  }
}

如果你不需要按比例收费,而是固定金额手续费,也可以直接校验msg.value == 固定手续费金额即可,不需要动态计算费率。

2. 修改React端Ethers.js代码

移除单独的sendETH调用逻辑,直接在调用合约add方法时通过overrides参数附带要支付的ETH即可:

async function add() {
  await requestAccount();
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();
  const contract = new ethers.Contract(
    calculateAddress,
    Calculate.abi,
    signer
  );
  // 调用add方法时通过第三个参数传入overrides,指定本次交易附带的ETH金额
  const tx = await contract.add(a, b, {
    value: ethers.utils.parseEther(fee.toString())
  });
  await tx.wait();
  getC();
}

额外说明

  • 这种方案全程只需要用户签署一次交易,既避免了两次签名的繁琐,也杜绝了用户付了费但没调用合约、或者调用了合约没付费的不一致问题
  • 合约内置的转账逻辑会在add执行前完成,手续费扣不成功的话整个交易会直接回滚,不会出现异常情况

内容的提问来源于stack exchange,提问作者Trevor Behnke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:36:06