The unique property of immutable state variables is that they are set either upon declaration or within the constructor. Typically, immutable state variables are initialized in the constructor, with their values depending on the parameters passed to it. To enhance the testability of contracts with immutable state variables, this property can be leveraged by creating a test contract as demonstrated below:
// 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);
}
}
In the code above, the test contract GodModeUniswapV3PriceFeed
utilizes elements of the Proxy and Factory Method software design patterns. It implements the UniswapV3PriceFeed
interface, but forwards function calls to the internal instance
.
When And when a god mode setter function is called, it updates any immutable state variables set in the constructor, regardless of whether they are internal or publicdeploys a new instance with a different configuration. This method allows to maintain the benefits of using immutable state variables, such as gas optimization and security, while improving testability.
** Example above is from hifi-finance/hifi.
** Disclaimer: I work at Hifi Finance as a full-time blockchain engineer.