Skip to content
53 changes: 28 additions & 25 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,42 +1,45 @@
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeContract() (gas: 248218)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeEtched(address,bytes) (runs: 2048, μ: 7528, ~: 7527)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeZero(address) (runs: 2048, μ: 7214, ~: 7267)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeContract() (gas: 297955)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeEip7702Designator(address,address) (runs: 2048, μ: 7422, ~: 7422)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeEtched(address,bytes) (runs: 2048, μ: 7962, ~: 7960)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeZero(address) (runs: 2048, μ: 7169, ~: 7223)
LibICloneableFactoryV4CloneCreationCodeTest:testCloneCreationCodeDeploysEIP1167Runtime(address,bytes32) (runs: 2048, μ: 45636, ~: 45636)
LibICloneableFactoryV4CloneCreationCodeTest:testCloneCreationCodeIsEIP1167(address) (runs: 2048, μ: 4245, ~: 4245)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltCallerIndependent(bytes32,bytes,address,address) (runs: 2048, μ: 459061, ~: 443058)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDataInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 466856, ~: 450600)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltCallerIndependent(bytes32,bytes,address,address) (runs: 2048, μ: 551304, ~: 537270)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDataInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 559272, ~: 544857)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDiffersFromSenderNamespaced(address,bytes,bytes32,bytes32,address) (runs: 2048, μ: 8139, ~: 8126)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDisjointTagsCloseTheSquat(address,bytes,bytes) (runs: 2048, μ: 467536, ~: 451203)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDoesNotConsumeNamespacedSalt(bytes32,bytes) (runs: 2048, μ: 456513, ~: 438428)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltEvent(bytes32,bytes) (runs: 2048, μ: 359037, ~: 349969)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDisjointTagsCloseTheSquat(address,bytes,bytes) (runs: 2048, μ: 559970, ~: 545141)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDoesNotConsumeNamespacedSalt(bytes32,bytes) (runs: 2048, μ: 548840, ~: 532640)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltEvent(bytes32,bytes) (runs: 2048, μ: 430031, ~: 421907)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltInitializeFailureFails(bytes32,bytes32) (runs: 2048, μ: 167203, ~: 167203)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltIsDomainTaggedHash(address,bytes,bytes32) (runs: 2048, μ: 6499, ~: 6482)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 455295, ~: 438862)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltMatchesPredict(bytes32,bytes) (runs: 2048, μ: 361663, ~: 352503)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltPredictCallerIndependent(address,bytes,bytes32,address,address) (runs: 2048, μ: 12247, ~: 12224)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltSecondDeployReverts(bytes32,bytes,address,address) (runs: 2048, μ: 1040442913, ~: 1040443151)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11124, ~: 11129)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicDataNotInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 464734, ~: 448450)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicEvent(bytes32,bytes) (runs: 2048, μ: 359015, ~: 349951)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 547613, ~: 533074)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltMatchesPredict(bytes32,bytes) (runs: 2048, μ: 432699, ~: 424488)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltPredictCallerIndependent(address,bytes,bytes32,address,address) (runs: 2048, μ: 12246, ~: 12224)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltSecondDeployReverts(bytes32,bytes,address,address) (runs: 2048, μ: 1040445117, ~: 1040445349)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11111, ~: 11129)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicDataNotInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 557117, ~: 542707)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicEvent(bytes32,bytes) (runs: 2048, μ: 430009, ~: 421889)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicInitializeFailureFails(bytes32,bytes32) (runs: 2048, μ: 166695, ~: 166695)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 455229, ~: 438823)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicMatchesPredict(bytes32,bytes) (runs: 2048, μ: 361175, ~: 352030)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 547547, ~: 533035)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicMatchesPredict(bytes32,bytes) (runs: 2048, μ: 432212, ~: 424015)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSaltIsDomainTaggedHash(address,bytes32,address) (runs: 2048, μ: 5563, ~: 5563)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSecondDeployReverts(bytes32,bytes) (runs: 2048, μ: 1040443447, ~: 1040443612)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSenderScoped(bytes32,bytes,address,address) (runs: 2048, μ: 459986, ~: 444001)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11086, ~: 11111)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSecondDeployReverts(bytes32,bytes) (runs: 2048, μ: 1040445655, ~: 1040445817)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSenderScoped(bytes32,bytes,address,address) (runs: 2048, μ: 552210, ~: 538213)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11087, ~: 11111)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressBoundarySalts(address,address) (runs: 2048, μ: 2666, ~: 2666)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressIsCreate2Formula(address,address,bytes32) (runs: 2048, μ: 1724, ~: 1724)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressMatchesOZ(address,address,bytes32) (runs: 2048, μ: 1570, ~: 1570)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressMatchesRealDeploy(address,bytes32) (runs: 2048, μ: 42869, ~: 42869)
LibICloneableFactoryV4Test:testDerivationsDisjoint(address,bytes32,bytes32,bytes) (runs: 2048, μ: 1405, ~: 1398)
LibICloneableFactoryV4Test:testDomainTagsDistinct() (gas: 233)
LibICloneableFactoryV4Test:testDomainTagsDistinct() (gas: 255)
LibICloneableFactoryV4Test:testDomainTagsPinned() (gas: 325)
LibICloneableFactoryV4Test:testEffectiveOpenSaltDataSensitive(bytes32,bytes,bytes) (runs: 2048, μ: 4687, ~: 4682)
LibICloneableFactoryV4Test:testEffectiveOpenSaltBoundarySalts(bytes) (runs: 2048, μ: 1849, ~: 1830)
LibICloneableFactoryV4Test:testEffectiveOpenSaltDataSensitive(bytes32,bytes,bytes) (runs: 2048, μ: 4686, ~: 4682)
LibICloneableFactoryV4Test:testEffectiveOpenSaltEmptyData(bytes32) (runs: 2048, μ: 826, ~: 826)
LibICloneableFactoryV4Test:testEffectiveOpenSaltMatchesFormula(bytes32,bytes) (runs: 2048, μ: 1269, ~: 1257)
LibICloneableFactoryV4Test:testEffectiveOpenSaltPreimageShape(bytes32,bytes) (runs: 2048, μ: 1360, ~: 1348)
LibICloneableFactoryV4Test:testEffectiveOpenSaltMatchesFormula(bytes32,bytes) (runs: 2048, μ: 1268, ~: 1257)
LibICloneableFactoryV4Test:testEffectiveOpenSaltPreimageShape(bytes32,bytes) (runs: 2048, μ: 1314, ~: 1303)
LibICloneableFactoryV4Test:testEffectiveOpenSaltSaltSensitive(bytes32,bytes32,bytes) (runs: 2048, μ: 4177, ~: 4166)
LibICloneableFactoryV4Test:testEffectiveSaltDeployerSensitive(address,address,bytes32) (runs: 2048, μ: 3858, ~: 3858)
LibICloneableFactoryV4Test:testEffectiveSaltMatchesFormula(address,bytes32) (runs: 2048, μ: 865, ~: 865)
LibICloneableFactoryV4Test:testEffectiveSaltPreimageShape(address,bytes32) (runs: 2048, μ: 966, ~: 966)
LibICloneableFactoryV4Test:testEffectiveSaltPreimageShape(address,bytes32) (runs: 2048, μ: 988, ~: 988)
LibICloneableFactoryV4Test:testEffectiveSaltSaltSensitive(address,bytes32,bytes32) (runs: 2048, μ: 3819, ~: 3819)
69 changes: 69 additions & 0 deletions test/concrete/TestCloneable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

import {ICloneableV2} from "src/interface/ICloneableV2.sol";

/// Thrown by a second call to `TestCloneable.initialize`. `ICloneableV2` says
/// the implementation MUST ensure `initialize` can NOT be called more than
/// once; this is how this fixture ensures it.
error TestCloneableAlreadyInitialized();

/// @title TestCloneable
/// @notice THE conforming `ICloneableV2` fixture. Every test that needs a
/// clone that initializes successfully uses this one, so there is a single
/// place where "what a correct `ICloneableV2` does" is written down, and every
/// flow test in the suite is run against something that actually honours the
/// interface rather than against the minimum the factory happens to check.
///
/// Three properties, each load bearing:
///
/// - It stores whatever `data` it was initialized with in the public `sData`,
/// so a test can prove the bytes reached the clone verbatim.
/// - `initialize` can NOT be called more than once — the interface's first
/// normative MUST. The flag is written before the data so a re-entrant call
/// cannot slip past the guard.
/// - It returns the success sentinel written out from the LITERAL STRING
/// `ICloneableV2` names, NOT the imported `ICLONEABLE_V2_SUCCESS`. Importing
/// the constant would put both sides of the library's comparison in
/// lockstep: change the constant and every clone still initializes, because
/// the fixture changed with it. A third party implementing `ICloneableV2`
/// has no such luxury — the interface tells them to return
/// `keccak256("ICloneableV2.initialize")` and they hard-code that value — so
/// the fixture hard-codes it too, and a drift in the constant surfaces as a
/// real `InitializationFailed` through a real factory.
///
/// It also carries the RECOMMENDED typed overload, which the interface
/// requires to revert `InitializeSignatureFn` always.
contract TestCloneable is ICloneableV2 {
/// The data this clone was initialized with. Set once.
bytes public sData;

/// Whether `initialize` has already run on this clone. Storage lives on
/// the clone, not the implementation, because the factory reaches this
/// code through an EIP-1167 `DELEGATECALL` proxy.
bool public sInitialized;

/// @inheritdoc ICloneableV2
function initialize(bytes memory data) external returns (bytes32) {
if (sInitialized) {
revert TestCloneableAlreadyInitialized();
}
sInitialized = true;
sData = data;
// Deliberately the literal, not `ICLONEABLE_V2_SUCCESS`. See the
// contract notice.
return keccak256("ICloneableV2.initialize");
}

/// The RECOMMENDED typed overload of `initialize`, which exists only so an
/// initialization config type appears in the ABI. `ICloneableV2` requires
/// it to revert `InitializeSignatureFn` ALWAYS, so that it is never
/// accidentally called in place of the generic `initialize(bytes)` the
/// factory calls. The parameter is unnamed because it is never read.
/// @return Never returns; the declared return type only exists so the
/// overload has the shape a real typed `initialize` would.
function initialize(uint256) external pure returns (bytes32) {
revert InitializeSignatureFn();
}
}
19 changes: 0 additions & 19 deletions test/src/concrete/TestCloneable.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pragma solidity =0.8.25;
import {Test} from "forge-std-1.16.1/src/Test.sol";

import {LibICloneableFactoryV4, ZeroImplementationCodeSize} from "src/lib/LibICloneableFactoryV4.sol";
import {TestCloneable} from "test/src/concrete/TestCloneable.sol";
import {TestCloneable} from "test/concrete/TestCloneable.sol";

/// @title LibICloneableFactoryV4CheckImplementationCodeTest
/// @notice Tests `LibICloneableFactoryV4.checkImplementationCode`: a codeless
Expand Down Expand Up @@ -38,7 +38,55 @@ contract LibICloneableFactoryV4CheckImplementationCodeTest is Test {
vm.assume(implementation.code.length == 0);
vm.assume(uint160(implementation) > 0x0a);
vm.assume(code.length > 0);
// EIP-3541 forbids DEPLOYING any code whose first byte is `0xef`, so
// no CREATE or CREATE2 can put such code at an address. That leaves
// exactly one way an account can hold it — an EIP-7702 delegation
// designator, which is `0xef0100` followed by an address and is
// therefore EXACTLY 23 bytes. That case is real, so it is not excluded
// here, it is pinned by its own test below.
//
// What this exclusion drops is the rest: `0xef`-leading blobs of any
// other length, which no chain can produce. It cannot weaken the
// property under test, because the guard only ever looks at code
// LENGTH.
//
// It is also what keeps this test from failing for a harness reason:
// `vm.etch` parses a `0xef01` prefix as an EIP-7702 delegation
// designator and rejects it unless the blob is exactly 23 bytes
// ("Eip7702 is not 23 bytes long"), so a fuzz run that drew one died
// in the cheatcode rather than in the code under test.
vm.assume(code[0] != 0xef);
vm.etch(implementation, code);
LibICloneableFactoryV4.checkImplementationCode(implementation);
}

/// The one `0xef`-leading code a real account can hold: an EIP-7702
/// delegation designator, `0xef0100 || address`, exactly 23 bytes. The
/// fuzz test above cannot reach it, so it is pinned here as a fixed case.
///
/// It PASSES the guard, and that is the point worth having on the record.
/// `EXTCODESIZE` on a delegated EOA returns 23, not zero, so the size
/// check cannot tell an ordinary implementation contract from an EOA that
/// has delegated — and unlike a deployed contract, a delegation is
/// REVOCABLE by the account holder at any time. A caller who wants an
/// immutable implementation does not get that from this guard; the guard
/// promises only that something is there.
///
/// Scoped honestly: `foundry.toml` pins `evm_version = "cancun"`, which
/// predates EIP-7702, so what is asserted here is that the 23-byte
/// designator is storable at an address and passes the SIZE check. The
/// execution semantics of delegation are not exercised and this test does
/// not claim them.
function testCheckImplementationCodeEip7702Designator(address delegated, address delegate) external {
vm.assume(delegated.code.length == 0);
vm.assume(uint160(delegated) > 0x0a);

bytes memory designator = abi.encodePacked(hex"ef0100", delegate);
assertEq(designator.length, 23, "an EIP-7702 designator is 23 bytes");

vm.etch(delegated, designator);

assertEq(delegated.code.length, 23, "EXTCODESIZE sees the designator, not zero");
LibICloneableFactoryV4.checkImplementationCode(delegated);
}
}
6 changes: 3 additions & 3 deletions test/src/lib/LibICloneableFactoryV4.cloneDeterministic.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
InitializationFailed,
ZeroImplementationCodeSize
} from "src/lib/LibICloneableFactoryV4.sol";
import {TestCloneFactory} from "test/src/concrete/TestCloneFactory.sol";
import {TestCloneable} from "test/src/concrete/TestCloneable.sol";
import {TestCloneableFailure} from "test/src/concrete/TestCloneableFailure.sol";
import {TestCloneFactory} from "test/concrete/TestCloneFactory.sol";
import {TestCloneable} from "test/concrete/TestCloneable.sol";
import {TestCloneableFailure} from "test/concrete/TestCloneableFailure.sol";

/// @title LibICloneableFactoryV4CloneDeterministicTest
/// @notice Tests `LibICloneableFactoryV4.cloneDeterministic` /
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
InitializationFailed,
ZeroImplementationCodeSize
} from "src/lib/LibICloneableFactoryV4.sol";
import {TestCloneFactory} from "test/src/concrete/TestCloneFactory.sol";
import {TestCloneable} from "test/src/concrete/TestCloneable.sol";
import {TestCloneableFailure} from "test/src/concrete/TestCloneableFailure.sol";
import {TestCloneFactory} from "test/concrete/TestCloneFactory.sol";
import {TestCloneable} from "test/concrete/TestCloneable.sol";
import {TestCloneableFailure} from "test/concrete/TestCloneableFailure.sol";

/// @title LibICloneableFactoryV4CloneDeterministicOpenSaltTest
/// @notice Tests `LibICloneableFactoryV4.cloneDeterministicOpenSalt` /
Expand Down
18 changes: 18 additions & 0 deletions test/src/lib/LibICloneableFactoryV4.predictCloneAddress.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,22 @@ contract LibICloneableFactoryV4PredictCloneAddressTest is Test {
}
assertEq(child, predicted);
}

/// Boundary derived salts are supported, distinct cases: `bytes32(0)` and
/// `bytes32(type(uint256).max)` enter the `CREATE2` preimage exactly as any
/// other salt does, with no special-casing at either end. Pinned
/// deliberately rather than left to the fuzzer — a mutation special-casing
/// the maximum salt survives the whole suite otherwise — and checked
/// against OZ `Clones`, so the expectation is a foreign implementation of
/// the standard rather than this library's own arithmetic restated.
function testPredictCloneAddressBoundarySalts(address factory, address implementation) external pure {
assertEq(
LibICloneableFactoryV4.predictCloneAddress(factory, implementation, bytes32(0)),
Clones.predictDeterministicAddress(implementation, bytes32(0), factory)
);
assertEq(
LibICloneableFactoryV4.predictCloneAddress(factory, implementation, bytes32(type(uint256).max)),
Clones.predictDeterministicAddress(implementation, bytes32(type(uint256).max), factory)
);
}
}
Loading
Loading