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

使用etherjs调用Solidity payable函数报需signer错误如何解决

问题原因

报错的核心原因有两个:

  1. 你初始化合约实例时传入的是provider对象,该对象仅具备链上读权限,没有交易签名能力。你在调用accumulated方法时把wallet作为函数参数传入的写法是错误的——ethers.js不会把传入合约方法的signer识别为交易签名者,只会把它当做合约函数的入参处理,而绑定了provider的合约实例发起写交易时,会直接抛出缺少signer的错误。
  2. 你的accumulated合约函数本身没有定义入参,你传入的第一个参数'1'属于无效入参。如果要向payable函数转ETH,需要将转账金额放在overrides配置对象中作为最后一个参数传入,不能直接作为函数入参传递。

另外你的代码里还存在两个隐性问题:

  • 读取_blockTimestamp时使用的合约实例名是NFTLTokenContract,和你前面初始化的TestContract实例名不一致,运行时会触发变量未定义错误
  • 你配置的节点是Rinkeby测试网节点,私钥却是Ropsten测试网的私钥,两个测试网链ID不匹配,就算签名逻辑正确也会广播交易失败。
修正方法

有两种标准写法可以解决问题:

写法1:初始化合约时直接绑定签名者

这是最常用的实现方式,初始化合约时直接传入wallet实例(wallet本身就是集成了provider的signer对象),后续读、写操作都可以用同一个合约实例完成:

const provider = new providers.JsonRpcProvider(getEnv('RINKEBY_NODE_URL'));
const wallet = new Wallet(getEnv('ROPSTEN_PRIVATE_KEY'), provider);

// 第二个参数传入wallet(signer),而非仅传provider
const TestContract = TestContract__factory.connect(getEnv('TEST_CONTRACT'), wallet);

// 读操作无需签名,可正常调用
const _blockTimestamp = await TestContract._blockTimestamp();

// 调用payable函数,不需要转ETH时直接调用即可
const tx = await TestContract.accumulated();
// 如果需要转1wei,就传入overrides配置:const tx = await TestContract.accumulated({value: 1});

// 等待交易确认上链
await tx.wait();

写法2:临时为合约实例绑定签名者

如果你已经有绑定了provider的合约实例用于高频读操作,不想重新初始化合约,可以在发起写交易前,用connect方法临时生成绑定签名者的合约实例再调用:

const provider = new providers.JsonRpcProvider(getEnv('RINKEBY_NODE_URL'));
const wallet = new Wallet(getEnv('ROPSTEN_PRIVATE_KEY'), provider);

// 绑定provider的合约实例仅用于读操作
const TestContract = TestContract__factory.connect(getEnv('TEST_CONTRACT'), provider);
const _blockTimestamp = await TestContract._blockTimestamp();

// 发起交易前临时绑定signer
const tx = await TestContract.connect(wallet).accumulated();
// 需要转1wei时写法:const tx = await TestContract.connect(wallet).accumulated({value: 1});
await tx.wait();
调用规则说明
  • 所有合约读操作(纯函数、视图函数、public变量读取):用绑定provider的合约实例即可调用,不需要签名
  • 所有合约写操作(修改链上状态的函数,不管是不是payable):必须用绑定了signer的合约实例调用,否则会抛出缺少signer的错误
  • payable函数如果需要转入ETH,转账金额必须放在函数调用的最后一个参数,格式为{value: 转账金额(单位wei)}的overrides对象,不能直接作为函数入参传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:09:26