在测试使用不可变状态变量的契约时,我遇到了一些挑战。如我们所知,不可变变量在合同部署期间只分配一次,此后不能修改。这提供了诸如高效读取、气体优化和安全性等优点,但也使测试各种场景变得困难,因为每当我想要更改不可变变量的值时,我都必须重新部署契约。以前,我只需创建一个用于测试的契约,用于扩展目标契约,以便公开任何需要修改以模拟不同场景的公共状态变量,如下面的示例代码所示:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
import "../Hifi.sol";
/// @author Hifi
/// @dev Strictly for test purposes. Do not use in production.
contract GodModeHifi is Hifi {
constructor(address account, address minter_) Hifi(account, minter_) {}
function __godMode_setCheckpoint(
address delegatee,
uint32 index,
uint32 blockNumber,
uint32 newVotes
) external {
checkpoints[delegatee][index] = Checkpoint(blockNumber, newVotes);
}
function __godMode_setNumCheckpoints(address account, uint32 newNumCheckpoints) external {
numCheckpoints[account] = newNumCheckpoints;
}
}上面的方法不能处理不可变的状态变量。我正在寻找一种替代方法,它可以帮助我改进我的合同的可测试性,而不影响使用不可变状态变量(气体优化、安全性等)的好处。
**上面的例子来自高保真-金融/高保真-治理。
**免责声明:我是Hifi Finance的全职区块链工程师。
发布于 2023-03-21 01:13:34
不可变状态变量的唯一属性是它们是在声明时或在构造函数中设置的。通常,不可变状态变量在构造函数中初始化,其值取决于传递给它的参数。为了增强具有不可变状态变量的契约的可测试性,可以通过创建测试契约来利用此属性,如下所示:
// SPDX-License-Identifier: UNLICENSED
// solhint-disable func-name-mixedcase
pragma solidity ^0.8.4;
import "@prb/contracts/token/erc20/IErc20.sol";
import "../external/uniswap/interfaces/IUniswapV3Pool.sol";
import "../oracles/IUniswapV3PriceFeed.sol";
import "../oracles/UniswapV3PriceFeed.sol";
/// @title GodModeUniswapV3PriceFeed
/// @author Hifi
/// @dev Strictly for test purposes. Do not use in production.
contract GodModeUniswapV3PriceFeed is IUniswapV3PriceFeed {
IUniswapV3PriceFeed internal instance;
constructor(
IUniswapV3Pool pool_,
IErc20 refAsset_,
uint32 twapInterval_
) {
instance = new UniswapV3PriceFeed(pool_, refAsset_, twapInterval_);
}
...
function pool() external view returns (IUniswapV3Pool) {
return instance.pool();
}
function refAsset() external view returns (IErc20) {
return instance.refAsset();
}
function twapInterval() external view returns (uint32) {
return instance.twapInterval();
}
...
function __godMode_setPool(IUniswapV3Pool newPool) external {
instance = new UniswapV3PriceFeed(newPool, instance.refAsset(), instance.twapInterval());
}
function __godMode_setRefAsset(IErc20 newRefAsset) external {
instance = new UniswapV3PriceFeed(instance.pool(), newRefAsset, instance.twapInterval());
}
function __godMode_setTwapInterval(uint32 newTwapInterval) external {
instance = new UniswapV3PriceFeed(instance.pool(), instance.refAsset(), newTwapInterval);
}
}在上面的代码中,测试契约GodModeUniswapV3PriceFeed使用代理和工厂方法软件设计模式的元素。它实现了UniswapV3PriceFeed接口,但将函数调用转发给内部instance。当调用一个god模式设置函数时,它会部署一个具有不同配置的新实例。该方法允许在提高可测试性的同时,维护使用不可变状态变量(如气体优化和安全性)的好处。
**上面的例子来自高保真-金融/高保真。
**免责声明:我是Hifi Finance的全职区块链工程师。
发布于 2023-03-19 22:00:52
测试时最好的方法是使用试验装置 (安装/拆卸)。
但是,一种完全人为的方法可以使可变的不可变变量成为私有/内部变量,并包含一个虚拟视图getter函数。见下文:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract Foo {
uint256 immutable _VALUE;
constructor(uint256 _initialValue) {
_VALUE = _initialValue;
}
function VALUE() external view virtual returns (uint256) {
return _VALUE;
}
}
contract GodModeFoo is Foo {
uint256 private __godMode_value;
bool private __use_godMode_value;
constructor(uint256 _initalValue) Foo(_initalValue) {}
function __set_value(uint256 _value) external {
__use_godMode_value = true;
__godMode_value = _value;
}
function VALUE() external view override(Foo) returns (uint256) {
if (__use_godMode_value) {
return __godMode_value;
}
return _VALUE;
}
}您将注意到,GodModeFoo契约具有一个getter函数,该函数覆盖基本合同。默认情况下将返回不可变的值,但是如果调用setter函数,视图函数将切换到返回不可变的值。
尽管如此,实现您想要的东西(对于一个不可变的跨测试的不同值)的最好方法是使用固定和参数化,这是单元测试中的标准实践。
https://ethereum.stackexchange.com/questions/147614
复制相似问题