Gap
Two re-initialization invariants are unverified:
- The implementation contract calls
_disableInitializers() in its constructor (src/concrete/Flow.sol:120); calling initialize(bytes) on the implementation directly must revert with the OZ InvalidInitialization selector.
- A clone, once successfully initialized, must reject a second
initialize(bytes) call.
Neither path has a test.
Source
src/concrete/Flow.sol:119-121 (constructor)
src/concrete/Flow.sol:130 (initializer modifier)
Existing related tests
test/src/concrete/Flow.construction.t.sol::testFlowConstructionInitialize — single-init happy path on a clone.
Proposed test
Add to test/src/concrete/Flow.construction.t.sol:
import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {Flow} from "src/concrete/Flow.sol";
function testFlowImplementationInitializeReverts(
address expression,
bytes memory bytecode,
uint256[] memory constants
) external {
expressionDeployerDeployExpression2MockCall(expression, bytes(hex"0007"));
EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1);
flowConfig[0] = EvaluableConfigV3(DEPLOYER, bytecode, constants);
Flow impl = Flow(deployFlowImplementation());
// OZ v4 selector for "Initializable: contract is already initialized" is a string
// revert; for OZ v5+ it is `Initializable.InvalidInitialization()`. Use the
// appropriate matcher for the OZ version pinned in this repo.
vm.expectRevert(Initializable.InvalidInitialization.selector);
impl.initialize(abi.encode(flowConfig));
}
function testFlowCloneInitializeOnceOnly(
address expression,
bytes memory bytecode,
uint256[] memory constants
) external {
expressionDeployerDeployExpression2MockCall(expression, bytes(hex"0007"));
EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1);
flowConfig[0] = EvaluableConfigV3(DEPLOYER, bytecode, constants);
address clone = I_CLONE_FACTORY.clone(deployFlowImplementation(), abi.encode(flowConfig));
vm.expectRevert(Initializable.InvalidInitialization.selector);
Flow(clone).initialize(abi.encode(flowConfig));
}
If Initializable.InvalidInitialization is not the right selector for the OZ version pinned in this repo, replace with the matching error (e.g. vm.expectRevert(bytes("Initializable: contract is already initialized")) for OZ v4).
Gap
Two re-initialization invariants are unverified:
_disableInitializers()in its constructor (src/concrete/Flow.sol:120); callinginitialize(bytes)on the implementation directly must revert with the OZInvalidInitializationselector.initialize(bytes)call.Neither path has a test.
Source
src/concrete/Flow.sol:119-121(constructor)src/concrete/Flow.sol:130(initializermodifier)Existing related tests
test/src/concrete/Flow.construction.t.sol::testFlowConstructionInitialize— single-init happy path on a clone.Proposed test
Add to
test/src/concrete/Flow.construction.t.sol:If
Initializable.InvalidInitializationis not the right selector for the OZ version pinned in this repo, replace with the matching error (e.g.vm.expectRevert(bytes("Initializable: contract is already initialized"))for OZ v4).