// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {IWETH} from "../src/IWETH.sol"; import {WETHRegistry} from "../src/WETHRegistry.sol"; import {CommonBase} from "forge-std/Base.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; /// @title Fixtures /// @author Julien Béranger /// @notice Brings a chain up the way a real one comes up: the registry at its /// deterministic address, a genuine wETH at some address, registered. /// @dev The wETH here is real runtime bytecode lifted off a live chain, etched /// into place. A mock would need its codehash added to the allowlist to /// be registrable, which would mean testing a different allowlist from /// the one that ships. Etching real code keeps the test and the product /// honest about the same ten hashes. abstract contract Fixtures is CommonBase, StdCheats { /// @notice Salt the registry is deployed with, here and in the script. bytes32 internal constant REGISTRY_SALT = keccak256("LOVE.WETHRegistry"); /// @notice OP Stack legacy WETH9, as deployed on Base, Mode and Zora. string internal constant WETH9_OP_LEGACY = "test/fixtures/weth9-op-legacy.hex"; /// @notice Canonical WETH9, as deployed on Ethereum mainnet. string internal constant WETH9_CANONICAL = "test/fixtures/weth9-canonical.hex"; /// @notice Put real wETH runtime bytecode at `where`. /// @dev Reads the fixture rather than embedding it, so refreshing a /// fixture from chain state does not mean editing Solidity. /// @param where The address to place it at. /// @param fixture Path to the runtime bytecode, `WETH9_*` above. /// @return The wETH now living at `where`. function etchWeth(address where, string memory fixture) internal returns (IWETH) { vm.etch(where, vm.parseBytes(vm.trim(vm.readFile(fixture)))); vm.label(where, "WETH"); return IWETH(where); } /// @notice Deploy the registry to the address `Love` expects. /// @dev Through the canonical deterministic deployer with `REGISTRY_SALT`, /// exactly as the deploy script does, so the address it lands at is /// the one compiled into `Love.REGISTRY` rather than one arranged for /// the test. /// @return The registry. function deployRegistry() internal returns (WETHRegistry) { (bool ok, bytes memory ret) = CREATE2_FACTORY.call(abi.encodePacked(REGISTRY_SALT, type(WETHRegistry).creationCode)); require(ok, "registry deployment failed"); // casting to 'bytes20' is safe because the deployer returns the 20-byte address, raw // forge-lint: disable-next-line(unsafe-typecast) WETHRegistry registry = WETHRegistry(payable(address(bytes20(ret)))); vm.label(address(registry), "WETHRegistry"); return registry; } /// @notice Register `weth`, funding the one-wei probe. /// @param registry The registry to register with. /// @param weth The wETH to register. function register(WETHRegistry registry, IWETH weth) internal { uint256 probe = registry.PROBE(); deal(address(this), address(this).balance + probe); registry.register{value: probe}(weth); } /// @notice The whole chain in one call: registry, wETH, registration. /// @param wethAt The address to etch wETH at. /// @param fixture Which implementation to etch. /// @return registry The registry, with `wethAt` registered. /// @return weth The wETH now backing any `Love` deployed here. function setUpChain(address wethAt, string memory fixture) internal returns (WETHRegistry registry, IWETH weth) { registry = deployRegistry(); weth = etchWeth(wethAt, fixture); register(registry, weth); } }