如何不编写getter函数直接读取Solidity合约的状态属性?
解答
无getter读取合约状态属性的实现方案
你示例中的number状态变量未添加public修饰符,默认是internal可见性,Solidity不会为这类变量自动生成外部可调用的getter函数,因此你原来的测试代码中直接调用await this._chars.number会失败。
无需手动编写getter的实现方式是直接读取合约对应存储槽的内容:Solidity的状态变量按声明顺序依次存储在从0开始编号的存储槽中,你示例中的number是第一个状态变量,对应0号存储槽,可通过ethers提供的getStorageAt接口读取,测试代码修改如下:
it('test setting number', async function () { // 原代码使用call属于只读模拟调用,不会实际修改链上状态,需改为普通交易调用 await this._chars.connect(OWNER_ADDRESS).setNumber(123); // 读取目标合约0号存储槽的原始数据 const numberRaw = await ethers.provider.getStorageAt(this._chars.address, 0); // 将原始bytes数据转换为数字类型 const number = ethers.BigNumber.from(numberRaw).toNumber(); expect(number).to.equal(123); });
注意:如果要读取的是mapping、动态数组、结构体等复杂类型的状态变量,需要先按照Solidity存储布局规则计算出目标数据对应的存储槽位置后再读取。
层级读取子合约属性的可行性
无法直接通过myInstance.subcontract.property的链式调用读取属性,需要分步骤操作:
- 如果父合约中的
subcontract状态变量是public修饰的,自动生成的getter只会返回子合约的地址,你需要先获取该地址,再用子合约的ABI实例化出子合约对象,再读取子合约的属性。 - 如果父合约中的
subcontract是internal可见性的,你需要先通过读取存储槽的方式获取子合约地址,再实例化子合约对象读取对应属性。
示例代码如下:
// 假设subcontract是父合约的第二个状态变量,对应存储槽编号为1 const subAddrRaw = await ethers.provider.getStorageAt(parentContract.address, 1); const subContractAddr = ethers.utils.getAddress('0x' + subAddrRaw.slice(-40)); // 实例化子合约对象 const SubContract = await ethers.getContractFactory("你的子合约名称"); const subInstance = SubContract.attach(subContractAddr); // 读取子合约属性,若属性为internal则继续用读存储槽的方式读取 const targetProp = await subInstance.targetPublicProp();
内容的提问来源于stack exchange,提问作者Gabriel Slomka
相关产品推荐
相关产品推荐

