// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {LoveScript} from "../script/Love.s.sol"; import {Love} from "../src/Love.sol"; import {Test} from "forge-std/Test.sol"; /// @notice The token is meant to live at the same address on every EVM network, /// so these tests pin the CREATE2 deployment path and the inputs the /// address derives from. contract LoveCreate2Test is Test { bytes32 constant SALT = keccak256("LOVE"); LoveScript script; function setUp() public { script = new LoveScript(); // The canonical deterministic deployer, as it exists on live networks. assertGt(CREATE2_FACTORY.code.length, 0, "create2 deployer missing"); } function test_DeploysAtPredictedAddress() public { address predicted = vm.computeCreate2Address(SALT, keccak256(type(Love).creationCode), CREATE2_FACTORY); Love deployed = Love(_deploy(SALT)); assertEq(address(deployed), predicted); assertEq(deployed.symbol(), "LOVE"); } /// @dev What the deploy script prints must be what the chain gives back. function test_ScriptPredictionMatchesDeployment() public { assertEq(script.predict(SALT), _deploy(SALT)); } function test_ScriptUsesKeccakOfLoveAsDefaultSalt() public view { assertEq(script.DEFAULT_SALT(), SALT); } /// @dev Same salt, same code, same address — that is the whole point. function test_PredictionIsChainAgnostic() public { address onThisChain = script.predict(SALT); vm.chainId(137); assertEq(script.predict(SALT), onThisChain); vm.chainId(42_161); assertEq(script.predict(SALT), onThisChain); } function test_DifferentSaltsGiveDifferentAddresses() public view { assertTrue(script.predict(SALT) != script.predict(keccak256("LOVE2"))); } /// @dev Redeploying with the same salt must fail, not silently return the /// existing token. function test_RevertWhen_RedeployingWithSameSalt() public { _deploy(SALT); (bool ok,) = CREATE2_FACTORY.call(abi.encodePacked(SALT, type(Love).creationCode)); assertFalse(ok); } function testFuzz_PredictionMatchesDeployment(bytes32 salt) public { assertEq(script.predict(salt), _deploy(salt)); } function _deploy(bytes32 salt) internal returns (address deployed) { (bool ok, bytes memory ret) = CREATE2_FACTORY.call(abi.encodePacked(salt, type(Love).creationCode)); require(ok, "create2 deployment failed"); // casting to 'bytes20' is safe because the deployer returns the 20-byte address, raw // forge-lint: disable-next-line(unsafe-typecast) deployed = address(bytes20(ret)); } }