如何生成有余额地址,测试合约函数调用后的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
相关产品推荐
相关产品推荐

