pragma solidity 0.8.20; import {Test} from "forge-std/Test.sol"; import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; import {RequestPacking} from "../../src/libraries/Packing.sol"; contract MockStaking is Test { Gatekeeper public gatekeeper; mapping(address => uint256) private _materializedAmounts; constructor(uint256 existential) { gatekeeper = new Gatekeeper(existential, address(0)); } function redoGatekeeper(uint256 existential) external { gatekeeper = new Gatekeeper(existential, address(gatekeeper)); } function runGhost(bytes32 receiver, uint256 amount) external { gatekeeper.ghost(receiver, amount); } function runMaterialize(uint256 exodusSession, uint256 amount, uint256 packed) external { vm.prank(address(gatekeeper)); gatekeeper.materialize(exodusSession, amount, packed); } function materialize(address receiver, uint256 amount) external { _materializedAmounts[receiver] += amount; } function materializedAmount(address who) external view returns (uint256) { return _materializedAmounts[who]; } } contract GatekeeperStorageHistoryTest is Test { using RequestPacking for RequestPacking.RequestPayload; address constant ALICE = 0x0000000000000000000000000000000000000001; address constant BOB = 0x0000000000000000000000000000000000000002; uint256 constant INIT_AMOUNT = 1337 * 1e18; uint256 constant EXISTENTIAL = 0; Gatekeeper gatekeeper; MockStaking staking; function setUp() public { vm.prank(ALICE); staking = new MockStaking(EXISTENTIAL); staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT); gatekeeper = staking.gatekeeper(); } function test_correctStorageHistoryInitialization() public view { address storageHistory = gatekeeper.storageHistory(); IStorageHistory.DeploymentSnapshot memory snapshot = IStorageHistory(storageHistory).deploymentSnapshot(); assertEq(snapshot.deployedAt, 0); assertEq(snapshot.amountIn, INIT_AMOUNT); assertEq(snapshot.amountOut, 0); assertEq(gatekeeper.ghostedSupply(), INIT_AMOUNT); } function test_historicalAmountsOnlyIncrease(uint256 ghostAmount, uint64 exodusSession) public { vm.assume(ghostAmount > 0 && ghostAmount < INIT_AMOUNT); uint256 amountToGhost = ghostAmount; uint256 amountToMaterialize = ghostAmount / 2; bytes32 receiver = bytes32(abi.encodePacked(ALICE)); address storageHistory = gatekeeper.storageHistory(); IStorageHistory.DeploymentSnapshot memory snapshot = IStorageHistory(storageHistory).deploymentSnapshot(); uint104 amountIn = snapshot.amountIn; uint104 amountOut = snapshot.amountOut; if (ghostAmount % 2 == 0) { staking.runGhost(receiver, amountToGhost); // forge-lint: disable-next-line(unsafe-typecast) amountIn += uint104(amountToGhost); } else { // forge-lint: disable-next-line(unsafe-typecast) uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB); uint256 previousAmount = staking.materializedAmount(BOB); staking.runMaterialize(exodusSession, amountToMaterialize, packed); assertEq(previousAmount + amountToMaterialize, staking.materializedAmount(BOB)); // forge-lint: disable-next-line(unsafe-typecast) amountOut += uint104(amountToMaterialize); } IStorageHistory.DeploymentSnapshot memory newSnapshot = IStorageHistory(storageHistory).deploymentSnapshot(); assertEq(newSnapshot.amountIn, amountIn); assertEq(newSnapshot.amountOut, amountOut); assertEq(gatekeeper.ghostedSupply(), amountIn - amountOut); } function test_inheritanceWorksForHistoricalStorge() public { uint256 exodusSession = 69; uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB); staking.runMaterialize(exodusSession, INIT_AMOUNT, packed); staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT); address prevStorageHistory = gatekeeper.storageHistory(); IStorageHistory.DeploymentSnapshot memory prevSnapshot = IStorageHistory(prevStorageHistory).deploymentSnapshot(); staking.redoGatekeeper(EXISTENTIAL + 1); assert(gatekeeper != staking.gatekeeper()); address currStorageHistory = gatekeeper.storageHistory(); IStorageHistory.DeploymentSnapshot memory currSnapshot = IStorageHistory(currStorageHistory).deploymentSnapshot(); assertEq(prevSnapshot.amountIn, currSnapshot.amountIn); assertEq(prevSnapshot.amountOut, currSnapshot.amountOut); assertEq(prevSnapshot.deployedAt, currSnapshot.deployedAt); assert(IStorageHistory(prevStorageHistory).isTransactionExecuted(exodusSession)); assert(IStorageHistory(currStorageHistory).isTransactionExecuted(exodusSession)); assertEq(gatekeeper.existentialDeposit(), EXISTENTIAL); assertEq(staking.gatekeeper().existentialDeposit(), EXISTENTIAL + 1); } }