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

如何生成有余额地址,测试合约函数调用后的lastPoster字段更新?

问题:如何在Solidity测试中生成有余额的地址验证lastPoster字段

合约代码

// SPDX-License-Identifier: ISC
pragma solidity ^0.8.17;

contract NoteDrop {
    string public text = "Hello, world!";
    address public lastPoster;

    event NewText(
        string text,
        address lastPoster
    );

    constructor() {
        // Do nothing
    }

    function setText(string memory _text) public {
        text = _text;
        lastPoster = msg.sender;
        emit NewText(_text, msg.sender);
    }
}

测试代码(原代码)

contract('NoteDrop', () => {
  let noteDropContract;

  beforeEach(async() => {
    noteDropContract = await NoteDropContract.new();
  })

  describe('lastPoster', async() => {
    it('is changed automatically', async() => {
      await noteDropContract.setText("something", {from: "XXXXX"});
      const lastPoster = await noteDropContract.lastPoster();
      assert.equal(lastPoster, "XXXXX");
    })
  })
})

问题说明

我希望测试合约的lastPoster字段会在调用setText函数后更新为最后调用该函数的地址。但我不知道如何生成(或获取)一个有余额的地址,以便在调用setText时通过from参数指定该地址,进而断言noteDrop.lastPoster()等于该发送地址。请问我遗漏了什么?该如何生成这样的地址?


解决方案

1. 直接用测试框架自带的预充值账户

不管是Truffle还是Hardhat这类常用的Solidity测试框架,启动时都会自动生成一组已经预加载ETH的测试账户,这些账户默认就能用来发交易付gas费,直接拿过来用就行。

修改测试代码如下:

contract('NoteDrop', () => {
  let noteDropContract;
  let accounts; // 新增存储测试账户的变量

  beforeEach(async() => {
    noteDropContract = await NoteDropContract.new();
    accounts = await web3.eth.getAccounts(); // 获取框架自带的测试账户列表
  })

  describe('lastPoster', async() => {
    it('is changed automatically', async() => {
      // 用第二个测试账户调用setText(第一个账户是合约部署者,也可以用)
      await noteDropContract.setText("something", {from: accounts[1]});
      const lastPoster = await noteDropContract.lastPoster();
      assert.equal(lastPoster, accounts[1]); // 断言对比该账户地址
    })
  })
})

2. 自定义生成新地址并充值ETH

如果需要用全新的地址测试,你可以手动生成地址,再从已有测试账户转ETH过去,解锁后就能用了:

contract('NoteDrop', () => {
  let noteDropContract;
  let accounts;

  beforeEach(async() => {
    noteDropContract = await NoteDropContract.new();
    accounts = await web3.eth.getAccounts();
  })

  describe('lastPoster', async() => {
    it('updates with custom funded account', async() => {
      // 生成新的账户
      const customAccount = web3.eth.accounts.create();
      // 从第一个测试账户转1ETH到新地址(够付gas就行)
      await web3.eth.sendTransaction({
        from: accounts[0],
        to: customAccount.address,
        value: web3.utils.toWei('1', 'ether')
      });
      // 解锁新账户(测试环境需要解锁才能发起交易)
      await web3.eth.personal.unlockAccount(customAccount.address, customAccount.privateKey, 600);
      
      // 用新地址调用setText
      await noteDropContract.setText("custom text", {from: customAccount.address});
      const lastPoster = await noteDropContract.lastPoster();
      assert.equal(lastPoster, customAccount.address);
    })
  })
})

关键提醒

你的合约setText函数是普通public函数,不需要支付ETH,但以太坊上任何交易都要付gas费,所以用来发交易的地址必须有足够ETH支付gas——这也是你不能直接填个假地址"XXXXX"的原因。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 23:45:34