diff --git a/README.md b/README.md index bfcd70e0..226daced 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # world-id-state-bridge -![spec](https://raw.githubusercontent.com/worldcoin/world-id-state-bridge/2cba98da38cfc5173ad773824126ce4285d240b1/docs/state-bridge.svg) +![spec](docs/state-bridge2.svg) ## Description State bridge between the WorldID Ethereum mainnet deployment and WorldID supported networks. The [spec](./docs/spec.md) can be found in `docs/spec.md`. +## Deployments + +The addresses of the contract deployments for production and staging can be found in +[`docs/deployments.md`](./docs/deployments.md). + ## Supported networks Currently, the supported networks are Polygon PoS (for backwards compatibility) and Optimism. The next iteration of the diff --git a/docs/deployments.md b/docs/deployments.md new file mode 100644 index 00000000..67b83939 --- /dev/null +++ b/docs/deployments.md @@ -0,0 +1,23 @@ +# Addresses of the production and staging deployments + +## Production + +### Orb (group id: 1) + +- WorldID State Bridge (Ethereum mainnet): + [0x86d26ed31556ea7694bd0cc4e674d7526f70511a](https://etherscan.io/address/0x86d26ed31556ea7694bd0cc4e674d7526f70511a#code) +- OpWorldID (Optimism mainnet): + [0x42ff98c4e85212a5d31358acbfe76a621b50fc02](https://optimistic.etherscan.io/address/0x42ff98c4e85212a5d31358acbfe76a621b50fc02#code) +- PolygonWorldID (Polygon PoS mainnet): + [0x2Ad412A1dF96434Eed0779D2dB4A8694a06132f8](https://polygonscan.com/address/0x2Ad412A1dF96434Eed0779D2dB4A8694a06132f8#code) + +## Staging + +### Orb (group id: 1) + +- WorldID State Bridge (Ethereum Goerli): + [0xCD60dA00e20bE3d23e2E135283423FD86867C98D](https://goerli.etherscan.io/address/0xcd60da00e20be3d23e2e135283423fd86867c98d#code) +- OpWorldID (Optimism Goerli): + [0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208](https://goerli.optimistic.etherscan.io/address/0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208#code) +- PolygonWorldID (Polygon Mumbai): + [0x8d6308aC8d34088587Ef345736389Ee915e2A9dA](https://mumbai.polygonscan.com/address/0x8d6308aC8d34088587Ef345736389Ee915e2A9dA#code) diff --git a/docs/spec.md b/docs/spec.md index 86ae30cb..39c4f79b 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -1,45 +1,11 @@ # State Bridge spec -## Design iteration - -1. Push-based approach (current - v0.1.0) - -- [`registerIdentities`](https://github.com/worldcoin/world-id-contracts/blob/98f4d6ae959a5a8a3c5ad5b57086e01d999d1b83/src/WorldIDIdentityManagerImplV1.sol#L288-L355) - in - [`WorldIDIdentityManagerImplV1.sol`](https://github.com/worldcoin/world-id-contracts/src/WorldIDIdentityManagerImplV1.sol) - calls a method in the [`StateBridge.sol`](../src/StateBridge.sol) contract that sends over each new state root and its - timestamp. - -- Downsides: - - Hard to scale, redeploying semaphore, separate impls for diff L2s - -2. Pull-based approach (CRON) - -- status: archived -- idea: proxy contract you can send state roots, checks against semaphore if they are correct and proceeds to send them - to L2s. Service like Gelato periodically fetches latest state roots and checks agains semaphore before submitting to - proxy - -3. Pull-based approach (external service) - -- status: ideation, strong candidate the for next iteration of the state bridge functionality -- idea: leverage storage proofs of `WorldIDIdentityManager` contract on L1 and run an external relayer service that - would update all of the supported target chain contracts by providing a new root, the block timestamp and a storage - proof from Ethereum mainnet. This approach is easily generalizable to any target chain that supports the cryptography - needed to deploy a Semaphore verifier and a storage proof verifier. For EVM-based chain it is a trivial change as - contracts for this are already implemented and would only require a \WorldID contract deployment and - integration to the state relayer service. - -- Upsides: - - generalizeable - - proper solution (conceptually simpler, no ownability required) -- Downsides: - - non-trivial implementation for storage proofs on non-EVM chains - -## Structure - ![state-bridge.svg](state-bridge.svg) +Propagates new World ID merkle tree roots from the `WorldIDIdentityManager` contract +([`world-id-contracts`](https://github.com/worldcoin/world-id-contracts) repo) on Ethereum mainnet to Optimism and +Polygon PoS. + Currently the state bridge supports Optimism and Polygon PoS. The root and the timestamp get sent from [`StateBridge.sol`](../src/StateBridge.sol) to the [`OpWorldID.sol`](../src/OpWorldID.sol) contract on the Optimism L2 which accepts them in order to allow developers on Optimism to call `verifyProof` on [WorldID](https://id.worldcoin.org) diff --git a/docs/state-bridge2.svg b/docs/state-bridge2.svg new file mode 100644 index 00000000..4600d82c --- /dev/null +++ b/docs/state-bridge2.svg @@ -0,0 +1,17 @@ + + + + + + + + World ID State Bridgeonce registerIdentities() inserts a new batch of identity commitments into the WorldIDIdentityManagerWorldIDIdentityManager(0 - phone)(1 - orb)StateBridgeOpWorldIDPolygonWorldIDsendRootMultichain()_sendRootToOptimism()_sendRootToPolygon()receiveRoot()receiveRoot() \ No newline at end of file diff --git a/src/OpWorldID.sol b/src/OpWorldID.sol index 6466f91d..16e1029c 100644 --- a/src/OpWorldID.sol +++ b/src/OpWorldID.sol @@ -1,11 +1,9 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {WorldIDBridge} from "./abstract/WorldIDBridge.sol"; import {IOpWorldID} from "./interfaces/IOpWorldID.sol"; -import {SemaphoreTreeDepthValidator} from "./utils/SemaphoreTreeDepthValidator.sol"; -import {SemaphoreVerifier} from "semaphore/base/SemaphoreVerifier.sol"; import {CrossDomainOwnable3} from "@eth-optimism/contracts-bedrock/contracts/L2/CrossDomainOwnable3.sol"; diff --git a/src/PolygonWorldID.sol b/src/PolygonWorldID.sol index 5f194b84..6e1bd501 100644 --- a/src/PolygonWorldID.sol +++ b/src/PolygonWorldID.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {WorldIDBridge} from "./abstract/WorldIDBridge.sol"; diff --git a/src/StateBridge.sol b/src/StateBridge.sol index 73e6e950..29f97acb 100644 --- a/src/StateBridge.sol +++ b/src/StateBridge.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; // Optimism interface for cross domain messaging @@ -25,19 +25,34 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { address public immutable opWorldIDAddress; /// @notice address for Optimism's Ethereum mainnet L1CrossDomainMessenger contract - address internal immutable crossDomainMessengerAddress; + address internal immutable opCrossDomainMessengerAddress; + + /// @notice The address of the BaseWorldID contract on Base + address public immutable baseWorldIDAddress; + + /// @notice address for Base's Ethereum mainnet L1CrossDomainMessenger contract + address internal immutable baseCrossDomainMessengerAddress; /// @notice worldID Address address public immutable worldIDAddress; /// @notice Amount of gas purchased on Optimism for _sendRootToOptimism - uint32 internal opGasLimitSendRootOptimism; + uint32 internal gasLimitSendRootOptimism; /// @notice Amount of gas purchased on Optimism for setRootHistoryExpiryOptimism - uint32 internal opGasLimitSetRootHistoryExpiryOptimism; + uint32 internal gasLimitSetRootHistoryExpiryOptimism; /// @notice Amount of gas purchased on Optimism for transferOwnershipOptimism - uint32 internal opGasLimitTransferOwnershipOptimism; + uint32 internal gasLimitTransferOwnershipOptimism; + + /// @notice Amount of gas purchased on Base for _sendRootToBase + uint32 internal gasLimitSendRootBase; + + /// @notice Amount of gas purchased on Base for setRootHistoryExpiryBase + uint32 internal gasLimitSetRootHistoryExpiryBase; + + /// @notice Amount of gas purchased on Base for transferOwnershipBase + uint32 internal gasLimitTransferOwnershipBase; /////////////////////////////////////////////////////////////////// /// EVENTS /// @@ -52,6 +67,15 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { address indexed previousOwner, address indexed newOwner, bool isLocal ); + /// @notice Emmitted when the the StateBridge gives ownership of the OPWorldID contract + /// to the WorldID Identity Manager contract away + /// @param previousOwner The previous owner of the OPWorldID contract + /// @param newOwner The new owner of the OPWorldID contract + /// @param isLocal Whether the ownership transfer is local (Base EOA/contract) or an Ethereum EOA or contract + event OwnershipTransferredBase( + address indexed previousOwner, address indexed newOwner, bool isLocal + ); + /// @notice Emmitted when the the StateBridge sets the root history expiry for OpWorldID and PolygonWorldID /// @param rootHistoryExpiry The new root history expiry event SetRootHistoryExpiry(uint256 rootHistoryExpiry); @@ -61,17 +85,29 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { /// @param timestamp The Ethereum block timestamp of the latest WorldID Identity Manager root. event RootSentMultichain(uint256 root, uint128 timestamp); - /// @notice Emmitted when the the StateBridge sets the opGasLimit for sendRootOptimism + /// @notice Emmitted when the the StateBridge sets the gas limit for sendRootOptimism /// @param _opGasLimit The new opGasLimit for sendRootOptimism - event SetOpGasLimitSendRootOptimism(uint32 _opGasLimit); + event SetGasLimitSendRootOptimism(uint32 _opGasLimit); - /// @notice Emmitted when the the StateBridge sets the opGasLimit for setRootHistoryExpiryOptimism + /// @notice Emmitted when the the StateBridge sets the gas limit for setRootHistoryExpiryOptimism /// @param _opGasLimit The new opGasLimit for setRootHistoryExpiryOptimism - event SetOpGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit); + event SetGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit); - /// @notice Emmitted when the the StateBridge sets the opGasLimit for transferOwnershipOptimism + /// @notice Emmitted when the the StateBridge sets the gas limit for transferOwnershipOptimism /// @param _opGasLimit The new opGasLimit for transferOwnershipOptimism - event SetOpGasLimitTransferOwnershipOptimism(uint32 _opGasLimit); + event SetGasLimitTransferOwnershipOptimism(uint32 _opGasLimit); + + /// @notice Emmitted when the the StateBridge sets the gas limit for sendRootBase + /// @param _baseGasLimit The new baseGasLimit for sendRootBase + event SetGasLimitSendRootBase(uint32 _baseGasLimit); + + /// @notice Emmitted when the the StateBridge sets the gas limit for setRootHistoryExpiryBase + /// @param _baseGasLimit The new baseGasLimit for setRootHistoryExpiryBase + event SetGasLimitSetRootHistoryExpiryBase(uint32 _baseGasLimit); + + /// @notice Emmitted when the the StateBridge sets the gas limit for transferOwnershipBase + /// @param _baseGasLimit The new baseGasLimit for transferOwnershipBase + event SetGasLimitTransferOwnershipBase(uint32 _baseGasLimit); /////////////////////////////////////////////////////////////////// /// ERRORS /// @@ -102,20 +138,29 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { /// @param _fxRoot address of Polygon's fxRoot contract, part of the FxPortal bridge (Goerli or Mainnet) /// @param _worldIDIdentityManager Deployment address of the WorldID Identity Manager contract /// @param _opWorldIDAddress Address of the Optimism contract that will receive the new root and timestamp - /// @param _crossDomainMessenger L1CrossDomainMessenger contract used to communicate with the Optimism network + /// @param _opCrossDomainMessenger L1CrossDomainMessenger contract used to communicate with the Optimism network + /// @param _baseWorldIDAddress Address of the Base contract that will receive the new root and timestamp + /// @param _baseCrossDomainMessenger L1CrossDomainMessenger contract used to communicate with the Base OP-Stack network constructor( address _checkpointManager, address _fxRoot, address _worldIDIdentityManager, address _opWorldIDAddress, - address _crossDomainMessenger + address _opCrossDomainMessenger, + address _baseWorldIDAddress, + address _baseCrossDomainMessenger ) FxBaseRootTunnel(_checkpointManager, _fxRoot) { opWorldIDAddress = _opWorldIDAddress; worldIDAddress = _worldIDIdentityManager; - crossDomainMessengerAddress = _crossDomainMessenger; - opGasLimitSendRootOptimism = 100000; - opGasLimitSetRootHistoryExpiryOptimism = 100000; - opGasLimitTransferOwnershipOptimism = 100000; + baseWorldIDAddress = _baseWorldIDAddress; + opCrossDomainMessengerAddress = _opCrossDomainMessenger; + baseCrossDomainMessengerAddress = _baseCrossDomainMessenger; + gasLimitSendRootOptimism = 100000; + gasLimitSetRootHistoryExpiryOptimism = 100000; + gasLimitTransferOwnershipOptimism = 100000; + gasLimitSendRootBase = 100000; + gasLimitSetRootHistoryExpiryBase = 100000; + gasLimitTransferOwnershipBase = 100000; } /////////////////////////////////////////////////////////////////// @@ -129,6 +174,7 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { uint128 timestamp = uint128(block.timestamp); _sendRootToOptimism(root, timestamp); _sendRootToPolygon(root, timestamp); + _sendRootToBase(root, timestamp); // add other chains here emit RootSentMultichain(root, timestamp); @@ -139,6 +185,7 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { function setRootHistoryExpiry(uint256 expiryTime) public onlyWorldIDIdentityManager { setRootHistoryExpiryOptimism(expiryTime); setRootHistoryExpiryPolygon(expiryTime); + setRootHistoryExpiryBase(expiryTime); emit SetRootHistoryExpiry(expiryTime); } @@ -156,11 +203,11 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { // correct data to the optimism bridge. bytes memory message = abi.encodeCall(IOpWorldID.receiveRoot, (root, timestamp)); - ICrossDomainMessenger(crossDomainMessengerAddress).sendMessage( + ICrossDomainMessenger(opCrossDomainMessengerAddress).sendMessage( // Contract address on Optimism opWorldIDAddress, message, - opGasLimitSendRootOptimism + gasLimitSendRootOptimism ); } @@ -175,11 +222,11 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { // correct data to the optimism bridge. message = abi.encodeCall(ICrossDomainOwnable3.transferOwnership, (_owner, _isLocal)); - ICrossDomainMessenger(crossDomainMessengerAddress).sendMessage( + ICrossDomainMessenger(opCrossDomainMessengerAddress).sendMessage( // Contract address on Optimism opWorldIDAddress, message, - opGasLimitTransferOwnershipOptimism + gasLimitTransferOwnershipOptimism ); emit OwnershipTransferredOptimism(owner(), _owner, _isLocal); @@ -194,11 +241,11 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { // correct data to the optimism bridge. message = abi.encodeCall(IRootHistory.setRootHistoryExpiry, (_rootHistoryExpiry)); - ICrossDomainMessenger(crossDomainMessengerAddress).sendMessage( + ICrossDomainMessenger(opCrossDomainMessengerAddress).sendMessage( // Contract address on Optimism opWorldIDAddress, message, - opGasLimitSetRootHistoryExpiryOptimism + gasLimitSetRootHistoryExpiryOptimism ); } @@ -208,26 +255,113 @@ contract StateBridge is FxBaseRootTunnel, Ownable2Step { /// @notice Sets the gas limit for the Optimism sendRootMultichain method /// @param _opGasLimit The new gas limit for the sendRootMultichain method - function setOpGasLimitSendRootOptimism(uint32 _opGasLimit) external onlyOwner { - opGasLimitSendRootOptimism = _opGasLimit; + function setGasLimitSendRootOptimism(uint32 _opGasLimit) external onlyOwner { + gasLimitSendRootOptimism = _opGasLimit; - emit SetOpGasLimitSendRootOptimism(_opGasLimit); + emit SetGasLimitSendRootOptimism(_opGasLimit); } /// @notice Sets the gas limit for the Optimism setRootHistoryExpiry method /// @param _opGasLimit The new gas limit for the setRootHistoryExpiry method - function setOpGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit) external onlyOwner { - opGasLimitSetRootHistoryExpiryOptimism = _opGasLimit; + function setGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit) external onlyOwner { + gasLimitSetRootHistoryExpiryOptimism = _opGasLimit; - emit SetOpGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); + emit SetGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); } /// @notice Sets the gas limit for the transferOwnershipOptimism method /// @param _opGasLimit The new gas limit for the transferOwnershipOptimism method - function setOpGasLimitTransferOwnershipOptimism(uint32 _opGasLimit) external onlyOwner { - opGasLimitTransferOwnershipOptimism = _opGasLimit; + function setGasLimitTransferOwnershipOptimism(uint32 _opGasLimit) external onlyOwner { + gasLimitTransferOwnershipOptimism = _opGasLimit; + + emit SetGasLimitTransferOwnershipOptimism(_opGasLimit); + } + + /////////////////////////////////////////////////////////////////// + /// BASE /// + /////////////////////////////////////////////////////////////////// + + /// @notice Sends the latest WorldID Identity Manager root to all chains. + /// @dev Calls this method on the L1 Proxy contract to relay roots and timestamps to WorldID supported chains. + /// @param root The latest WorldID Identity Manager root. + /// @param timestamp The Ethereum block timestamp of the latest WorldID Identity Manager root. + function _sendRootToBase(uint256 root, uint128 timestamp) internal { + // The `encodeCall` function is strongly typed, so this checks that we are passing the + // correct data to the optimism bridge. + bytes memory message = abi.encodeCall(IOpWorldID.receiveRoot, (root, timestamp)); + + ICrossDomainMessenger(baseCrossDomainMessengerAddress).sendMessage( + // Contract address on Base + baseWorldIDAddress, + message, + gasLimitSendRootBase + ); + } + + /// @notice Adds functionality to the StateBridge to transfer ownership + /// of OpWorldID to another contract on L1 or to a local Base EOA + /// @param _owner new owner (EOA or contract) + /// @param _isLocal true if new owner is on Base, false if it is a cross-domain owner + function transferOwnershipBase(address _owner, bool _isLocal) public onlyOwner { + bytes memory message; + + // The `encodeCall` function is strongly typed, so this checks that we are passing the + // correct data to the optimism bridge. + message = abi.encodeCall(ICrossDomainOwnable3.transferOwnership, (_owner, _isLocal)); + + ICrossDomainMessenger(baseCrossDomainMessengerAddress).sendMessage( + // Contract address on Base + baseWorldIDAddress, + message, + gasLimitTransferOwnershipBase + ); + + emit OwnershipTransferredBase(owner(), _owner, _isLocal); + } + + /// @notice Adds functionality to the StateBridge to set the root history expiry on OpWorldID + /// @param _rootHistoryExpiry new root history expiry + function setRootHistoryExpiryBase(uint256 _rootHistoryExpiry) internal { + bytes memory message; + + // The `encodeCall` function is strongly typed, so this checks that we are passing the + // correct data to the optimism bridge. + message = abi.encodeCall(IRootHistory.setRootHistoryExpiry, (_rootHistoryExpiry)); + + ICrossDomainMessenger(baseCrossDomainMessengerAddress).sendMessage( + // Contract address on Base + baseWorldIDAddress, + message, + gasLimitSetRootHistoryExpiryBase + ); + } + + /////////////////////////////////////////////////////////////////// + /// BASE GAS LIMIT /// + /////////////////////////////////////////////////////////////////// + + /// @notice Sets the gas limit for the Base sendRootMultichain method + /// @param _baseGasLimit The new gas limit for the sendRootMultichain method + function setGasLimitSendRootBase(uint32 _baseGasLimit) external onlyOwner { + gasLimitSendRootBase = _baseGasLimit; + + emit SetGasLimitSendRootBase(_baseGasLimit); + } + + /// @notice Sets the gas limit for the Base setRootHistoryExpiry method + /// @param _baseGasLimit The new gas limit for the setRootHistoryExpiry method + function setGasLimitSetRootHistoryExpiryBase(uint32 _baseGasLimit) external onlyOwner { + gasLimitSetRootHistoryExpiryBase = _baseGasLimit; + + emit SetGasLimitSetRootHistoryExpiryBase(_baseGasLimit); + } + + /// @notice Sets the gas limit for the transferOwnershipBase method + /// @param _baseGasLimit The new gas limit for the transferOwnershipBase method + function setGasLimitTransferOwnershipBase(uint32 _baseGasLimit) external onlyOwner { + gasLimitTransferOwnershipBase = _baseGasLimit; - emit SetOpGasLimitTransferOwnershipOptimism(_opGasLimit); + emit SetGasLimitTransferOwnershipBase(_baseGasLimit); } /////////////////////////////////////////////////////////////////// diff --git a/src/interfaces/IWorldIDIdentityManager.sol b/src/interfaces/IWorldIDIdentityManager.sol index 741af4ca..d0d5b01d 100644 --- a/src/interfaces/IWorldIDIdentityManager.sol +++ b/src/interfaces/IWorldIDIdentityManager.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @title ISemaphoreRoot diff --git a/src/mock/MockPolygonBridge.sol b/src/mock/MockPolygonBridge.sol index ff82c6de..6c6a66ae 100644 --- a/src/mock/MockPolygonBridge.sol +++ b/src/mock/MockPolygonBridge.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {IWorldIDIdentityManager} from "../interfaces/IWorldIDIdentityManager.sol"; diff --git a/src/mock/MockStateBridge.sol b/src/mock/MockStateBridge.sol index 7c252762..c3a75fe7 100644 --- a/src/mock/MockStateBridge.sol +++ b/src/mock/MockStateBridge.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; // Optimism interface for cross domain messaging diff --git a/src/mock/WorldIDIdentityManagerMock.sol b/src/mock/WorldIDIdentityManagerMock.sol index 2c8b404c..c4b814cc 100644 --- a/src/mock/WorldIDIdentityManagerMock.sol +++ b/src/mock/WorldIDIdentityManagerMock.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; diff --git a/src/script/deploy.js b/src/script/deploy.js index 3abbd3b7..5e5beb4c 100644 --- a/src/script/deploy.js +++ b/src/script/deploy.js @@ -99,6 +99,18 @@ async function getOptimismRpcUrl(config) { } } +async function getBaseRpcUrl(config) { + if (!config.baseRpcUrl) { + config.baseRpcUrl = process.env.OP_RPC_URL; + } + if (!config.baseRpcUrl) { + config.baseRpcUrl = await ask(`Enter Base RPC URL: (${DEFAULT_RPC_URL}) `); + } + if (!config.baseRpcUrl) { + config.baseRpcUrl = DEFAULT_RPC_URL; + } +} + async function getOptimismAlchemyApiKey(config) { if (!config.optimismAlchemyApiKey) { config.optimismAlchemyApiKey = process.env.OP_ALCHEMY_API_KEY; @@ -131,6 +143,15 @@ async function getOptimismEtherscanApiKey(config) { } } +async function getBaseEtherscanApiKey(config) { + if (!config.baseEtherscanApiKey) { + config.baseEtherscanApiKey = process.env.BASE_ETHERSCAN_API_KEY; + } + if (!config.baseEtherscanApiKey) { + config.baseEtherscanApiKey = await ask(`Enter BaseScan API KEY: (https://basescan.org/register) `); + } +} + async function getEthereumEtherscanApiKey(config) { if (!config.ethereumEtherscanApiKey) { config.ethereumEtherscanApiKey = process.env.ETHERSCAN_API_KEY; @@ -150,9 +171,6 @@ async function getPolygonscanApiKey(config) { } async function getTreeDepth(config) { - if (!config.treeDepth) { - config.treeDepth = process.env.TREE_DEPTH; - } if (!config.treeDepth) { config.treeDepth = await ask("Enter WorldID tree depth: "); } @@ -176,6 +194,15 @@ async function getOptimismWorldIDAddress(config) { } } +async function getBaseWorldIDAddress(config) { + if (!config.baseWorldIDAddress) { + config.baseWorldIDAddress = process.env.BASE_WORLD_ID_ADDRESS; + } + if (!config.baseWorldIDAddress) { + config.baseWorldIDAddress = await ask("Enter Base World ID Address: "); + } +} + async function getPolygonWorldIDAddress(config) { if (!config.polygonWorldIDAddress) { config.polygonWorldIDAddress = process.env.POLYGON_WORLD_ID_ADDRESS; @@ -214,37 +241,64 @@ async function getDeployerAddress(config) { } } -async function getOpGasLimitSendRootOptimism(config) { - if (!config.opGasLimitSendRootOptimism) { - config.opGasLimitSendRootOptimism = process.env.OP_GAS_LIMIT_SEND_ROOT_OPTIMISM; +async function getGasLimitSendRootOptimism(config) { + if (!config.gasLimitSendRootOptimism) { + config.gasLimitSendRootOptimism = process.env.GAS_LIMIT_SEND_ROOT_OPTIMISM; } - if (!config.opGasLimitSendRootOptimism) { - config.opGasLimitSendRootOptimism = await ask("Enter the Optimism gas limit for sendRootOptimism: "); + if (!config.gasLimitSendRootOptimism) { + config.gasLimitSendRootOptimism = await ask("Enter the Optimism gas limit for sendRootOptimism: "); } } -async function getOpGasLimitSetRootHistoryExpiryOptimism(config) { - if (!config.opGasLimitSetRootHistoryExpiryOptimism) { - config.opGasLimitSetRootHistoryExpiryOptimism = process.env.OP_GAS_LIMIT_SET_ROOT_HISTORY_EXPIRY_OPTIMISM; +async function getGasLimitSetRootHistoryExpiryOptimism(config) { + if (!config.gasLimitSetRootHistoryExpiryOptimism) { + config.gasLimitSetRootHistoryExpiryOptimism = process.env.GAS_LIMIT_SET_ROOT_HISTORY_EXPIRY_OPTIMISM; } - if (!config.opGasLimitSetRootHistoryExpiryOptimism) { - config.opGasLimitSetRootHistoryExpiryOptimism = await ask( + if (!config.gasLimitSetRootHistoryExpiryOptimism) { + config.gasLimitSetRootHistoryExpiryOptimism = await ask( "Enter the Optimism gas limit for setRootHistoryExpiryOptimism: ", ); } } -async function getOpGasLimitTransferOwnershipOptimism(config) { - if (!config.opGasLimitTransferOwnershipOptimism) { - config.opGasLimitTransferOwnershipOptimism = process.env.OP_GAS_LIMIT_TRANSFER_OWNERSHIP_OPTIMISM; +async function getGasLimitTransferOwnershipOptimism(config) { + if (!config.gasLimitTransferOwnershipOptimism) { + config.gasLimitTransferOwnershipOptimism = process.env.GAS_LIMIT_TRANSFER_OWNERSHIP_OPTIMISM; } - if (!config.opGasLimitTransferOwnershipOptimism) { - config.opGasLimitTransferOwnershipOptimism = await ask( + if (!config.gasLimitTransferOwnershipOptimism) { + config.gasLimitTransferOwnershipOptimism = await ask( "Enter the Optimism gas limit for transferOwnershipOptimism: ", ); } } +async function getGasLimitSendRootBase(config) { + if (!config.gasLimitSendRootBase) { + config.gasLimitSendRootBase = process.env.GAS_LIMIT_SEND_ROOT_BASE; + } + if (!config.gasLimitSendRootBase) { + config.gasLimitSendRootBase = await ask("Enter the Base gas limit for sendRootBase: "); + } +} + +async function getGasLimitSetRootHistoryExpiryBase(config) { + if (!config.gasLimitSetRootHistoryExpiryBase) { + config.gasLimitSetRootHistoryExpiryBase = process.env.GAS_LIMIT_SET_ROOT_HISTORY_EXPIRY_BASE; + } + if (!config.gasLimitSetRootHistoryExpiryBase) { + config.gasLimitSetRootHistoryExpiryBase = await ask("Enter the Base gas limit for setRootHistoryExpiryBase: "); + } +} + +async function getGasLimitTransferOwnershipBase(config) { + if (!config.gasLimitTransferOwnershipBase) { + config.gasLimitTransferOwnershipBase = process.env.GAS_LIMIT_TRANSFER_OWNERSHIP_BASE; + } + if (!config.gasLimitTransferOwnershipBase) { + config.gasLimitTransferOwnershipBase = await ask("Enter the Base gas limit for transferOwnershipBase: "); + } +} + async function loadConfiguration(useConfig) { if (!useConfig) { return {}; @@ -343,7 +397,7 @@ async function deployPolygonWorldIDMainnet(config) { try { const data = - execSync(`forge script src/script/deploy/DeployPolygonWorldIDMainnet.s.sol:DeployPolygonWorldIDMainnet --fork-url ${config.polygonRpcUrl} \ + execSync(`forge script src/script/deploy/DeployPolygonWorldIDMainnet.s.sol:DeployPolygonWorldID --fork-url ${config.polygonRpcUrl} \ --etherscan-api-key ${config.polygonscanApiKey} --legacy --broadcast --verify -vvvv`); console.log(data.toString()); } catch (err) { @@ -385,6 +439,22 @@ async function deployOptimismWorldID(config) { spinner.succeed("DeployOpWorldID.s.sol ran successfully!"); } +async function deployBaseWorldID(config) { + const spinner = ora("Deploying BaseWorldID...").start(); + + try { + const data = execSync( + `forge script src/script/deploy/DeployOpWorldID.s.sol:DeployOpWorldID --fork-url ${config.baseRpcUrl} \ + --etherscan-api-key ${config.baseEtherscanApiKey} --broadcast --verify -vvvv`, + ); + console.log(data.toString()); + } catch (err) { + console.error(err); + } + + spinner.succeed("DeployOpWorldID.s.sol ran successfully!"); +} + async function deployMockOpPolygonWorldID(config) { const spinner = ora("Deploying MockOpPolygonWorldID...").start(); @@ -437,7 +507,7 @@ async function initializePolygonWorldID(config) { try { const data = execSync( - `forge script src/script/initialize/InitializePolygonWorldID.s.sol:InitializePolygonWorldID --fork-url ${config.polygonRpcUrl} --broadcast -vvvv`, + `forge script src/script/initialize/InitializePolygonWorldID.s.sol:InitializePolygonWorldID --fork-url ${config.polygonRpcUrl} --broadcast -vvvv --legacy`, ); console.log(data.toString()); } catch (err) { @@ -503,23 +573,27 @@ async function deploymentMainnet(config) { await getPrivateKey(config); await getEthereumRpcUrl(config); await getOptimismRpcUrl(config); + await getBaseRpcUrl(config); await getPolygonRpcUrl(config); await getEthereumEtherscanApiKey(config); await getOptimismEtherscanApiKey(config); + await getBaseEtherscanApiKey(config); await getPolygonscanApiKey(config); await getTreeDepth(config); await saveConfiguration(config); await deployOptimismWorldID(config); + await deployBaseWorldID(config); await deployPolygonWorldIDMainnet(config); await getWorldIDIdentityManagerAddress(config); await getOptimismWorldIDAddress(config); + await getBaseWorldIDAddress(config); await getPolygonWorldIDAddress(config); await saveConfiguration(config); await deployStateBridgeMainnet(config); await getStateBridgeAddress(config); await saveConfiguration(config); await initializePolygonWorldID(config); - await transferOwnershipOfOpWorldIDMainnet(config); + // await transferOwnershipOfOpWorldIDMainnet(config); } async function deploymentTestnet(config) { @@ -528,23 +602,27 @@ async function deploymentTestnet(config) { await getPrivateKey(config); await getEthereumRpcUrl(config); await getOptimismRpcUrl(config); + await getBaseRpcUrl(config); await getPolygonRpcUrl(config); await getEthereumEtherscanApiKey(config); await getOptimismEtherscanApiKey(config); + await getBaseEtherscanApiKey(config); await getPolygonscanApiKey(config); await getTreeDepth(config); await saveConfiguration(config); await deployOptimismWorldID(config); + await deployBaseWorldID(config); await deployPolygonWorldIDMumbai(config); await getWorldIDIdentityManagerAddress(config); await getOptimismWorldIDAddress(config); + await getBaseWorldIDAddress(config); await getPolygonWorldIDAddress(config); await saveConfiguration(config); await deployStateBridgeGoerli(config); await getStateBridgeAddress(config); await saveConfiguration(config); await initializePolygonWorldID(config); - await transferOwnershipOfOpWorldIDGoerli(config); + // await transferOwnershipOfOpWorldIDGoerli(config); } async function mockDeployment(config) { @@ -553,9 +631,11 @@ async function mockDeployment(config) { await getPrivateKey(config); await getEthereumRpcUrl(config); await getOptimismRpcUrl(config); + await getBaseRpcUrl(config); await getPolygonRpcUrl(config); await getEthereumEtherscanApiKey(config); await getOptimismEtherscanApiKey(config); + await getBaseEtherscanApiKey(config); await getPolygonscanApiKey(config); await getTreeDepth(config); await saveConfiguration(config); @@ -564,6 +644,7 @@ async function mockDeployment(config) { await deployPolygonWorldIDMumbai(config); await getWorldIDIdentityManagerAddress(config); await getOptimismWorldIDAddress(config); + await getBaseWorldIDAddress(config); await getPolygonWorldIDAddress(config); await saveConfiguration(config); await deployStateBridgeGoerli(config); @@ -584,8 +665,10 @@ async function mockLocalDeployment(config) { await getEthereumRpcUrl(config); await getOptimismRpcUrl(config); await getPolygonRpcUrl(config); + await getBaseRpcUrl(config); await getEthereumEtherscanApiKey(config); await getOptimismEtherscanApiKey(config); + await getBaseEtherscanApiKey(config); await getPolygonscanApiKey(config); await getTreeDepth(config); await saveConfiguration(config); @@ -593,6 +676,7 @@ async function mockLocalDeployment(config) { await deployMockOpPolygonWorldID(config); await getWorldIDIdentityManagerAddress(config); await getOptimismWorldIDAddress(config); + await getBaseWorldIDAddress(config); await getPolygonWorldIDAddress(config); await saveConfiguration(config); await deployMockStateBridge(config); @@ -609,12 +693,18 @@ async function setOpGasLimit(config) { await getEthereumRpcUrl(config); await getOptimismWorldIDAddress(config); - await getOptimismAlchemyApiKey(config); + await getOptimismRpcUrl(config); + await getBaseWorldIDAddress(config); + await getBaseRpcUrl(config); await getDeployerAddress(config); await saveConfiguration(config); - await getOpGasLimitSendRootOptimism(config); - await getOpGasLimitSetRootHistoryExpiryOptimism(config); - await getOpGasLimitTransferOwnershipOptimism(config); + await getGasLimitSendRootOptimism(config); + await getGasLimitSetRootHistoryExpiryOptimism(config); + await getGasLimitTransferOwnershipOptimism(config); + await getGasLimitSendRootBase(config); + await getGasLimitSetRootHistoryExpiryBase(config); + await getGasLimitTransferOwnershipBase(config); + // await getOpGasLimitEstimates(config); await saveConfiguration(config); diff --git a/src/script/deploy/DeployMockOpPolygonWorldID.s.sol b/src/script/deploy/DeployMockOpPolygonWorldID.s.sol index 4ef565f4..bbfe286d 100644 --- a/src/script/deploy/DeployMockOpPolygonWorldID.s.sol +++ b/src/script/deploy/DeployMockOpPolygonWorldID.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; diff --git a/src/script/deploy/DeployMockStateBridge.s.sol b/src/script/deploy/DeployMockStateBridge.s.sol index 26aa0da6..eaa86f84 100644 --- a/src/script/deploy/DeployMockStateBridge.s.sol +++ b/src/script/deploy/DeployMockStateBridge.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; @@ -12,7 +12,6 @@ contract DeployMockStateBridge is Script { MockStateBridge public bridge; address public opWorldIDAddress; - address public polygonWorldIDAddress; address public worldIDIdentityManagerAddress; address public stateBridgeAddress; @@ -32,7 +31,6 @@ contract DeployMockStateBridge is Script { worldIDIdentityManagerAddress = abi.decode(vm.parseJson(json, ".worldIDIdentityManagerAddress"), (address)); opWorldIDAddress = abi.decode(vm.parseJson(json, ".optimismWorldIDAddress"), (address)); - polygonWorldIDAddress = abi.decode(vm.parseJson(json, ".polygonWorldIDAddress"), (address)); } function run() public { diff --git a/src/script/deploy/DeployMockWorldID.s.sol b/src/script/deploy/DeployMockWorldID.s.sol index 7fd83631..b82d49ad 100644 --- a/src/script/deploy/DeployMockWorldID.s.sol +++ b/src/script/deploy/DeployMockWorldID.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @dev Demo deployments diff --git a/src/script/deploy/DeployOpWorldID.s.sol b/src/script/deploy/DeployOpWorldID.s.sol index 6318a6b2..d90f86d2 100644 --- a/src/script/deploy/DeployOpWorldID.s.sol +++ b/src/script/deploy/DeployOpWorldID.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @dev Demo deployments @@ -22,7 +22,7 @@ contract DeployOpWorldID is Script { string public json = vm.readFile(path); uint256 public privateKey = abi.decode(vm.parseJson(json, ".privateKey"), (uint256)); - uint8 public treeDepth = abi.decode(vm.parseJson(json, ".treeDepth"), (uint8)); + uint8 public treeDepth = uint8(30); function run() external { vm.startBroadcast(privateKey); diff --git a/src/script/deploy/DeployPolygonWorldIDMainnet.s.sol b/src/script/deploy/DeployPolygonWorldIDMainnet.s.sol index 94373c13..6a23ac7d 100644 --- a/src/script/deploy/DeployPolygonWorldIDMainnet.s.sol +++ b/src/script/deploy/DeployPolygonWorldIDMainnet.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; @@ -27,7 +27,7 @@ contract DeployPolygonWorldID is Script { function setUp() public { privateKey = abi.decode(vm.parseJson(json, ".privateKey"), (uint256)); - treeDepth = abi.decode(vm.parseJson(json, ".treeDepth"), (uint8)); + treeDepth = uint8(30); } function run() external { diff --git a/src/script/deploy/DeployPolygonWorldIDMumbai.s.sol b/src/script/deploy/DeployPolygonWorldIDMumbai.s.sol index 231ec92a..b68b01e6 100644 --- a/src/script/deploy/DeployPolygonWorldIDMumbai.s.sol +++ b/src/script/deploy/DeployPolygonWorldIDMumbai.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @dev Demo deployments @@ -31,7 +31,7 @@ contract DeployPolygonWorldIDMumbai is Script { function setUp() public { privateKey = abi.decode(vm.parseJson(json, ".privateKey"), (uint256)); - treeDepth = abi.decode(vm.parseJson(json, ".treeDepth"), (uint8)); + treeDepth = uint8(30); } function run() external { diff --git a/src/script/deploy/DeployStateBridgeGoerli.s.sol b/src/script/deploy/DeployStateBridgeGoerli.s.sol index dd9c1911..5b811b38 100644 --- a/src/script/deploy/DeployStateBridgeGoerli.s.sol +++ b/src/script/deploy/DeployStateBridgeGoerli.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @dev Demo deployments @@ -16,8 +16,10 @@ contract DeployStateBridge is Script { address public opWorldIDAddress; address public polygonWorldIDAddress; + address public baseWorldIDAddress; address public worldIDIdentityManagerAddress; - address public crossDomainMessengerAddress; + address public opCrossDomainMessengerAddress; + address public baseCrossDomainMessengerAddress; address public stateBridgeAddress; address public checkpointManagerAddress; @@ -47,7 +49,13 @@ contract DeployStateBridge is Script { /////////////////////////////////////////////////////////////////// /// OPTIMISM /// /////////////////////////////////////////////////////////////////// - crossDomainMessengerAddress = address(0x5086d1eEF304eb5284A0f6720f79403b4e9bE294); + opCrossDomainMessengerAddress = address(0x5086d1eEF304eb5284A0f6720f79403b4e9bE294); + + /////////////////////////////////////////////////////////////////// + /// BASE /// + /////////////////////////////////////////////////////////////////// + // Taken from https://docs.base.org/base-contracts + baseCrossDomainMessengerAddress = address(0x8e5693140eA606bcEB98761d9beB1BC87383706D); /////////////////////////////////////////////////////////////////// /// WORLD ID /// @@ -55,6 +63,7 @@ contract DeployStateBridge is Script { worldIDIdentityManagerAddress = abi.decode(vm.parseJson(json, ".worldIDIdentityManagerAddress"), (address)); opWorldIDAddress = abi.decode(vm.parseJson(json, ".optimismWorldIDAddress"), (address)); + baseWorldIDAddress = abi.decode(vm.parseJson(json, ".baseWorldIDAddress"), (address)); polygonWorldIDAddress = abi.decode(vm.parseJson(json, ".polygonWorldIDAddress"), (address)); } @@ -66,7 +75,9 @@ contract DeployStateBridge is Script { fxRootAddress, worldIDIdentityManagerAddress, opWorldIDAddress, - crossDomainMessengerAddress + opCrossDomainMessengerAddress, + baseWorldIDAddress, + baseCrossDomainMessengerAddress ); bridge.setFxChildTunnel(polygonWorldIDAddress); diff --git a/src/script/deploy/DeployStateBridgeMainnet.s.sol b/src/script/deploy/DeployStateBridgeMainnet.s.sol index 83cbca56..3bb830fc 100644 --- a/src/script/deploy/DeployStateBridgeMainnet.s.sol +++ b/src/script/deploy/DeployStateBridgeMainnet.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; @@ -13,8 +13,10 @@ contract DeployStateBridge is Script { address public opWorldIDAddress; address public polygonWorldIDAddress; + address public baseWorldIDAddress; address public worldIDIdentityManagerAddress; - address public crossDomainMessengerAddress; + address public opCrossDomainMessengerAddress; + address public baseCrossDomainMessengerAddress; address public stateBridgeAddress; address public checkpointManagerAddress; @@ -43,7 +45,13 @@ contract DeployStateBridge is Script { /////////////////////////////////////////////////////////////////// /// OPTIMISM /// /////////////////////////////////////////////////////////////////// - crossDomainMessengerAddress = address(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1); + opCrossDomainMessengerAddress = address(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1); + + /////////////////////////////////////////////////////////////////// + /// BASE /// + /////////////////////////////////////////////////////////////////// + // Taken from https://docs.base.org/base-contracts + baseCrossDomainMessengerAddress = address(0x866E82a600A1414e583f7F13623F1aC5d58b0Afa); /////////////////////////////////////////////////////////////////// /// WORLD ID /// @@ -52,6 +60,7 @@ contract DeployStateBridge is Script { abi.decode(vm.parseJson(json, ".worldIDIdentityManagerAddress"), (address)); opWorldIDAddress = abi.decode(vm.parseJson(json, ".optimismWorldIDAddress"), (address)); polygonWorldIDAddress = abi.decode(vm.parseJson(json, ".polygonWorldIDAddress"), (address)); + baseWorldIDAddress = abi.decode(vm.parseJson(json, ".baseWorldIDAddress"), (address)); } function run() public { @@ -62,9 +71,13 @@ contract DeployStateBridge is Script { fxRootAddress, worldIDIdentityManagerAddress, opWorldIDAddress, - crossDomainMessengerAddress + opCrossDomainMessengerAddress, + baseWorldIDAddress, + baseCrossDomainMessengerAddress ); + bridge.setFxChildTunnel(polygonWorldIDAddress); + vm.stopBroadcast(); } } diff --git a/src/script/initialize/InitializeMockWorldID.s.sol b/src/script/initialize/InitializeMockWorldID.s.sol index b8a90bf7..afb89f44 100644 --- a/src/script/initialize/InitializeMockWorldID.s.sol +++ b/src/script/initialize/InitializeMockWorldID.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; diff --git a/src/script/initialize/InitializePolygonWorldID.s.sol b/src/script/initialize/InitializePolygonWorldID.s.sol index d3cb6353..35ffb76f 100644 --- a/src/script/initialize/InitializePolygonWorldID.s.sol +++ b/src/script/initialize/InitializePolygonWorldID.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; // Demo deployments diff --git a/src/script/initialize/SetOpGasLimit.s.sol b/src/script/initialize/SetOpGasLimit.s.sol index 95c4822b..f084e835 100644 --- a/src/script/initialize/SetOpGasLimit.s.sol +++ b/src/script/initialize/SetOpGasLimit.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; @@ -27,12 +27,18 @@ contract SetOpGasLimit is Script { /////////////////////////////////////////////////////////////////// /// OP GAS LIMITS /// /////////////////////////////////////////////////////////////////// - uint32 public opGasLimitSendRootOptimism = - abi.decode(vm.parseJson(json, ".opGasLimitSendRootOptimism"), (uint32)); - uint32 public opGasLimitSetRootHistoryExpiryOptimism = - abi.decode(vm.parseJson(json, ".opGasLimitSetRootHistoryExpiryOptimism"), (uint32)); - uint32 public opGasLimitTransferOwnershipOptimism = - abi.decode(vm.parseJson(json, ".opGasLimitTransferOwnershipOptimism"), (uint32)); + uint32 public gasLimitSendRootOptimism = + abi.decode(vm.parseJson(json, ".gasLimitSendRootOptimism"), (uint32)); + uint32 public gasLimitSetRootHistoryExpiryOptimism = + abi.decode(vm.parseJson(json, ".gasLimitSetRootHistoryExpiryOptimism"), (uint32)); + uint32 public gasLimitTransferOwnershipOptimism = + abi.decode(vm.parseJson(json, ".gasLimitTransferOwnershipOptimism"), (uint32)); + uint32 public gasLimitSendRootBase = + abi.decode(vm.parseJson(json, ".gasLimitSendRootBase"), (uint32)); + uint32 public gasLimitSetRootHistoryExpiryBase = + abi.decode(vm.parseJson(json, ".gasLimitSetRootHistoryExpiryBase"), (uint32)); + uint32 public gasLimitTransferOwnershipBase = + abi.decode(vm.parseJson(json, ".gasLimitTransferOwnershipBase"), (uint32)); function setUp() public { stateBridgeAddress = abi.decode(vm.parseJson(json, ".stateBridgeAddress"), (address)); @@ -43,12 +49,13 @@ contract SetOpGasLimit is Script { function run() public { vm.startBroadcast(privateKey); - stateBridge.setOpGasLimitSendRootOptimism(opGasLimitSendRootOptimism); - stateBridge.setOpGasLimitSetRootHistoryExpiryOptimism( - opGasLimitSetRootHistoryExpiryOptimism - ); - stateBridge.setOpGasLimitTransferOwnershipOptimism(opGasLimitTransferOwnershipOptimism); + stateBridge.setGasLimitSendRootOptimism(gasLimitSendRootOptimism); + stateBridge.setGasLimitSetRootHistoryExpiryOptimism(gasLimitSetRootHistoryExpiryOptimism); + stateBridge.setGasLimitTransferOwnershipOptimism(gasLimitTransferOwnershipOptimism); + stateBridge.setGasLimitSendRootBase(gasLimitSendRootBase); + stateBridge.setGasLimitSetRootHistoryExpiryBase(gasLimitSetRootHistoryExpiryBase); + stateBridge.setGasLimitTransferOwnershipBase(gasLimitTransferOwnershipBase); vm.stopBroadcast(); } } diff --git a/src/script/initialize/TransferOwnershipOfOpWorldIDGoerli.s.sol b/src/script/initialize/TransferOwnershipOfOpWorldIDGoerli.s.sol index d6e1b99b..b390d11f 100644 --- a/src/script/initialize/TransferOwnershipOfOpWorldIDGoerli.s.sol +++ b/src/script/initialize/TransferOwnershipOfOpWorldIDGoerli.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; diff --git a/src/script/initialize/TransferOwnershipOfOpWorldIDMainnet.s.sol b/src/script/initialize/TransferOwnershipOfOpWorldIDMainnet.s.sol index f0abafbc..8dcca90a 100644 --- a/src/script/initialize/TransferOwnershipOfOpWorldIDMainnet.s.sol +++ b/src/script/initialize/TransferOwnershipOfOpWorldIDMainnet.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {Script} from "forge-std/Script.sol"; diff --git a/src/script/test/SendStateRootToStateBridge.s.sol b/src/script/test/SendStateRootToStateBridge.s.sol index f2ba9d41..67b6bf6a 100644 --- a/src/script/test/SendStateRootToStateBridge.s.sol +++ b/src/script/test/SendStateRootToStateBridge.s.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; // demo deployments diff --git a/src/test/OpWorldID.t.sol b/src/test/OpWorldID.t.sol index d0ed6f33..44761178 100644 --- a/src/test/OpWorldID.t.sol +++ b/src/test/OpWorldID.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @dev using Test from forge-std which is inherited from Optimism's CommonTest.t.sol diff --git a/src/test/PolygonWorldID.t.sol b/src/test/PolygonWorldID.t.sol index e5655872..cb3fba1b 100644 --- a/src/test/PolygonWorldID.t.sol +++ b/src/test/PolygonWorldID.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {PolygonWorldID} from "src/PolygonWorldID.sol"; diff --git a/src/test/StateBridge.t.sol b/src/test/StateBridge.t.sol index a32b56d9..d9931f9e 100644 --- a/src/test/StateBridge.t.sol +++ b/src/test/StateBridge.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import {StateBridge} from "src/StateBridge.sol"; @@ -27,6 +27,9 @@ contract StateBridgeTest is PRBTest, StdCheats { address public mockWorldIDAddress; address public crossDomainMessengerAddress; + address public opCrossDomainMessengerAddress; + address public baseCrossDomainMessengerAddress; + address public fxRoot; address public checkpointManager; address public owner; @@ -65,15 +68,27 @@ contract StateBridgeTest is PRBTest, StdCheats { /// @notice Emmitted when the the StateBridge sets the opGasLimit for sendRootOptimism /// @param _opGasLimit The new opGasLimit for sendRootOptimism - event SetOpGasLimitSendRootOptimism(uint32 _opGasLimit); + event SetGasLimitSendRootOptimism(uint32 _opGasLimit); + + /// @notice Emmitted when the the StateBridge sets the opGasLimit for setRootHistoryExpiryOptimism + /// @param _opGasLimit The new opGasLimit for setRootHistoryExpiryOptimism + event SetGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit); + + /// @notice Emmitted when the the StateBridge sets the opGasLimit for transferOwnershipOptimism + /// @param _opGasLimit The new opGasLimit for transferOwnershipOptimism + event SetGasLimitTransferOwnershipOptimism(uint32 _opGasLimit); + + /// @notice Emmitted when the the StateBridge sets the opGasLimit for sendRootOptimism + /// @param _opGasLimit The new opGasLimit for sendRootOptimism + event SetGasLimitSendRootBase(uint32 _opGasLimit); /// @notice Emmitted when the the StateBridge sets the opGasLimit for setRootHistoryExpiryOptimism /// @param _opGasLimit The new opGasLimit for setRootHistoryExpiryOptimism - event SetOpGasLimitSetRootHistoryExpiryOptimism(uint32 _opGasLimit); + event SetGasLimitSetRootHistoryExpiryBase(uint32 _opGasLimit); /// @notice Emmitted when the the StateBridge sets the opGasLimit for transferOwnershipOptimism /// @param _opGasLimit The new opGasLimit for transferOwnershipOptimism - event SetOpGasLimitTransferOwnershipOptimism(uint32 _opGasLimit); + event SetGasLimitTransferOwnershipBase(uint32 _opGasLimit); /////////////////////////////////////////////////////////////////// /// ERRORS /// @@ -87,11 +102,13 @@ contract StateBridgeTest is PRBTest, StdCheats { mainnetFork = vm.createFork(MAINNET_RPC_URL); vm.selectFork(mainnetFork); - /// @notice Roll the fork to the block where Optimim's crossDomainMessenger contract is deployed - vm.rollFork(16883118); + /// @notice Roll the fork to a block where both Optimim's and Base's crossDomainMessenger contract is deployed + /// @notice and the Base crossDomainMessenger ResolvedDelegateProxy target address is initialized + vm.rollFork(17711915); if (block.chainid == 1) { - crossDomainMessengerAddress = address(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1); + opCrossDomainMessengerAddress = address(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1); + baseCrossDomainMessengerAddress = address(0x866E82a600A1414e583f7F13623F1aC5d58b0Afa); } else { revert invalidCrossDomainMessengerFork(); } @@ -102,12 +119,17 @@ contract StateBridgeTest is PRBTest, StdCheats { checkpointManager = address(0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287); fxRoot = address(0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2); + address opWorldIDAddress = address(0x1); + address baseWorldIDAddress = address(0x2); + stateBridge = new StateBridge( checkpointManager, fxRoot, mockWorldIDAddress, - address(0x1), - crossDomainMessengerAddress + opWorldIDAddress, + opCrossDomainMessengerAddress, + baseWorldIDAddress, + baseCrossDomainMessengerAddress ); owner = stateBridge.owner(); @@ -184,7 +206,6 @@ contract StateBridgeTest is PRBTest, StdCheats { vm.assume(_rootHistoryExpiry != 0); vm.expectEmit(true, true, true, true); - emit SetRootHistoryExpiry(_rootHistoryExpiry); vm.prank(mockWorldIDAddress); @@ -193,45 +214,82 @@ contract StateBridgeTest is PRBTest, StdCheats { /// @notice tests whether the StateBridge contract can set the opGasLimit for sendRootOptimism /// @param _opGasLimit The new opGasLimit for sendRootOptimism - function test_owner_setOpGasLimitSendRootOptimism_succeeds(uint32 _opGasLimit) public { + function test_owner_setGasLimitSendRootOptimism_succeeds(uint32 _opGasLimit) public { vm.assume(_opGasLimit != 0); vm.expectEmit(true, true, true, true); - emit SetOpGasLimitSendRootOptimism(_opGasLimit); + emit SetGasLimitSendRootOptimism(_opGasLimit); vm.prank(owner); - stateBridge.setOpGasLimitSendRootOptimism(_opGasLimit); + stateBridge.setGasLimitSendRootOptimism(_opGasLimit); } /// @notice tests whether the StateBridge contract can set the opGasLimit for setRootHistoryExpiryOptimism /// @param _opGasLimit The new opGasLimit for setRootHistoryExpiryOptimism - function test_owner_setOpGasLimitSetRootHistoryExpiryOptimism_succeeds(uint32 _opGasLimit) + function test_owner_setGasLimitSetRootHistoryExpiryOptimism_succeeds(uint32 _opGasLimit) public { vm.assume(_opGasLimit != 0); vm.expectEmit(true, true, true, true); - emit SetOpGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); + emit SetGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); vm.prank(owner); - stateBridge.setOpGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); + stateBridge.setGasLimitSetRootHistoryExpiryOptimism(_opGasLimit); } /// @notice tests whether the StateBridge contract can set the opGasLimit for transferOwnershipOptimism /// @param _opGasLimit The new opGasLimit for transferOwnershipOptimism - function test_owner_setOpGasLimitTransferOwnershipOptimism_succeeds(uint32 _opGasLimit) - public - { + function test_owner_setGasLimitTransferOwnershipOptimism_succeeds(uint32 _opGasLimit) public { vm.assume(_opGasLimit != 0); vm.expectEmit(true, true, true, true); - emit SetOpGasLimitTransferOwnershipOptimism(_opGasLimit); + emit SetGasLimitTransferOwnershipOptimism(_opGasLimit); + + vm.prank(owner); + stateBridge.setGasLimitTransferOwnershipOptimism(_opGasLimit); + } + + /// @notice tests whether the StateBridge contract can set the opGasLimit for sendRootBase + /// @param _baseGasLimit The new opGasLimit for sendRootBase + function test_owner_setGasLimitSendRootBase_succeeds(uint32 _baseGasLimit) public { + vm.assume(_baseGasLimit != 0); + + vm.expectEmit(true, true, true, true); + + emit SetGasLimitSendRootBase(_baseGasLimit); + + vm.prank(owner); + stateBridge.setGasLimitSendRootBase(_baseGasLimit); + } + + /// @notice tests whether the StateBridge contract can set the opGasLimit for setRootHistoryExpiryBase + /// @param _baseGasLimit The new opGasLimit for setRootHistoryExpiryBase + function test_owner_setGasLimitSetRootHistoryExpiryBase_succeeds(uint32 _baseGasLimit) public { + vm.assume(_baseGasLimit != 0); + + vm.expectEmit(true, true, true, true); + + emit SetGasLimitSetRootHistoryExpiryBase(_baseGasLimit); + + vm.prank(owner); + stateBridge.setGasLimitSetRootHistoryExpiryBase(_baseGasLimit); + } + + /// @notice tests whether the StateBridge contract can set the opGasLimit for transferOwnershipBase + /// @param _baseGasLimit The new opGasLimit for transferOwnershipBase + function test_owner_setGasLimitTransferOwnershipBase_succeeds(uint32 _baseGasLimit) public { + vm.assume(_baseGasLimit != 0); + + vm.expectEmit(true, true, true, true); + + emit SetGasLimitTransferOwnershipBase(_baseGasLimit); vm.prank(owner); - stateBridge.setOpGasLimitTransferOwnershipOptimism(_opGasLimit); + stateBridge.setGasLimitTransferOwnershipBase(_baseGasLimit); } /////////////////////////////////////////////////////////////////// @@ -274,6 +332,21 @@ contract StateBridgeTest is PRBTest, StdCheats { stateBridge.transferOwnershipOptimism(newOwner, isLocal); } + /// @notice tests that the StateBridge contract's ownership can't be changed by a non-owner + /// @param newOwner The new owner of the StateBridge contract (foundry fuzz) + function test_notOwner_transferOwnershipBase_reverts( + address nonOwner, + address newOwner, + bool isLocal + ) public { + vm.assume(nonOwner != owner && newOwner != address(0x0)); + + vm.expectRevert("Ownable: caller is not the owner"); + + vm.prank(nonOwner); + stateBridge.transferOwnershipBase(newOwner, isLocal); + } + /// @notice tests whether the StateBridge contract can set root history expiry on Optimism and Polygon /// @param _rootHistoryExpiry The new root history expiry for OpWorldID and PolygonWorldID function test_notOwner_setRootHistoryExpiry_reverts(