Commit 3dff3bc7 authored by Inphi's avatar Inphi Committed by Adrian Sutton

fix(ctb): Set Anchor State (#280)

Allow the `DeputyGuardian` to set the Anchor State for brick prevention.

---------
Co-authored-by: default avatarclabby <ben@clab.by>
Co-authored-by: default avatarrefcell <abigger87@gmail.com>
parent f1a52e94
...@@ -990,6 +990,7 @@ contract Deploy is Deployer { ...@@ -990,6 +990,7 @@ contract Deploy is Deployer {
console.log("Upgrading and initializing AnchorStateRegistry proxy"); console.log("Upgrading and initializing AnchorStateRegistry proxy");
address anchorStateRegistryProxy = mustGetAddress("AnchorStateRegistryProxy"); address anchorStateRegistryProxy = mustGetAddress("AnchorStateRegistryProxy");
address anchorStateRegistry = mustGetAddress("AnchorStateRegistry"); address anchorStateRegistry = mustGetAddress("AnchorStateRegistry");
SuperchainConfig superchainConfig = SuperchainConfig(mustGetAddress("SuperchainConfigProxy"));
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](5); AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](5);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({ roots[0] = AnchorStateRegistry.StartingAnchorRoot({
...@@ -1031,7 +1032,7 @@ contract Deploy is Deployer { ...@@ -1031,7 +1032,7 @@ contract Deploy is Deployer {
_upgradeAndCallViaSafe({ _upgradeAndCallViaSafe({
_proxy: payable(anchorStateRegistryProxy), _proxy: payable(anchorStateRegistryProxy),
_implementation: anchorStateRegistry, _implementation: anchorStateRegistry,
_innerCallData: abi.encodeCall(AnchorStateRegistry.initialize, (roots)) _innerCallData: abi.encodeCall(AnchorStateRegistry.initialize, (roots, superchainConfig))
}); });
string memory version = AnchorStateRegistry(payable(anchorStateRegistryProxy)).version(); string memory version = AnchorStateRegistry(payable(anchorStateRegistryProxy)).version();
......
...@@ -92,6 +92,8 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -92,6 +92,8 @@ contract FPACOPS is Deploy, StdAssertions {
function initializeAnchorStateRegistryProxy() internal broadcast { function initializeAnchorStateRegistryProxy() internal broadcast {
console.log("Initializing AnchorStateRegistryProxy with AnchorStateRegistry."); console.log("Initializing AnchorStateRegistryProxy with AnchorStateRegistry.");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
SuperchainConfig superchainConfig = SuperchainConfig(superchainConfigProxy);
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](2); AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](2);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({ roots[0] = AnchorStateRegistry.StartingAnchorRoot({
...@@ -111,7 +113,8 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -111,7 +113,8 @@ contract FPACOPS is Deploy, StdAssertions {
address asrProxy = mustGetAddress("AnchorStateRegistryProxy"); address asrProxy = mustGetAddress("AnchorStateRegistryProxy");
Proxy(payable(asrProxy)).upgradeToAndCall( Proxy(payable(asrProxy)).upgradeToAndCall(
mustGetAddress("AnchorStateRegistry"), abi.encodeCall(AnchorStateRegistry.initialize, (roots)) mustGetAddress("AnchorStateRegistry"),
abi.encodeCall(AnchorStateRegistry.initialize, (roots, superchainConfig))
); );
} }
......
...@@ -170,8 +170,10 @@ contract FPACOPS2 is Deploy, StdAssertions { ...@@ -170,8 +170,10 @@ contract FPACOPS2 is Deploy, StdAssertions {
}); });
address asrProxy = mustGetAddress("AnchorStateRegistryProxy"); address asrProxy = mustGetAddress("AnchorStateRegistryProxy");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
Proxy(payable(asrProxy)).upgradeToAndCall( Proxy(payable(asrProxy)).upgradeToAndCall(
mustGetAddress("AnchorStateRegistry"), abi.encodeCall(AnchorStateRegistry.initialize, (roots)) mustGetAddress("AnchorStateRegistry"),
abi.encodeCall(AnchorStateRegistry.initialize, (roots, SuperchainConfig(superchainConfigProxy)))
); );
} }
......
...@@ -128,8 +128,8 @@ ...@@ -128,8 +128,8 @@
"sourceCodeHash": "0xbe200a6cb297a3ca1a7d174a9c886e3f17eb8edf617ad014a2ac4f6c2e2ac7f1" "sourceCodeHash": "0xbe200a6cb297a3ca1a7d174a9c886e3f17eb8edf617ad014a2ac4f6c2e2ac7f1"
}, },
"src/Safe/DeputyGuardianModule.sol": { "src/Safe/DeputyGuardianModule.sol": {
"initCodeHash": "0x433eb7488e613a51c7ff05a76bbecf47f5beac8b8614f5c50001f99e39ae7ed2", "initCodeHash": "0x23ecfc6ff42575058b51b3bde8449f62ee929fd5881f707a904fa3eb8f628053",
"sourceCodeHash": "0x5b415dc432a83fb1d5c41585539245997c62acb6bd77c489bf57e9b59be5f983" "sourceCodeHash": "0x904ff08bf288dc257501dc0a60539791c7a2dc5fba508a3523521c39009e7d5e"
}, },
"src/Safe/LivenessGuard.sol": { "src/Safe/LivenessGuard.sol": {
"initCodeHash": "0xf54289de5cef7ba0044e0d63310937fa231d6528aac91e13e531c845af42afac", "initCodeHash": "0xf54289de5cef7ba0044e0d63310937fa231d6528aac91e13e531c845af42afac",
...@@ -152,8 +152,8 @@ ...@@ -152,8 +152,8 @@
"sourceCodeHash": "0xb1f0d6f26c2e6a2c3b635eaf8f327e91a8d22ef7479b1ebb93427b88f73ed163" "sourceCodeHash": "0xb1f0d6f26c2e6a2c3b635eaf8f327e91a8d22ef7479b1ebb93427b88f73ed163"
}, },
"src/dispute/AnchorStateRegistry.sol": { "src/dispute/AnchorStateRegistry.sol": {
"initCodeHash": "0x0305c21e50829b9e07d43358d8c2c82f1449534c90d4391400d46e76d0503a49", "initCodeHash": "0x92aa16ad6c88b8c2842fea07ec69afe45e7d0dcb49cdfad7ce4693e03c3bbc47",
"sourceCodeHash": "0x56b069b33d080c2a45ee6fd340e5c5824ab4dc866eadb5b481b9026ebb12aa7c" "sourceCodeHash": "0x0383f33a1cbf41ceb010a1bf79ddd14880f55c13512e4704f82d5587fff16826"
}, },
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0x7a7cb8f2c95df2f9afb3ce9eaefe4a6f997ccce7ed8ffda5d425a65a2474a792", "initCodeHash": "0x7a7cb8f2c95df2f9afb3ce9eaefe4a6f997ccce7ed8ffda5d425a65a2474a792",
......
...@@ -77,6 +77,11 @@ ...@@ -77,6 +77,11 @@
"internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]", "internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]",
"name": "_startingAnchorRoots", "name": "_startingAnchorRoots",
"type": "tuple[]" "type": "tuple[]"
},
{
"internalType": "contract SuperchainConfig",
"name": "_superchainConfig",
"type": "address"
} }
], ],
"name": "initialize", "name": "initialize",
...@@ -84,6 +89,32 @@ ...@@ -84,6 +89,32 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract IFaultDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "setAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "superchainConfig",
"outputs": [
{
"internalType": "contract SuperchainConfig",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "tryUpdateAnchorState", "name": "tryUpdateAnchorState",
...@@ -116,5 +147,20 @@ ...@@ -116,5 +147,20 @@
], ],
"name": "Initialized", "name": "Initialized",
"type": "event" "type": "event"
},
{
"inputs": [],
"name": "InvalidGameStatus",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"inputs": [],
"name": "UnregisteredGame",
"type": "error"
} }
] ]
\ No newline at end of file
...@@ -71,6 +71,24 @@ ...@@ -71,6 +71,24 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract AnchorStateRegistry",
"name": "_registry",
"type": "address"
},
{
"internalType": "contract IFaultDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "setAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
......
...@@ -19,5 +19,12 @@ ...@@ -19,5 +19,12 @@
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(GameType => struct OutputRoot)" "type": "mapping(GameType => struct OutputRoot)"
},
{
"bytes": "20",
"label": "superchainConfig",
"offset": 0,
"slot": "2",
"type": "contract SuperchainConfig"
} }
] ]
\ No newline at end of file
...@@ -4,11 +4,13 @@ pragma solidity 0.8.15; ...@@ -4,11 +4,13 @@ pragma solidity 0.8.15;
import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import { Enum } from "safe-contracts/common/Enum.sol"; import { Enum } from "safe-contracts/common/Enum.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { ISemver } from "src/universal/ISemver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { Unauthorized } from "src/libraries/PortalErrors.sol"; import { Unauthorized } from "src/libraries/PortalErrors.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -43,8 +45,8 @@ contract DeputyGuardianModule is ISemver { ...@@ -43,8 +45,8 @@ contract DeputyGuardianModule is ISemver {
address internal immutable DEPUTY_GUARDIAN; address internal immutable DEPUTY_GUARDIAN;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.1.0 /// @custom:semver 2.0.0-beta.1
string public constant version = "1.1.0"; string public constant version = "2.0.0-beta.1";
// Constructor to initialize the Safe and baseModule instances // Constructor to initialize the Safe and baseModule instances
constructor(Safe _safe, SuperchainConfig _superchainConfig, address _deputyGuardian) { constructor(Safe _safe, SuperchainConfig _superchainConfig, address _deputyGuardian) {
...@@ -108,6 +110,22 @@ contract DeputyGuardianModule is ISemver { ...@@ -108,6 +110,22 @@ contract DeputyGuardianModule is ISemver {
emit Unpaused(); emit Unpaused();
} }
/// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments
/// necessary to call `setAnchorState()` on the `AnchorStateRegistry` contract.
/// Only the deputy guardian can call this function.
/// @param _registry The `AnchorStateRegistry` contract instance.
/// @param _game The `IFaultDisputeGame` contract instance.
function setAnchorState(AnchorStateRegistry _registry, IFaultDisputeGame _game) external {
_onlyDeputyGuardian();
bytes memory data = abi.encodeCall(AnchorStateRegistry.setAnchorState, (_game));
(bool success, bytes memory returnData) =
SAFE.execTransactionFromModuleReturnData(address(_registry), 0, data, Enum.Operation.Call);
if (!success) {
revert ExecutionFailed(string(returnData));
}
}
/// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments /// @notice Calls the Security Council Safe's `execTransactionFromModuleReturnData()`, with the arguments
/// necessary to call `blacklistDisputeGame()` on the `OptimismPortal2` contract. /// necessary to call `blacklistDisputeGame()` on the `OptimismPortal2` contract.
/// Only the deputy guardian can call this function. /// Only the deputy guardian can call this function.
......
...@@ -8,8 +8,11 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr ...@@ -8,8 +8,11 @@ import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistr
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";
import { UnregisteredGame, InvalidGameStatus } from "src/dispute/lib/Errors.sol";
/// @title AnchorStateRegistry /// @title AnchorStateRegistry
/// @notice The AnchorStateRegistry is a contract that stores the latest "anchor" state for each available /// @notice The AnchorStateRegistry is a contract that stores the latest "anchor" state for each available
...@@ -24,8 +27,8 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -24,8 +27,8 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
} }
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0 /// @custom:semver 1.1.0-beta.1
string public constant version = "1.0.0"; string public constant version = "2.0.0-beta.1";
/// @notice DisputeGameFactory address. /// @notice DisputeGameFactory address.
IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY; IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY;
...@@ -33,21 +36,30 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -33,21 +36,30 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
/// @inheritdoc IAnchorStateRegistry /// @inheritdoc IAnchorStateRegistry
mapping(GameType => OutputRoot) public anchors; mapping(GameType => OutputRoot) public anchors;
/// @notice Address of the SuperchainConfig contract.
SuperchainConfig public superchainConfig;
/// @param _disputeGameFactory DisputeGameFactory address. /// @param _disputeGameFactory DisputeGameFactory address.
constructor(IDisputeGameFactory _disputeGameFactory) { constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory; DISPUTE_GAME_FACTORY = _disputeGameFactory;
_disableInitializers();
// Initialize the implementation with an empty array of starting anchor roots.
initialize(new StartingAnchorRoot[](0));
} }
/// @notice Initializes the contract. /// @notice Initializes the contract.
/// @param _startingAnchorRoots An array of starting anchor roots. /// @param _startingAnchorRoots An array of starting anchor roots.
function initialize(StartingAnchorRoot[] memory _startingAnchorRoots) public initializer { /// @param _superchainConfig The address of the SuperchainConfig contract.
function initialize(
StartingAnchorRoot[] memory _startingAnchorRoots,
SuperchainConfig _superchainConfig
)
public
initializer
{
for (uint256 i = 0; i < _startingAnchorRoots.length; i++) { for (uint256 i = 0; i < _startingAnchorRoots.length; i++) {
StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i]; StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i];
anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot; anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot;
} }
superchainConfig = _superchainConfig;
} }
/// @inheritdoc IAnchorStateRegistry /// @inheritdoc IAnchorStateRegistry
...@@ -67,10 +79,7 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -67,10 +79,7 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData }); DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game. // Must be a valid game.
require( if (address(factoryRegisteredGame) != address(game)) revert UnregisteredGame();
address(factoryRegisteredGame) == address(game),
"AnchorStateRegistry: fault dispute game not registered with factory"
);
// No need to update anything if the anchor state is already newer. // No need to update anything if the anchor state is already newer.
if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) { if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) {
...@@ -85,4 +94,27 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver { ...@@ -85,4 +94,27 @@ contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
// Actually update the anchor state. // Actually update the anchor state.
anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) }); anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) });
} }
/// @inheritdoc IAnchorStateRegistry
function setAnchorState(IFaultDisputeGame _game) external {
if (msg.sender != superchainConfig.guardian()) revert Unauthorized();
// Get the metadata of the game.
(GameType gameType, Claim rootClaim, bytes memory extraData) = _game.gameData();
// Grab the verified address of the game based on the game data.
// slither-disable-next-line unused-return
(IDisputeGame factoryRegisteredGame,) =
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game.
if (address(factoryRegisteredGame) != address(_game)) revert UnregisteredGame();
// The game must have resolved in favor of the root claim.
if (_game.status() != GameStatus.DEFENDER_WINS) revert InvalidGameStatus();
// Update the anchor.
anchors[gameType] =
OutputRoot({ l2BlockNumber: _game.l2BlockNumber(), root: Hash.wrap(_game.rootClaim().raw()) });
}
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -21,4 +22,8 @@ interface IAnchorStateRegistry { ...@@ -21,4 +22,8 @@ interface IAnchorStateRegistry {
/// the FaultDisputeGame contract and stores it in the registry if the new anchor state is valid and the /// the FaultDisputeGame contract and stores it in the registry if the new anchor state is valid and the
/// state is newer than the current anchor state. /// state is newer than the current anchor state.
function tryUpdateAnchorState() external; function tryUpdateAnchorState() external;
/// @notice Sets the anchor state given the game.
/// @param _game The game to set the anchor state for.
function setAnchorState(IFaultDisputeGame _game) external;
} }
...@@ -126,3 +126,13 @@ error L2BlockNumberChallenged(); ...@@ -126,3 +126,13 @@ error L2BlockNumberChallenged();
/// @notice Thrown when an unauthorized address attempts to interact with the game. /// @notice Thrown when an unauthorized address attempts to interact with the game.
error BadAuth(); error BadAuth();
////////////////////////////////////////////////////////////////
// `AnchorStateRegistry` Errors //
////////////////////////////////////////////////////////////////
/// @notice Thrown when attempting to set an anchor state using an unregistered game.
error UnregisteredGame();
/// @notice Thrown when attempting to set an anchor state using an invalid game result.
error InvalidGameStatus();
...@@ -7,6 +7,8 @@ import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; ...@@ -7,6 +7,8 @@ import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import "test/safe-tools/SafeTestTools.sol"; import "test/safe-tools/SafeTestTools.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { DeputyGuardianModule } from "src/Safe/DeputyGuardianModule.sol"; import { DeputyGuardianModule } from "src/Safe/DeputyGuardianModule.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -148,6 +150,43 @@ contract DeputyGuardianModule_Unpause_TestFail is DeputyGuardianModule_Unpause_T ...@@ -148,6 +150,43 @@ contract DeputyGuardianModule_Unpause_TestFail is DeputyGuardianModule_Unpause_T
} }
} }
contract DeputyGuardianModule_SetAnchorState_TestFail is DeputyGuardianModule_TestInit {
function test_setAnchorState_notDeputyGuardian_reverts() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector));
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
function test_setAnchorState_targetReverts_reverts() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.mockCallRevert(
address(asr),
abi.encodeWithSelector(asr.setAnchorState.selector),
"AnchorStateRegistry: setAnchorState reverted"
);
vm.prank(address(deputyGuardian));
vm.expectRevert(
abi.encodeWithSelector(ExecutionFailed.selector, "AnchorStateRegistry: setAnchorState reverted")
);
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
}
contract DeputyGuardianModule_SetAnchorState_Test is DeputyGuardianModule_TestInit {
function test_setAnchorState_succeeds() external {
AnchorStateRegistry asr = AnchorStateRegistry(makeAddr("asr"));
vm.mockCall(
address(asr),
abi.encodeWithSelector(AnchorStateRegistry.setAnchorState.selector, IFaultDisputeGame(address(0))),
""
);
vm.expectEmit(address(safeInstance.safe));
emit ExecutionFromModuleSuccess(address(deputyGuardianModule));
vm.prank(address(deputyGuardian));
deputyGuardianModule.setAnchorState(asr, IFaultDisputeGame(address(0)));
}
}
contract DeputyGuardianModule_BlacklistDisputeGame_Test is DeputyGuardianModule_TestInit { contract DeputyGuardianModule_BlacklistDisputeGame_Test is DeputyGuardianModule_TestInit {
/// @dev Tests that `blacklistDisputeGame` successfully blacklists a dispute game when called by the deputy /// @dev Tests that `blacklistDisputeGame` successfully blacklists a dispute game when called by the deputy
/// guardian. /// guardian.
......
...@@ -617,9 +617,11 @@ contract Specification_Test is CommonTest { ...@@ -617,9 +617,11 @@ contract Specification_Test is CommonTest {
// AnchorStateRegistry // AnchorStateRegistry
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("anchors(uint32)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("disputeGameFactory()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize((uint32,(bytes32,uint256))[])") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("initialize((uint32,(bytes32,uint256))[],address)") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("tryUpdateAnchorState()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("setAnchorState(address)"), _auth: Role.GUARDIAN });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") }); _addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("version()") });
_addSpec({ _name: "AnchorStateRegistry", _sel: _getSel("superchainConfig()") });
// PermissionedDisputeGame // PermissionedDisputeGame
_addSpec({ _name: "PermissionedDisputeGame", _sel: _getSel("absolutePrestate()") }); _addSpec({ _name: "PermissionedDisputeGame", _sel: _getSel("absolutePrestate()") });
...@@ -826,6 +828,11 @@ contract Specification_Test is CommonTest { ...@@ -826,6 +828,11 @@ contract Specification_Test is CommonTest {
_sel: _getSel("setRespectedGameType(address,uint32)"), _sel: _getSel("setRespectedGameType(address,uint32)"),
_auth: Role.DEPUTYGUARDIAN _auth: Role.DEPUTYGUARDIAN
}); });
_addSpec({
_name: "DeputyGuardianModule",
_sel: _getSel("setAnchorState(address,address)"),
_auth: Role.DEPUTYGUARDIAN
});
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("pause()"), _auth: Role.DEPUTYGUARDIAN }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("pause()"), _auth: Role.DEPUTYGUARDIAN });
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("unpause()"), _auth: Role.DEPUTYGUARDIAN }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("unpause()"), _auth: Role.DEPUTYGUARDIAN });
_addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("deputyGuardian()") }); _addSpec({ _name: "DeputyGuardianModule", _sel: _getSel("deputyGuardian()") });
...@@ -940,11 +947,12 @@ contract Specification_Test is CommonTest { ...@@ -940,11 +947,12 @@ contract Specification_Test is CommonTest {
/// @notice Ensures that the DeputyGuardian is authorized to take all Guardian actions. /// @notice Ensures that the DeputyGuardian is authorized to take all Guardian actions.
function testDeputyGuardianAuth() public view { function testDeputyGuardianAuth() public view {
assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, specsByRole[Role.GUARDIAN].length); assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, specsByRole[Role.GUARDIAN].length);
assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, 4); assertEq(specsByRole[Role.DEPUTYGUARDIAN].length, 5);
mapping(bytes4 => Spec) storage dgmFuncSpecs = specs["DeputyGuardianModule"]; mapping(bytes4 => Spec) storage dgmFuncSpecs = specs["DeputyGuardianModule"];
mapping(bytes4 => Spec) storage superchainConfigFuncSpecs = specs["SuperchainConfig"]; mapping(bytes4 => Spec) storage superchainConfigFuncSpecs = specs["SuperchainConfig"];
mapping(bytes4 => Spec) storage portal2FuncSpecs = specs["OptimismPortal2"]; mapping(bytes4 => Spec) storage portal2FuncSpecs = specs["OptimismPortal2"];
mapping(bytes4 => Spec) storage anchorRegFuncSpecs = specs["AnchorStateRegistry"];
// Ensure that for each of the DeputyGuardianModule's methods there is a corresponding method on another // Ensure that for each of the DeputyGuardianModule's methods there is a corresponding method on another
// system contract authed to the Guardian role. // system contract authed to the Guardian role.
...@@ -959,5 +967,8 @@ contract Specification_Test is CommonTest { ...@@ -959,5 +967,8 @@ contract Specification_Test is CommonTest {
_assertRolesEq(dgmFuncSpecs[_getSel("setRespectedGameType(address,uint32)")].auth, Role.DEPUTYGUARDIAN); _assertRolesEq(dgmFuncSpecs[_getSel("setRespectedGameType(address,uint32)")].auth, Role.DEPUTYGUARDIAN);
_assertRolesEq(portal2FuncSpecs[_getSel("setRespectedGameType(uint32)")].auth, Role.GUARDIAN); _assertRolesEq(portal2FuncSpecs[_getSel("setRespectedGameType(uint32)")].auth, Role.GUARDIAN);
_assertRolesEq(dgmFuncSpecs[_getSel("setAnchorState(address,address)")].auth, Role.DEPUTYGUARDIAN);
_assertRolesEq(anchorRegFuncSpecs[_getSel("setAnchorState(address)")].auth, Role.GUARDIAN);
} }
} }
...@@ -5,7 +5,7 @@ import "src/dispute/lib/Types.sol"; ...@@ -5,7 +5,7 @@ import "src/dispute/lib/Types.sol";
import "src/dispute/lib/Errors.sol"; import "src/dispute/lib/Errors.sol";
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol"; import { FaultDisputeGame_Init, IDisputeGame, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol";
contract AnchorStateRegistry_Init is FaultDisputeGame_Init { contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
function setUp() public virtual override { function setUp() public virtual override {
...@@ -124,7 +124,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -124,7 +124,7 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
// Try to update the anchor state. // Try to update the anchor state.
vm.prank(address(gameProxy)); vm.prank(address(gameProxy));
vm.expectRevert("AnchorStateRegistry: fault dispute game not registered with factory"); vm.expectRevert(UnregisteredGame.selector);
anchorStateRegistry.tryUpdateAnchorState(); anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated. // Confirm that the anchor state has not updated.
...@@ -132,4 +132,88 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In ...@@ -132,4 +132,88 @@ contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_In
assertEq(updatedL2BlockNumber, l2BlockNumber); assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw()); assertEq(updatedRoot.raw(), root.raw());
} }
function test_setAnchorState_invalidGame_fails() public {
// Confirm that the anchor state is older than the game state.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
require(l2BlockNumber < gameProxy.l2BlockNumber(), "l2BlockNumber < gameProxy.l2BlockNumber()");
// Mock the state that we want.
vm.mockCall(
address(disputeGameFactory),
abi.encodeWithSelector(
disputeGameFactory.games.selector, gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()
),
abi.encode(address(0), 0)
);
// Try to update the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(UnregisteredGame.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state fails if the challenger wins.
function test_setAnchorState_challengerWins_fails() public {
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the state that we want.
vm.mockCall(
address(gameProxy),
abi.encodeWithSelector(gameProxy.status.selector),
abi.encode(GameStatus.CHALLENGER_WINS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state fails if the game is in progress.
function test_setAnchorState_gameInProgress_fails() public {
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
// Mock the state that we want.
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.IN_PROGRESS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
vm.expectRevert(InvalidGameStatus.selector);
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that setting the anchor state succeeds.
function test_setAnchorState_succeeds() public {
// Mock the state that we want.
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS)
);
// Set the anchor state.
vm.prank(superchainConfig.guardian());
anchorStateRegistry.setAnchorState(gameProxy);
// Confirm that the anchor state has updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, gameProxy.l2BlockNumber());
assertEq(updatedRoot.raw(), gameProxy.rootClaim().raw());
}
} }
...@@ -13,7 +13,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -13,7 +13,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
Vm private constant vm = Vm(VM_ADDRESS); Vm private constant vm = Vm(VM_ADDRESS);
address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4; address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4;
address internal constant anchorStateRegistryAddress = 0xE2a80256d1dAFe06683F231F8e9561639Aa0e9b9; address internal constant anchorStateRegistryAddress = 0x18AdFeBA1BcE3eCa45E563944759E2d6eCaF7BA2;
address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e; address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e;
address internal constant delayedWETHAddress = 0x90b505357aFad15A1fb8F1098B3295b7cfac1c24; address internal constant delayedWETHAddress = 0x90b505357aFad15A1fb8F1098B3295b7cfac1c24;
address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92; address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92;
...@@ -424,13 +424,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -424,13 +424,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
vm.etch(mipsAddress, mipsCode); vm.etch(mipsAddress, mipsCode);
vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode); vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"00000000000000000000000000000000000000000000000000000000000000ff";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryAddress, slot, value); vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005"; slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000003"; value = hex"0000000000000000000000000000000000000000000000000000000000000003";
...@@ -751,7 +745,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -751,7 +745,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000010"; value = hex"0000000000000000000000000000000000000000000000000000000000000010";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000e2a80256d1dafe06683f231f8e9561639aa0e9b9"; value = hex"00000000000000000000000018adfeba1bce3eca45e563944759e2d6ecaf7ba2";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -774,6 +768,9 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -774,6 +768,9 @@ contract DeploymentSummary is DeploymentSummaryCode {
slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d"; slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d";
value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000002";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
......
...@@ -13,7 +13,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -13,7 +13,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
Vm private constant vm = Vm(VM_ADDRESS); Vm private constant vm = Vm(VM_ADDRESS);
address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4; address internal constant addressManagerAddress = 0x50EEf481cae4250d252Ae577A09bF514f224C6C4;
address internal constant anchorStateRegistryAddress = 0xE2a80256d1dAFe06683F231F8e9561639Aa0e9b9; address internal constant anchorStateRegistryAddress = 0x18AdFeBA1BcE3eCa45E563944759E2d6eCaF7BA2;
address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e; address internal constant anchorStateRegistryProxyAddress = 0x970670459734a83899773A0fd45941B5afC1200e;
address internal constant delayedWETHAddress = 0x90b505357aFad15A1fb8F1098B3295b7cfac1c24; address internal constant delayedWETHAddress = 0x90b505357aFad15A1fb8F1098B3295b7cfac1c24;
address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92; address internal constant delayedWETHProxyAddress = 0xEF179756ea6525AFade217cA5aB0b1b5CfE0fd92;
...@@ -424,13 +424,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -424,13 +424,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
vm.etch(mipsAddress, mipsCode); vm.etch(mipsAddress, mipsCode);
vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode); vm.etch(anchorStateRegistryAddress, anchorStateRegistryCode);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"00000000000000000000000000000000000000000000000000000000000000ff";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000101";
vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryAddress, slot, value); vm.store(anchorStateRegistryAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000005"; slot = hex"0000000000000000000000000000000000000000000000000000000000000005";
value = hex"0000000000000000000000000000000000000000000000000000000000000003"; value = hex"0000000000000000000000000000000000000000000000000000000000000003";
...@@ -754,7 +748,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -754,7 +748,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000010"; value = hex"0000000000000000000000000000000000000000000000000000000000000010";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"000000000000000000000000e2a80256d1dafe06683f231f8e9561639aa0e9b9"; value = hex"00000000000000000000000018adfeba1bce3eca45e563944759e2d6ecaf7ba2";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -777,6 +771,9 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -777,6 +771,9 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d"; slot = hex"1d32deecea32fd1365d10df47fc6666a05871102e61a115a5c569bca7e5de14d";
value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; value = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000002";
value = hex"0000000000000000000000004f559f30f5eb88d635fde1548c4267db8fab0351";
vm.store(anchorStateRegistryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
vm.store(anchorStateRegistryProxyAddress, slot, value); vm.store(anchorStateRegistryProxyAddress, slot, value);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment