1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
// 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);
}
}
|