From 2f590ee4cc377c74252dc6b9ec76df95d716cfdf Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 21 Feb 2025 15:32:30 +0400 Subject: [PATCH 1/3] feat: add watcher role --- .../protocol/watcherPrecompile/WatcherPrecompile.sol | 10 +++++----- .../watcherPrecompile/WatcherPrecompileConfig.sol | 2 +- .../watcherPrecompile/WatcherPrecompileLimits.sol | 5 +++-- script/CheckDepositedFees.s.sol | 2 +- test/SetupTest.t.sol | 3 +++ 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index cf3d0b73..3ab83ee4 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -149,7 +149,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @notice Ends the timeouts and calls the target address with the callback payload /// @param timeoutId_ The unique identifier for the timeout /// @dev Only callable by the contract owner - function resolveTimeout(bytes32 timeoutId_) external onlyOwner { + function resolveTimeout(bytes32 timeoutId_) external onlyRole(WATCHER_ROLE) { TimeoutRequest storage timeoutRequest_ = timeoutRequests[timeoutId_]; if (timeoutRequest_.target == address(0)) revert InvalidTimeoutRequest(); @@ -296,7 +296,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @dev Only callable by the contract owner /// @dev Watcher signs on following digest for validation on switchboard: /// @dev keccak256(abi.encode(switchboard, root)) - function finalized(bytes32 payloadId_, bytes calldata signature_) external onlyOwner { + function finalized(bytes32 payloadId_, bytes calldata signature_) external onlyRole(WATCHER_ROLE) { watcherSignatures[payloadId_] = signature_; emit Finalized(payloadId_, asyncRequests[payloadId_], signature_); } @@ -304,7 +304,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @notice Resolves multiple promises with their return data /// @param resolvedPromises_ Array of resolved promises and their return data /// @dev Only callable by the contract owner - function resolvePromises(ResolvedPromises[] calldata resolvedPromises_) external onlyOwner { + function resolvePromises(ResolvedPromises[] calldata resolvedPromises_) external onlyRole(WATCHER_ROLE) { for (uint256 i = 0; i < resolvedPromises_.length; i++) { // Get the array of promise addresses for this payload AsyncRequest memory asyncRequest_ = asyncRequests[resolvedPromises_[i].payloadId]; @@ -327,7 +327,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { } // wait till expiry time to assign fees - function markRevert(bytes32 payloadId_, bool isRevertingOnchain_) external onlyOwner { + function markRevert(bytes32 payloadId_, bool isRevertingOnchain_) external onlyRole(WATCHER_ROLE) { AsyncRequest memory asyncRequest_ = asyncRequests[payloadId_]; address[] memory next = asyncRequest_.next; @@ -370,7 +370,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { // ================== On-Chain Inbox ================== - function callAppGateways(CallFromInboxParams[] calldata params_) external onlyOwner { + function callAppGateways(CallFromInboxParams[] calldata params_) external onlyRole(WATCHER_ROLE) { for (uint256 i = 0; i < params_.length; i++) { if (appGatewayCalled[params_[i].callId]) revert AppGatewayAlreadyCalled(); if (!isValidInboxCaller[params_[i].appGateway][params_[i].chainSlug][params_[i].plug]) diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index e90f196a..a14631dd 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -62,7 +62,7 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { /// @param configs_ Array of configurations containing app gateway, network, plug, and switchboard details /// @dev Only callable by the contract owner /// @dev This helps in verifying that plugs are called by respective app gateways - function setAppGateways(AppGatewayConfig[] calldata configs_) external onlyOwner { + function setAppGateways(AppGatewayConfig[] calldata configs_) external onlyRole(WATCHER_ROLE) { for (uint256 i = 0; i < configs_.length; i++) { // Store the plug configuration for this network and plug _plugConfigs[configs_[i].chainSlug][configs_[i].plug] = PlugConfig({ diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index df8f0f54..284d012c 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -1,17 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; -import {Ownable} from "solady/auth/Ownable.sol"; +import {AccessControl} from "../utils/AccessControl.sol"; import {Gauge} from "../utils/Gauge.sol"; import {LimitParams, UpdateLimitParams} from "../utils/common/Structs.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {QUERY, FINALIZE, SCHEDULE} from "../utils/common/Constants.sol"; import "../../interfaces/IWatcherPrecompile.sol"; +import {WATCHER_ROLE} from "../utils/common/AccessRoles.sol"; abstract contract WatcherPrecompileLimits is Gauge, AddressResolverUtil, - Ownable, + AccessControl, IWatcherPrecompile { /// @notice Number of decimals used in limit calculations diff --git a/script/CheckDepositedFees.s.sol b/script/CheckDepositedFees.s.sol index 70934724..6d102510 100644 --- a/script/CheckDepositedFees.s.sol +++ b/script/CheckDepositedFees.s.sol @@ -11,7 +11,7 @@ contract CheckDepositedFees is Script { function run() external { vm.createSelectFork(vm.envString("EVMX_RPC")); FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); - address appGateway = vm.envAddress("APP_GATEWAY"); + address appGateway = address(0x31000ca8d07a26640cA16f1af1276C179A4F4741); (uint256 deposited, uint256 blocked) = feesManager.appGatewayFeeBalances( appGateway, diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 513c7ed0..216ca823 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -139,6 +139,9 @@ contract SetupTest is Test { addressResolver = AddressResolver(address(addressResolverProxy)); watcherPrecompile = WatcherPrecompile(address(watcherPrecompileProxy)); + hoax(watcherEOA); + watcherPrecompile.grantRole(WATCHER_ROLE, watcherEOA); + hoax(watcherEOA); addressResolver.setWatcherPrecompile(address(watcherPrecompile)); } From fab3f691c1c1137d23e28c363fe6888b77089cd5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 21 Feb 2025 15:44:10 +0400 Subject: [PATCH 2/3] script: grant watcher role to relayers --- hardhat-scripts/constants/relayers.ts | 22 ++++ hardhat-scripts/deploy/2.roles.ts | 153 +++++++++++++++++--------- 2 files changed, 121 insertions(+), 54 deletions(-) create mode 100644 hardhat-scripts/constants/relayers.ts diff --git a/hardhat-scripts/constants/relayers.ts b/hardhat-scripts/constants/relayers.ts new file mode 100644 index 00000000..1cbee517 --- /dev/null +++ b/hardhat-scripts/constants/relayers.ts @@ -0,0 +1,22 @@ +export const relayerAddressList = [ + "0x59D24CD3A0b2b646F980A62DCb0aA4115506FFc9", + "0x762783712FD12231601d6d8591F3a5718812D534", + "0x9f5C9941306E7B57583E99033BedB7eA7889EEA4", + "0xd35F262d6f2D3F6D610bAf6635dE57c349ca83A1", + "0xce6b9c352f864515d7E8BBEA67d80d18245937F2", + "0xE651eDb3F16D9e6b1145ae5eee388f0e48D2d44b", + "0xc176E88dE45747743699fAeA58814Ce98a2faC2D", + "0x88220692264EEc280Bd5AaF9278CdE0737Feb2D6", + "0x2d68551354226c7321130f122055F049F8F67791", + "0xA7C89619ceaC009a23c6C3bC5F74d41BBaC68fD1", + "0x7893D79718860DF30e5Fd21AAA6Be05edD22465D", + "0x6386c83e994331c6a41A4420294D130930AEDF9e", + "0xfF33A0afc88CbF48C9DB31Fc2D2EC2F36D598184", + "0xB756B6D986eE448433542A60A1f590EE3B0DFCda", + "0x76943F947D5622624444aeFF135a5121d6732299", + "0x049B750045fdE15F347aF9E86FB80dD879C848ea", + "0x9c653569C32473F40210495128BB5A40ef10c65B", + "0x0291a40beF28E3606b8208a665F900d141E9A8B3", + "0x2e498dFB44CC79D2ef80Afd7C4439BEC8822E216", + "0x049E701A690E885467E54A1B0595f90BDc8324B0" +] \ No newline at end of file diff --git a/hardhat-scripts/deploy/2.roles.ts b/hardhat-scripts/deploy/2.roles.ts index ba3dd802..5e54711b 100644 --- a/hardhat-scripts/deploy/2.roles.ts +++ b/hardhat-scripts/deploy/2.roles.ts @@ -10,9 +10,10 @@ dotenvConfig(); import { Wallet } from "ethers"; import { ethers } from "hardhat"; import dev_addresses from "../../deployments/dev_addresses.json"; -import { chains, watcher } from "../config"; -import { ChainAddressesObj } from "../constants"; +import { chains, EVMX_CHAIN_ID, watcher } from "../config"; +import { ChainAddressesObj, EVMxCoreContracts } from "../constants"; import { getInstance, getProviderFromChainSlug, getRoleHash } from "../utils"; +import { relayerAddressList } from "../constants/relayers"; export const REQUIRED_ROLES = { FastSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], @@ -21,63 +22,107 @@ export const REQUIRED_ROLES = { ContractFactoryPlug: [ROLES.RESCUE_ROLE], }; +async function setRoleForContract( + contractName: CORE_CONTRACTS | EVMxCoreContracts, + contractAddress: string | number, + targetAddress: string, + roleName: string, + signer: Wallet, + chain: number +) { + let contract = await getInstance(contractName, contractAddress.toString()); + contract = contract.connect(signer); + + console.log(`checking ${roleName} role for ${contractName} on ${chain}`); + const roleHash = getRoleHash(roleName); + const hasRole = await contract.callStatic["hasRole(bytes32,address)"]( + roleHash, + targetAddress, + { + from: signer.address, + } + ); + + if (!hasRole) { + let tx = await contract.grantRole(roleHash, targetAddress); + console.log( + `granting ${roleName} role to ${targetAddress} for ${contractName}`, + chain, + "txHash: ", + tx.hash + ); + await tx.wait(); + } +} + +async function getSigner(chain: number) { + const providerInstance = getProviderFromChainSlug(chain); + const signer: Wallet = new ethers.Wallet( + process.env.SOCKET_SIGNER_KEY as string, + providerInstance + ); + return signer; +} + + +async function setRolesForOnChain(chain: number, addresses: DeploymentAddresses) { + const chainAddresses: ChainAddressesObj = (addresses[chain] ?? {}) as ChainAddressesObj; + const signer = await getSigner(chain); + + for (const [contractName, roles] of Object.entries(REQUIRED_ROLES)) { + const contractAddress = chainAddresses[contractName as keyof ChainAddressesObj]; + if (!contractAddress) continue; + + for (const roleName of roles) { + const targetAddress = + contractName === CORE_CONTRACTS.FastSwitchboard && roleName === ROLES.WATCHER_ROLE + ? watcher + : signer.address; + + + await setRoleForContract( + contractName as CORE_CONTRACTS, + contractAddress, + targetAddress, + roleName, + signer, + chain + ); + } + } +} + +async function setRolesForEVMx(addresses: DeploymentAddresses) { + const chainAddresses: ChainAddressesObj = (addresses[EVMX_CHAIN_ID] ?? {}) as ChainAddressesObj; + const signer = await getSigner(EVMX_CHAIN_ID); + + const contractAddress = chainAddresses[EVMxCoreContracts.WatcherPrecompile]; + if (!contractAddress) return; + + for (const relayerAddress of relayerAddressList) { + console.log(`setting WATCHER_ROLE for ${relayerAddress} on EVMX`); + await setRoleForContract( + EVMxCoreContracts.WatcherPrecompile, + contractAddress, + relayerAddress, + ROLES.WATCHER_ROLE, + signer, + EVMX_CHAIN_ID + ); + } +} + export const main = async () => { - let addresses: DeploymentAddresses; try { console.log("Setting Roles"); - addresses = dev_addresses as unknown as DeploymentAddresses; + const addresses = dev_addresses as unknown as DeploymentAddresses; - for (const chain of chains) { - let chainAddresses: ChainAddressesObj = (addresses[chain] ?? - {}) as ChainAddressesObj; - const providerInstance = getProviderFromChainSlug(chain); - const signer: Wallet = new ethers.Wallet( - process.env.SOCKET_SIGNER_KEY as string, - providerInstance - ); + console.log("Setting Roles for EVMx"); + await setRolesForEVMx(addresses); - for (const [contractName, roles] of Object.entries(REQUIRED_ROLES)) { - const contractAddress = - chainAddresses[contractName as keyof ChainAddressesObj]; - if (!contractAddress) continue; - - let contract = await getInstance( - contractName as CORE_CONTRACTS, - String(contractAddress) - ); - contract = contract.connect(signer); - - for (const roleName of roles) { - console.log( - `checking ${roleName} role for ${contractName} on ${chain}` - ); - const targetAddress = - contractName === CORE_CONTRACTS.FastSwitchboard && - roleName === ROLES.WATCHER_ROLE - ? watcher - : signer.address; - - const roleHash = getRoleHash(roleName); - const hasRole = await contract.callStatic["hasRole(bytes32,address)"]( - roleHash, - targetAddress, - { - from: signer.address, - } - ); - - if (!hasRole) { - let tx = await contract.grantRole(roleHash, targetAddress); - console.log( - `granting ${roleName} role to ${targetAddress} for ${contractName}`, - chain, - "txHash: ", - tx.hash - ); - await tx.wait(); - } - } - } + console.log("Setting Roles for On Chain"); + for (const chain of chains) { + await setRolesForOnChain(chain, addresses); } } catch (error) { console.log("Error:", error); From cc30148a312621bab2e776a033d48d0dd1657b5e Mon Sep 17 00:00:00 2001 From: Rookmate <14072042+rookmate@users.noreply.github.com> Date: Fri, 21 Feb 2025 11:50:14 +0000 Subject: [PATCH 3/3] Re-add env CheckDepositedFees.s.sol --- script/CheckDepositedFees.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/CheckDepositedFees.s.sol b/script/CheckDepositedFees.s.sol index 6d102510..70934724 100644 --- a/script/CheckDepositedFees.s.sol +++ b/script/CheckDepositedFees.s.sol @@ -11,7 +11,7 @@ contract CheckDepositedFees is Script { function run() external { vm.createSelectFork(vm.envString("EVMX_RPC")); FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); - address appGateway = address(0x31000ca8d07a26640cA16f1af1276C179A4F4741); + address appGateway = vm.envAddress("APP_GATEWAY"); (uint256 deposited, uint256 blocked) = feesManager.appGatewayFeeBalances( appGateway,