diff --git a/script/20260716-migrate-beacon-owners-ethereum.s.sol b/script/20260716-migrate-beacon-owners-ethereum.s.sol index 1b08b943..3161416b 100644 --- a/script/20260716-migrate-beacon-owners-ethereum.s.sol +++ b/script/20260716-migrate-beacon-owners-ethereum.s.sol @@ -39,8 +39,8 @@ contract MigrateBeaconOwnersEthereum is Script { function run() external { address safe = LibSafeInvariants.STOX_TOKEN_OWNER_SAFE_ETHEREUM; require(safe != address(0), "Ethereum token-owner Safe not pinned"); - address[3] memory beaconList = LibProdBeacons0_1_1.beacons(); - address[3] memory implList = LibProdBeacons0_1_1.implementations(); + address[4] memory beaconList = LibProdBeacons0_1_1.beacons(); + address[4] memory implList = LibProdBeacons0_1_1.implementations(); // Pre-flight: every beacon is deployed, is the OZ UpgradeableBeacon, // is still owned by the deploy EOA, and points at its pinned impl. diff --git a/script/20260722-migrate-beacon-owners-hyperevm.s.sol b/script/20260722-migrate-beacon-owners-hyperevm.s.sol index 70ba9146..c0f53f27 100644 --- a/script/20260722-migrate-beacon-owners-hyperevm.s.sol +++ b/script/20260722-migrate-beacon-owners-hyperevm.s.sol @@ -47,8 +47,8 @@ contract MigrateBeaconOwnersHyperEvm is Script { address safe = LibSafeInvariants.STOX_TOKEN_OWNER_SAFE_HYPEREVM; require(safe != address(0), "HyperEVM token-owner Safe not pinned"); require(block.chainid == LibSafeInvariants.HYPEREVM_CHAIN_ID, "not HyperEVM - wrong --rpc-url"); - address[3] memory beaconList = LibProdBeacons0_1_1.beacons(); - address[3] memory implList = LibProdBeacons0_1_1.implementations(); + address[4] memory beaconList = LibProdBeacons0_1_1.beacons(); + address[4] memory implList = LibProdBeacons0_1_1.implementations(); // Pre-flight: every beacon is deployed, is the OZ UpgradeableBeacon, // is still owned by the deploy EOA, and points at its pinned impl. diff --git a/script/20260729-migrate-governance-to-timelock.s.sol b/script/20260729-migrate-governance-to-timelock.s.sol index 98f25d88..fcff9fac 100644 --- a/script/20260729-migrate-governance-to-timelock.s.sol +++ b/script/20260729-migrate-governance-to-timelock.s.sol @@ -13,6 +13,7 @@ import {IBeacon} from "@openzeppelin-contracts-5.6.1/proxy/beacon/IBeacon.sol"; import {IGnosisSafe} from "../src/interface/IGnosisSafe.sol"; import {LibAuthoriserInvariants, RoleGrant} from "../src/lib/LibAuthoriserInvariants.sol"; import {LibBeaconInvariants} from "../src/lib/LibBeaconInvariants.sol"; +import {LibOrchestratorInvariants} from "../src/lib/LibOrchestratorInvariants.sol"; import {LibProdDeployV4} from "../src/generated/LibProdDeployV4.sol"; import {LibSafeInvariants} from "../src/lib/LibSafeInvariants.sol"; import {LibSafeOps, SafeTx} from "../src/lib/LibSafeOps.sol"; @@ -308,7 +309,7 @@ contract MigrateGovernanceToTimelock is Script { // What the beacons serve, captured before the bundle so the // post-state can prove the migration moved ownership only. - address[3] memory beaconImplsBefore = _beaconImplementations(); + address[4] memory beaconImplsBefore = _beaconImplementations(); // --- Build the bundle --------------------------------------------- @@ -447,14 +448,14 @@ contract MigrateGovernanceToTimelock is Script { address timelock, address authoriser, TokenInstance[] memory tokens, - address[3] memory beaconImplsBefore + address[4] memory beaconImplsBefore ) internal view { LibTokenInvariants.assertUniformOwnership(tokens, timelock); LibTokenInvariants.assertUniformAuthoriser(tokens, authoriser); LibBeaconInvariants.assertProdBeaconsOwnedBy(block.chainid, timelock); - address[3] memory beaconsAfter = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); - address[3] memory beaconImplsAfter = _beaconImplementations(); + address[4] memory beaconsAfter = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beaconImplsAfter = _beaconImplementations(); for (uint256 i = 0; i < beaconsAfter.length; i++) { if (beaconImplsAfter[i] != beaconImplsBefore[i]) { revert BeaconImplementationMoved(beaconsAfter[i], beaconImplsBefore[i], beaconImplsAfter[i]); @@ -550,20 +551,34 @@ contract MigrateGovernanceToTimelock is Script { /// @return targets The still-Safe-owned in-use beacons, in fixed /// (receipt, receipt vault, wrapped token vault) order. function _selectBeaconTargets(address safe, address timelock) internal view returns (address[] memory targets) { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + // Index-aligned: the set spans two build generations, so one pin + // cannot cover it. See `prodBeaconCodehashesForChainId`. + bytes32[4] memory expectedCodehashes = LibBeaconInvariants.prodBeaconCodehashesForChainId(block.chainid); address[] memory candidates = new address[](beacons.length); uint256 count = 0; for (uint256 i = 0; i < beacons.length; i++) { bytes32 codehash = beacons[i].codehash; - if (codehash != LibBeaconInvariants.UPGRADEABLE_BEACON_CODEHASH) { - revert MigrationBeaconCodehashMismatch( - beacons[i], LibBeaconInvariants.UPGRADEABLE_BEACON_CODEHASH, codehash - ); + if (codehash != expectedCodehashes[i]) { + revert MigrationBeaconCodehashMismatch(beacons[i], expectedCodehashes[i], codehash); } address actual = Ownable(beacons[i]).owner(); if (actual == timelock) { continue; } + // The orchestrator beacon joins the governed set mid-lifecycle: + // until its own EOA -> Safe migration + // (20260818-migrate-orchestrator-beacon-owner) executes, it is + // not the Safe's to move — skip it; a later dispatch of this + // script picks it up once Safe-owned. After that migration's + // deadline the baked EOA stops being an accepted state and the + // refusal below fires. + if ( + i == LibBeaconInvariants.ORCHESTRATOR_BEACON_INDEX && actual == LibProdDeployV4.BEACON_INITIAL_OWNER + && block.timestamp < LibOrchestratorInvariants.ST0X_ORCHESTRATOR_BEACON_OWNER_MIGRATION_DEADLINE + ) { + continue; + } if (actual != safe) { revert UnexpectedBeaconOwner(beacons[i], actual); } @@ -583,8 +598,8 @@ contract MigrateGovernanceToTimelock is Script { /// would propagate to every production proxy on the chain. /// @return impls The three current implementations, index-aligned with /// `prodBeaconsForChainId`. - function _beaconImplementations() internal view returns (address[3] memory impls) { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + function _beaconImplementations() internal view returns (address[4] memory impls) { + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); for (uint256 i = 0; i < beacons.length; i++) { impls[i] = IBeacon(beacons[i]).implementation(); } diff --git a/script/20260825-upgrade-fleet-to-0-1-30.s.sol b/script/20260825-upgrade-fleet-to-0-1-30.s.sol index 23e8e81b..7674d2ec 100644 --- a/script/20260825-upgrade-fleet-to-0-1-30.s.sol +++ b/script/20260825-upgrade-fleet-to-0-1-30.s.sol @@ -107,7 +107,7 @@ contract UpgradeFleetTo0_1_30 is Script { /// @param beacons The chain's in-use beacons (receipt, receipt vault, /// wrapped token vault) — index order pinned by `LibProdBeacons*`. /// @return txs The self-scoped upgrade transactions. - function authorBundle(address[3] memory beacons) internal view returns (SafeTx[] memory txs) { + function authorBundle(address[4] memory beacons) internal view returns (SafeTx[] memory txs) { address[2] memory gated = [ beacons[LibBeaconInvariants.RECEIPT_BEACON_INDEX], beacons[LibBeaconInvariants.RECEIPT_VAULT_BEACON_INDEX] ]; @@ -213,7 +213,7 @@ contract UpgradeFleetTo0_1_30 is Script { // The in-use beacons are deployed, OZ bytecode, Safe-owned. LibBeaconInvariants.assertProdBeaconsOwnedByChainSafe(block.chainid); - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); // --- Build the bundle ---------------------------------------------- diff --git a/script/20260831-enable-orchestrator-roles.s.sol b/script/20260831-enable-orchestrator-roles.s.sol index 11c4f505..55b03ef6 100644 --- a/script/20260831-enable-orchestrator-roles.s.sol +++ b/script/20260831-enable-orchestrator-roles.s.sol @@ -141,7 +141,7 @@ contract EnableOrchestratorRoles is Script { // Index order pinned by LibProdBeacons*: receipt, receipt vault, // wrapped token vault. The wrapped-token-vault beacon is not part of // the orchestrator's surface and is not gated here. - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); address receiptImpl = IBeacon(beacons[0]).implementation(); if (receiptImpl != LibProdDeployV4.STOX_RECEIPT_0_1_30) { revert FleetNotUpgraded(beacons[0], LibProdDeployV4.STOX_RECEIPT_0_1_30, receiptImpl); diff --git a/src/lib/LibBeaconInvariants.sol b/src/lib/LibBeaconInvariants.sol index 3dd0b24a..78d32914 100644 --- a/src/lib/LibBeaconInvariants.sol +++ b/src/lib/LibBeaconInvariants.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.25; import {IBeacon} from "@openzeppelin-contracts-5.6.1/proxy/beacon/IBeacon.sol"; import {LibMigrationInvariant} from "./LibMigrationInvariant.sol"; +import {LibOrchestratorInvariants} from "./LibOrchestratorInvariants.sol"; +import {LibProdDeployV4} from "../generated/LibProdDeployV4.sol"; import {LibProdBeaconsBase} from "./LibProdBeaconsBase.sol"; import {LibProdBeacons0_1_1} from "./LibProdBeacons0_1_1.sol"; import {LibSafeInvariants} from "./LibSafeInvariants.sol"; @@ -230,6 +232,26 @@ library LibBeaconInvariants { /// `prodBeaconsForChainId`. uint256 internal constant WRAPPED_TOKEN_VAULT_BEACON_INDEX = 2; + /// @notice Position of the orchestrator beacon in `prodBeaconsForChainId`. + uint256 internal constant ORCHESTRATOR_BEACON_INDEX = 3; + + /// @notice Expected runtime codehash of each beacon in + /// `prodBeaconsForChainId`, index-aligned with it. + /// @dev The set spans two build generations and a single pin cannot cover + /// both: the three token beacons are the V1 build (858 bytes), the + /// orchestrator beacon is OZ 5.6.1 at the current `optimizer_runs` + /// (728 bytes). Asserting the whole set against one constant reverts on + /// the orchestrator beacon and takes every chain's migration with it. + /// @return The expected codehash per beacon. + function prodBeaconCodehashesForChainId(uint256) internal pure returns (bytes32[4] memory) { + return [ + UPGRADEABLE_BEACON_CODEHASH, + UPGRADEABLE_BEACON_CODEHASH, + UPGRADEABLE_BEACON_CODEHASH, + UPGRADEABLE_BEACON_CODEHASH_0_1_30 + ]; + } + /// @notice The three production beacons IN USE on the active chain, in a /// fixed order (receipt, receipt vault, wrapped token vault). Beacon /// addresses are per-chain deploy artifacts that never change once a @@ -240,7 +262,7 @@ library LibBeaconInvariants { /// order); this map only dispatches by chain id. /// @param chainId The active chain id (`block.chainid`). /// @return The chain's three in-use beacon addresses. - function prodBeaconsForChainId(uint256 chainId) internal view returns (address[3] memory) { + function prodBeaconsForChainId(uint256 chainId) internal view returns (address[4] memory) { if (chainId == LibSafeInvariants.BASE_CHAIN_ID) { return LibProdBeaconsBase.beacons(); } @@ -285,10 +307,24 @@ library LibBeaconInvariants { /// @param expectedOwner The address every in-use beacon must report as /// `owner()`. function assertProdBeaconsOwnedBy(uint256 chainId, address expectedOwner) internal view { - address[3] memory beacons = prodBeaconsForChainId(chainId); + address[4] memory beacons = prodBeaconsForChainId(chainId); + bytes32[4] memory codehashes = prodBeaconCodehashesForChainId(chainId); for (uint256 i = 0; i < beacons.length; i++) { - _assertDeployedPinnedBeacon(beacons[i]); + _assertDeployedPinnedBeacon(beacons[i], codehashes[i]); address actualOwner = IOwnable(beacons[i]).owner(); + // The orchestrator beacon joins the governed set mid-lifecycle: + // its constructor bakes the deploy EOA as owner and the + // `20260818-migrate-orchestrator-beacon-owner` EOA broadcast + // moves it to the chain's Safe. Until that migration's deadline, + // the EOA is an accepted pre-state; after it, only the expected + // owner passes — the same window `LibOrchestratorInvariants. + // assertBeaconSet` applies. + if ( + i == ORCHESTRATOR_BEACON_INDEX && actualOwner == LibProdDeployV4.BEACON_INITIAL_OWNER + && block.timestamp < LibOrchestratorInvariants.ST0X_ORCHESTRATOR_BEACON_OWNER_MIGRATION_DEADLINE + ) { + continue; + } if (actualOwner != expectedOwner) { revert BeaconOwnerMismatch(beacons[i], expectedOwner, actualOwner); } @@ -327,10 +363,22 @@ library LibBeaconInvariants { internal view { - address[3] memory beacons = prodBeaconsForChainId(chainId); + address[4] memory beacons = prodBeaconsForChainId(chainId); + bytes32[4] memory codehashes = prodBeaconCodehashesForChainId(chainId); for (uint256 i = 0; i < beacons.length; i++) { - _assertDeployedPinnedBeacon(beacons[i]); - LibMigrationInvariant.assertMigration("beacon.owner()", IOwnable(beacons[i]).owner(), pre, post, deadline); + _assertDeployedPinnedBeacon(beacons[i], codehashes[i]); + address actualOwner = IOwnable(beacons[i]).owner(); + // See `assertProdBeaconsOwnedBy`: the orchestrator beacon's own + // EOA -> Safe migration window overlaps the governance window, + // so its baked initial owner is an accepted extra pre-state + // until that migration's deadline. + if ( + i == ORCHESTRATOR_BEACON_INDEX && actualOwner == LibProdDeployV4.BEACON_INITIAL_OWNER + && block.timestamp < LibOrchestratorInvariants.ST0X_ORCHESTRATOR_BEACON_OWNER_MIGRATION_DEADLINE + ) { + continue; + } + LibMigrationInvariant.assertMigration("beacon.owner()", actualOwner, pre, post, deadline); } } @@ -341,13 +389,13 @@ library LibBeaconInvariants { /// selector and report whatever owner passes the caller's check, so /// the pin is what makes the subsequent read meaningful. /// @param beacon The beacon to gate. - function _assertDeployedPinnedBeacon(address beacon) private view { + function _assertDeployedPinnedBeacon(address beacon, bytes32 expectedCodehash) private view { if (beacon.code.length == 0) { revert BeaconNotDeployed(beacon); } bytes32 actualCodehash = beacon.codehash; - if (actualCodehash != UPGRADEABLE_BEACON_CODEHASH) { - revert BeaconCodehashMismatch(beacon, UPGRADEABLE_BEACON_CODEHASH, actualCodehash); + if (actualCodehash != expectedCodehash) { + revert BeaconCodehashMismatch(beacon, expectedCodehash, actualCodehash); } } } diff --git a/src/lib/LibProdBeacons0_1_1.sol b/src/lib/LibProdBeacons0_1_1.sol index ae095151..ed0f5761 100644 --- a/src/lib/LibProdBeacons0_1_1.sol +++ b/src/lib/LibProdBeacons0_1_1.sol @@ -50,13 +50,14 @@ library LibProdBeacons0_1_1 { /// generated `0_1_1` pin. `view` because the first two are live reads from /// the deployer (which is why callers run against an Ethereum fork). /// @return The three Ethereum beacon addresses. - function beacons() internal view returns (address[3] memory) { + function beacons() internal view returns (address[4] memory) { IST0xVaultBeaconSet deployer = IST0xVaultBeaconSet(LibProdDeployV4.STOX_OFFCHAIN_ASSET_RECEIPT_VAULT_BEACON_SET_DEPLOYER_0_1_1); return [ address(deployer.iReceiptBeacon()), address(deployer.iOffchainAssetReceiptVaultBeacon()), - LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_BEACON_0_1_1 + LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_BEACON_0_1_1, + LibProdDeployV4.ST0X_ORCHESTRATOR_BEACON ]; } @@ -65,11 +66,12 @@ library LibProdBeacons0_1_1 { /// deterministic addresses on every chain, so no separate Ethereum copy. /// Asserted unchanged across the ownership transfer. /// @return The three implementation addresses. - function implementations() internal pure returns (address[3] memory) { + function implementations() internal pure returns (address[4] memory) { return [ LibProdDeployV4.STOX_RECEIPT_0_1_1, LibProdDeployV4.STOX_RECEIPT_VAULT_0_1_1, - LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_0_1_1 + LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_0_1_1, + LibProdDeployV4.ST0X_ORCHESTRATOR_0_1_30 ]; } } diff --git a/src/lib/LibProdBeaconsBase.sol b/src/lib/LibProdBeaconsBase.sol index 23457613..45796e8d 100644 --- a/src/lib/LibProdBeaconsBase.sol +++ b/src/lib/LibProdBeaconsBase.sol @@ -29,11 +29,12 @@ library LibProdBeaconsBase { /// receipt vault, wrapped token vault) — index-aligned with /// `implementations()` and with `LibProdBeacons0_1_1.beacons()`. /// @return The three Base beacon addresses. - function beacons() internal pure returns (address[3] memory) { + function beacons() internal pure returns (address[4] memory) { return [ LibProdDeployV1.STOX_RECEIPT_BEACON_V1, LibProdDeployV1.STOX_RECEIPT_VAULT_BEACON_V1, - LibProdDeployV1.STOX_WRAPPED_TOKEN_VAULT_BEACON_V1 + LibProdDeployV1.STOX_WRAPPED_TOKEN_VAULT_BEACON_V1, + LibProdDeployV4.ST0X_ORCHESTRATOR_BEACON ]; } @@ -42,11 +43,12 @@ library LibProdBeaconsBase { /// same deterministic addresses `LibProdBeacons0_1_1.implementations()` /// resolves, because implementation parity across chains is the goal. /// @return The three implementation addresses. - function implementations() internal pure returns (address[3] memory) { + function implementations() internal pure returns (address[4] memory) { return [ LibProdDeployV4.STOX_RECEIPT_0_1_1, LibProdDeployV4.STOX_RECEIPT_VAULT_0_1_1, - LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_0_1_1 + LibProdDeployV4.STOX_WRAPPED_TOKEN_VAULT_0_1_1, + LibProdDeployV4.ST0X_ORCHESTRATOR_0_1_30 ]; } } diff --git a/test/script/20260729-migrate-governance-to-timelock.t.sol b/test/script/20260729-migrate-governance-to-timelock.t.sol index 00f7f953..e6c2ffed 100644 --- a/test/script/20260729-migrate-governance-to-timelock.t.sol +++ b/test/script/20260729-migrate-governance-to-timelock.t.sol @@ -164,7 +164,7 @@ contract MigrateGovernanceToTimelockTest is Test { assertGt(vaultTargets.length, 0, "fork state should have Safe-owned vaults pre-migration"); address[] memory beaconTargets = _safeOwnedBeacons(safe); assertGt(beaconTargets.length, 0, "fork state should have Safe-owned beacons pre-migration"); - address[3] memory implsBefore = _beaconImplementations(); + address[4] memory implsBefore = _beaconImplementations(); new MigrateGovernanceToTimelockHarness(timelock).run(); @@ -203,7 +203,7 @@ contract MigrateGovernanceToTimelockTest is Test { /// @param safe The chain's token-owner Safe. /// @return targets The Safe-owned beacons, in pinned order. function _safeOwnedBeacons(address safe) internal view returns (address[] memory targets) { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); address[] memory candidates = new address[](beacons.length); uint256 count = 0; for (uint256 i = 0; i < beacons.length; i++) { @@ -221,8 +221,8 @@ contract MigrateGovernanceToTimelockTest is Test { /// @notice The implementation each in-use beacon currently serves, in /// `prodBeaconsForChainId` order. /// @return impls The three current implementations. - function _beaconImplementations() internal view returns (address[3] memory impls) { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + function _beaconImplementations() internal view returns (address[4] memory impls) { + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); for (uint256 i = 0; i < beacons.length; i++) { impls[i] = IBeacon(beacons[i]).implementation(); } @@ -238,13 +238,13 @@ contract MigrateGovernanceToTimelockTest is Test { /// @param safe The chain's token-owner Safe. /// @param timelock The chain's governance timelock. /// @param implsBefore Beacon implementations captured pre-run. - function _assertPostState(address safe, address timelock, address[3] memory implsBefore) internal view { + function _assertPostState(address safe, address timelock, address[4] memory implsBefore) internal view { address authoriser = _activeChainAuthoriser(); LibTokenInvariants.assertUniformOwnership(_activeChainTokens(), timelock); LibBeaconInvariants.assertProdBeaconsOwnedBy(block.chainid, timelock); - address[3] memory implsAfter = _beaconImplementations(); + address[4] memory implsAfter = _beaconImplementations(); for (uint256 i = 0; i < implsAfter.length; i++) { assertEq( implsAfter[i], @@ -350,7 +350,7 @@ contract MigrateGovernanceToTimelockTest is Test { function testRunRejectsUnknownBeaconOwner() external { selectBaseFork(); address timelock = deployTimelock(); - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); address stranger = address(0xDEAD); vm.mockCall(beacons[1], abi.encodeWithSignature("owner()"), abi.encode(stranger)); diff --git a/test/script/20260825-upgrade-fleet-to-0-1-30.prod.t.sol b/test/script/20260825-upgrade-fleet-to-0-1-30.prod.t.sol index f7df03dd..4bb3163a 100644 --- a/test/script/20260825-upgrade-fleet-to-0-1-30.prod.t.sol +++ b/test/script/20260825-upgrade-fleet-to-0-1-30.prod.t.sol @@ -45,7 +45,7 @@ contract UpgradeFleetProdTest is Test { /// @param label Human chain name, surfaced in logs and messages. function assertFleetRollout(string memory label) internal { UpgradeFleetTo0_1_30 script = new UpgradeFleetTo0_1_30(); - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); bool targetsLive = LibProdDeployV4.STOX_RECEIPT_0_1_30.code.length != 0 && LibProdDeployV4.STOX_RECEIPT_VAULT_0_1_30.code.length != 0 diff --git a/test/script/20260825-upgrade-fleet-to-0-1-30.t.sol b/test/script/20260825-upgrade-fleet-to-0-1-30.t.sol index 2296f9df..778f7787 100644 --- a/test/script/20260825-upgrade-fleet-to-0-1-30.t.sol +++ b/test/script/20260825-upgrade-fleet-to-0-1-30.t.sol @@ -24,7 +24,7 @@ import {SafeTx} from "../../src/lib/LibSafeOps.sol"; /// The live-fork walk is in `20260825-upgrade-fleet-to-0-1-30.prod.t.sol`. contract UpgradeFleetTest is Test { UpgradeFleetHarness internal harness; - address[3] internal beacons; + address[4] internal beacons; function setUp() external { vm.chainId(LibSafeInvariants.BASE_CHAIN_ID); diff --git a/test/script/20260831-enable-orchestrator-roles.prod.t.sol b/test/script/20260831-enable-orchestrator-roles.prod.t.sol index e92a9fc7..4771b059 100644 --- a/test/script/20260831-enable-orchestrator-roles.prod.t.sol +++ b/test/script/20260831-enable-orchestrator-roles.prod.t.sol @@ -68,7 +68,7 @@ contract EnableOrchestratorRolesProdTest is Test { return; } - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); bool fleetUpgraded = IBeacon(beacons[0]).implementation() == LibProdDeployV4.STOX_RECEIPT_0_1_30 && IBeacon(beacons[1]).implementation() == LibProdDeployV4.STOX_RECEIPT_VAULT_0_1_30; if (!fleetUpgraded) { diff --git a/test/script/20260831-enable-orchestrator-roles.t.sol b/test/script/20260831-enable-orchestrator-roles.t.sol index 751ecb11..99d6652a 100644 --- a/test/script/20260831-enable-orchestrator-roles.t.sol +++ b/test/script/20260831-enable-orchestrator-roles.t.sol @@ -40,7 +40,7 @@ contract EnableOrchestratorRolesTest is Test { /// @notice Mock Base's three in-use production beacons pointing at the /// 0.1.30 receipt/vault impls (the post-fleet-upgrade state). function mockUpgradedFleet() internal { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); vm.etch(beacons[0], hex"fe"); vm.etch(beacons[1], hex"fe"); vm.mockCall( @@ -90,7 +90,7 @@ contract EnableOrchestratorRolesTest is Test { /// The fleet-upgrade interlock refuses beacons still on pre-0.1.30 /// impls, naming the beacon and both impls. function testFleetGateRefusesUnupgradedBeacons() external { - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); vm.etch(beacons[0], hex"fe"); vm.mockCall( beacons[0], abi.encodeCall(IBeacon.implementation, ()), abi.encode(LibProdDeployV4.STOX_RECEIPT_0_1_1) diff --git a/test/script/UpgradeFleetHarness.sol b/test/script/UpgradeFleetHarness.sol index b22f7df0..3e601608 100644 --- a/test/script/UpgradeFleetHarness.sol +++ b/test/script/UpgradeFleetHarness.sol @@ -14,7 +14,7 @@ contract UpgradeFleetHarness is UpgradeFleetTo0_1_30 { } /// @notice The script's `authorBundle()`, externally callable. - function callAuthorBundle(address[3] memory beacons) external view returns (SafeTx[] memory) { + function callAuthorBundle(address[4] memory beacons) external view returns (SafeTx[] memory) { return authorBundle(beacons); } } diff --git a/test/src/concrete/deploy/EthereumBeaconOwnership.t.sol b/test/src/concrete/deploy/EthereumBeaconOwnership.t.sol index c7d0e352..7da3f55e 100644 --- a/test/src/concrete/deploy/EthereumBeaconOwnership.t.sol +++ b/test/src/concrete/deploy/EthereumBeaconOwnership.t.sol @@ -35,8 +35,8 @@ contract EthereumBeaconOwnershipTest is Test { } vm.createSelectFork(LibStoxDeployNetworks.ETHEREUM); - address[3] memory beacons = LibProdBeacons0_1_1.beacons(); - address[3] memory impls = LibProdBeacons0_1_1.implementations(); + address[4] memory beacons = LibProdBeacons0_1_1.beacons(); + address[4] memory impls = LibProdBeacons0_1_1.implementations(); // The wrapped-token-vault beacon (index 2) still serves its 0.1.1 // impl; the receipt + receipt-vault beacons ride the fleet-upgrade // migration window (20260825-upgrade-fleet-to-0-1-30): 0.1.1 OR diff --git a/test/src/concrete/deploy/GovernanceTimelockMigration.t.sol b/test/src/concrete/deploy/GovernanceTimelockMigration.t.sol index 150d0782..f271ca96 100644 --- a/test/src/concrete/deploy/GovernanceTimelockMigration.t.sol +++ b/test/src/concrete/deploy/GovernanceTimelockMigration.t.sol @@ -285,7 +285,7 @@ contract GovernanceTimelockMigrationTest is Test { vm.createSelectFork(LibRainDeploy.BASE); address safe = LibSafeInvariants.STOX_TOKEN_OWNER_SAFE; address stranger = address(0xBAD); - address[3] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); + address[4] memory beacons = LibBeaconInvariants.prodBeaconsForChainId(block.chainid); vm.mockCall(beacons[0], abi.encodeWithSignature("owner()"), abi.encode(stranger)); LibBeaconInvariantsHarness harness = new LibBeaconInvariantsHarness(); diff --git a/test/src/concrete/deploy/HyperEvmBeaconOwnership.t.sol b/test/src/concrete/deploy/HyperEvmBeaconOwnership.t.sol index 613c49fc..c2639ece 100644 --- a/test/src/concrete/deploy/HyperEvmBeaconOwnership.t.sol +++ b/test/src/concrete/deploy/HyperEvmBeaconOwnership.t.sol @@ -32,8 +32,8 @@ contract HyperEvmBeaconOwnershipTest is Test { address safe = LibSafeInvariants.STOX_TOKEN_OWNER_SAFE_HYPEREVM; vm.createSelectFork(LibStoxDeployNetworks.HYPEREVM); - address[3] memory beacons = LibProdBeacons0_1_1.beacons(); - address[3] memory impls = LibProdBeacons0_1_1.implementations(); + address[4] memory beacons = LibProdBeacons0_1_1.beacons(); + address[4] memory impls = LibProdBeacons0_1_1.implementations(); // The wrapped-token-vault beacon (index 2) still serves its 0.1.1 // impl; the receipt + receipt-vault beacons ride the fleet-upgrade // migration window (20260825-upgrade-fleet-to-0-1-30): 0.1.1 OR diff --git a/test/src/lib/LibBeaconInvariants.t.sol b/test/src/lib/LibBeaconInvariants.t.sol index 95a05ed4..cc565de1 100644 --- a/test/src/lib/LibBeaconInvariants.t.sol +++ b/test/src/lib/LibBeaconInvariants.t.sol @@ -133,7 +133,7 @@ contract LibBeaconInvariantsTest is Test { /// nothing runs on while leaving the live beacons unpinned. function testProdBeaconsForChainIdBaseIsTheV1Generation() external { selectBaseFork(); - address[3] memory beacons = harness.callProdBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); + address[4] memory beacons = harness.callProdBeaconsForChainId(LibSafeInvariants.BASE_CHAIN_ID); assertEq(beacons[0], LibProdDeployV1.STOX_RECEIPT_BEACON_V1, "receipt beacon"); assertEq(beacons[1], LibProdDeployV1.STOX_RECEIPT_VAULT_BEACON_V1, "receipt vault beacon"); assertEq(beacons[2], LibProdDeployV1.STOX_WRAPPED_TOKEN_VAULT_BEACON_V1, "wrapped vault beacon"); @@ -146,8 +146,8 @@ contract LibBeaconInvariantsTest is Test { function testProdBeaconsForChainIdEthereumIsThe011Set() external { vm.createSelectFork(LibStoxDeployNetworks.ETHEREUM); harness = new LibBeaconInvariantsHarness(); - address[3] memory beacons = harness.callProdBeaconsForChainId(LibSafeInvariants.ETHEREUM_CHAIN_ID); - address[3] memory expected = LibProdBeacons0_1_1.beacons(); + address[4] memory beacons = harness.callProdBeaconsForChainId(LibSafeInvariants.ETHEREUM_CHAIN_ID); + address[4] memory expected = LibProdBeacons0_1_1.beacons(); assertEq(beacons[0], expected[0], "receipt beacon"); assertEq(beacons[1], expected[1], "receipt vault beacon"); assertEq(beacons[2], expected[2], "wrapped vault beacon"); diff --git a/test/src/lib/LibBeaconInvariantsHarness.sol b/test/src/lib/LibBeaconInvariantsHarness.sol index 77550b33..83984b7b 100644 --- a/test/src/lib/LibBeaconInvariantsHarness.sol +++ b/test/src/lib/LibBeaconInvariantsHarness.sol @@ -14,7 +14,7 @@ contract LibBeaconInvariantsHarness { LibBeaconInvariants.assertBeaconInvariants(beacon, expectedOwner, expectedImpl); } - function callProdBeaconsForChainId(uint256 chainId) external view returns (address[3] memory) { + function callProdBeaconsForChainId(uint256 chainId) external view returns (address[4] memory) { return LibBeaconInvariants.prodBeaconsForChainId(chainId); } diff --git a/test/src/lib/LibProdBeaconsBase.t.sol b/test/src/lib/LibProdBeaconsBase.t.sol index b8c597d7..604c57e1 100644 --- a/test/src/lib/LibProdBeaconsBase.t.sol +++ b/test/src/lib/LibProdBeaconsBase.t.sol @@ -34,7 +34,7 @@ contract LibProdBeaconsBaseTest is Test { /// satisfies them just as well as the right one. function testProductionTokensRunOnTheseBeacons() external { vm.createSelectFork(LibRainDeploy.BASE); - address[3] memory beacons = LibProdBeaconsBase.beacons(); + address[4] memory beacons = LibProdBeaconsBase.beacons(); TokenInstance[] memory tokens = LibTokenInvariants.productionTokensBase(); assertTrue(tokens.length > 0, "no production tokens to check"); for (uint256 i = 0; i < tokens.length; i++) { @@ -57,7 +57,7 @@ contract LibProdBeaconsBaseTest is Test { /// pass every ownership check while quietly asserting nothing about the /// slot it displaced. function testTheThreeBeaconsAreDistinct() external pure { - address[3] memory beacons = LibProdBeaconsBase.beacons(); + address[4] memory beacons = LibProdBeaconsBase.beacons(); assertTrue(beacons[0] != beacons[1], "receipt == receipt vault"); assertTrue(beacons[1] != beacons[2], "receipt vault == wrapped vault"); assertTrue(beacons[0] != beacons[2], "receipt == wrapped vault");