Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions contracts/protocol/watcherPrecompile/WatcherPrecompile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -296,15 +296,15 @@ 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_);
}

/// @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];
Expand All @@ -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;

Expand Down Expand Up @@ -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])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
22 changes: 22 additions & 0 deletions hardhat-scripts/constants/relayers.ts
Original file line number Diff line number Diff line change
@@ -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"
]
153 changes: 99 additions & 54 deletions hardhat-scripts/deploy/2.roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions test/SetupTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
Loading