Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
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, μ: 553640, ~: 537270)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDataInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 561371, ~: 544860)
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, μ: 562154, ~: 545415)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDoesNotConsumeNamespacedSalt(bytes32,bytes) (runs: 2048, μ: 551175, ~: 532640)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltEvent(bytes32,bytes) (runs: 2048, μ: 431200, ~: 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:testCloneDeterministicOpenSaltIsDomainTaggedHash(address,bytes,bytes32) (runs: 2048, μ: 6500, ~: 6482)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 550042, ~: 533074)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltMatchesPredict(bytes32,bytes) (runs: 2048, μ: 433874, ~: 424488)
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:testCloneDeterministicOpenSaltSecondDeployReverts(bytes32,bytes,address,address) (runs: 2048, μ: 1040445147, ~: 1040445384)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11112, ~: 11129)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicDataNotInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 559215, ~: 542710)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicEvent(bytes32,bytes) (runs: 2048, μ: 431178, ~: 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, μ: 549976, ~: 533035)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicMatchesPredict(bytes32,bytes) (runs: 2048, μ: 433386, ~: 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, μ: 1040445683, ~: 1040445864)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSenderScoped(bytes32,bytes,address,address) (runs: 2048, μ: 554608, ~: 538213)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11088, ~: 11111)
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)
Expand Down
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);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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
Loading