diff --git a/.gitignore b/.gitignore index cd4390f8..09276a6e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ **/.coverage_contracts **/dist **/node_modules -**/types **/.yarn # files diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..6442e186 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +version: "3.8" + +services: + mongodb: + image: mongo:latest + container_name: mongodb + ports: + - "27017:27017" + networks: + - mongo-network + volumes: + - mongo-data:/data/db + + hashi_relayer: + build: + context: . + dockerfile: packages/relayer/Dockerfile + container_name: hashi_relayer + networks: + - mongo-network + depends_on: + - mongodb + + hashi_executor: + build: + context: . + dockerfile: packages/executor/Dockerfile + container_name: hashi_executor + networks: + - mongo-network + depends_on: + - mongodb + + hashi_reporter: + build: + context: . + dockerfile: packages/reporter/Dockerfile + container_name: hashi_reporter + +networks: + mongo-network: + driver: bridge + +volumes: + mongo-data: + driver: local \ No newline at end of file diff --git a/packages/evm/.gitignore b/packages/evm/.gitignore new file mode 100644 index 00000000..bcbfbbf2 --- /dev/null +++ b/packages/evm/.gitignore @@ -0,0 +1 @@ +types/ \ No newline at end of file diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol index e9688db8..280fcefb 100644 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol +++ b/packages/evm/contracts/adapters/LayerZero/LayerZeroAdapter.sol @@ -3,33 +3,54 @@ pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ILayerZeroReceiver } from "./interfaces/ILayerZeroReceiver.sol"; +import { Origin } from "./interfaces/ILayerZeroEndpointV2.sol"; +import { OAppCore } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OAppCore.sol"; import { BlockHashAdapter } from "../BlockHashAdapter.sol"; -contract LayerZeroAdapter is BlockHashAdapter, Ownable, ILayerZeroReceiver { +contract LayerZeroAdapter is BlockHashAdapter, Ownable, ILayerZeroReceiver, OAppCore { string public constant PROVIDER = "layer-zero"; address public immutable LAYER_ZERO_ENDPOINT; - mapping(uint32 => bytes32) public enabledReportersPaths; + mapping(uint32 => address) public enabledReporters; mapping(uint32 => uint256) public chainIds; error UnauthorizedLayerZeroReceive(); - event ReporterSet(uint256 indexed chainId, uint16 indexed endpointId, address indexed reporter); + event ReporterSet(uint256 indexed chainId, uint32 indexed endpointId, address indexed reporter); - constructor(address lzEndpoint) { + constructor(address lzEndpoint, address delegate) OAppCore(lzEndpoint, delegate) { LAYER_ZERO_ENDPOINT = lzEndpoint; } - function lzReceive(uint16 srcEndpointId, bytes memory srcPath, uint64 /* nonce */, bytes memory payload) external { - if (msg.sender != LAYER_ZERO_ENDPOINT || enabledReportersPaths[srcEndpointId] != keccak256(srcPath)) - revert UnauthorizedLayerZeroReceive(); - uint256 sourceChainId = chainIds[srcEndpointId]; - (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(payload, (uint256[], bytes32[])); - _storeHashes(sourceChainId, ids, hashes); + function lzReceive( + Origin calldata _origin, + bytes32 _guid, + bytes calldata _message, + address _executor, + bytes calldata _extraData + ) external payable { + if ( + msg.sender != LAYER_ZERO_ENDPOINT || + enabledReporters[_origin.srcEid] != address(uint160(uint256(_origin.sender))) + ) revert UnauthorizedLayerZeroReceive(); + (uint256[] memory ids, bytes32[] memory hashes) = abi.decode(_message, (uint256[], bytes32[])); + _storeHashes(chainIds[_origin.srcEid], ids, hashes); } - function setReporterByChain(uint256 chainId, uint16 endpointId, address reporter) external onlyOwner { - enabledReportersPaths[endpointId] = keccak256(abi.encodePacked(reporter, address(this))); + function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public pure override returns (uint64 nonce) { + return 0; + } + + function allowInitializePath(Origin calldata origin) public view override returns (bool) { + return peers[origin.srcEid] == origin.sender; + } + + function oAppVersion() public pure virtual override returns (uint64 senderVersion, uint64 receiverVersion) { + return (1, 1); + } + + function setReporterByChain(uint256 chainId, uint32 endpointId, address reporter) external onlyOwner { + enabledReporters[endpointId] = reporter; chainIds[endpointId] = chainId; emit ReporterSet(chainId, endpointId, reporter); } diff --git a/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol b/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol index fc87b8af..69986c4d 100644 --- a/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol +++ b/packages/evm/contracts/adapters/LayerZero/LayerZeroReporter.sol @@ -2,54 +2,78 @@ pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { ILayerZeroEndpoint } from "./interfaces/ILayerZeroEndpoint.sol"; +import { ILayerZeroEndpointV2, MessagingParams, MessagingFee, MessagingReceipt } from "./interfaces/ILayerZeroEndpointV2.sol"; import { Reporter } from "../Reporter.sol"; +import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol"; +import { OAppCore } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OAppCore.sol"; + +contract LayerZeroReporter is Reporter, Ownable, OAppCore { + using OptionsBuilder for bytes; -contract LayerZeroReporter is Reporter, Ownable { string public constant PROVIDER = "layer-zero"; - ILayerZeroEndpoint public immutable LAYER_ZERO_ENDPOINT; + ILayerZeroEndpointV2 public immutable LAYER_ZERO_ENDPOINT; - mapping(uint256 => uint16) public endpointIds; - uint256 public fee; + mapping(uint256 => uint32) public endpointIds; + uint128 public fee; + address refundAddress; error EndpointIdNotAvailable(); - event EndpointIdSet(uint256 indexed chainId, uint16 indexed endpointId); + event EndpointIdSet(uint256 indexed chainId, uint32 indexed endpointId); event FeeSet(uint256 fee); - constructor(address headerStorage, address yaho, address lzEndpoint) Reporter(headerStorage, yaho) { - LAYER_ZERO_ENDPOINT = ILayerZeroEndpoint(lzEndpoint); + constructor( + address headerStorage, + address yaho, + address lzEndpoint, + address delegate, + address refundAddress_, + uint128 defaultFee_ + ) Reporter(headerStorage, yaho) OAppCore(lzEndpoint, delegate) { + refundAddress = refundAddress_; + fee = defaultFee_; + LAYER_ZERO_ENDPOINT = ILayerZeroEndpointV2(lzEndpoint); } - function setEndpointIdByChainId(uint256 chainId, uint16 endpointId) external onlyOwner { + function setEndpointIdByChainId(uint256 chainId, uint32 endpointId) external onlyOwner { endpointIds[chainId] = endpointId; emit EndpointIdSet(chainId, endpointId); } - function setFee(uint256 fee_) external onlyOwner { + function setFee(uint128 fee_) external onlyOwner { fee = fee_; emit FeeSet(fee); } + function setDefaultRefundAddress(address refundAddress_) external onlyOwner { + refundAddress = refundAddress_; + } + + function oAppVersion() public pure virtual override returns (uint64 senderVersion, uint64 receiverVersion) { + return (1, 1); + } function _dispatch( uint256 targetChainId, address adapter, uint256[] memory ids, bytes32[] memory hashes ) internal override returns (bytes32) { - uint16 targetEndpointId = endpointIds[targetChainId]; + uint32 targetEndpointId = endpointIds[targetChainId]; if (targetEndpointId == 0) revert EndpointIdNotAvailable(); - bytes memory payload = abi.encode(ids, hashes); - bytes memory path = abi.encodePacked(adapter, address(this)); - // solhint-disable-next-line check-send-result - LAYER_ZERO_ENDPOINT.send{ value: fee }( + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(fee, 0); + bytes memory message = abi.encode(ids, hashes); + MessagingParams memory params = MessagingParams( targetEndpointId, - path, - payload, - payable(msg.sender), // _refundAddress: refund address - address(0), // _zroPaymentAddress: future parameter - bytes("") // _adapterParams: adapterParams (see "Advanced Features") + bytes32(abi.encode(adapter)), + message, + options, + false // receiver in lz Token ); - return bytes32(0); + // solhint-disable-next-line check-send-result + MessagingFee memory msgFee = LAYER_ZERO_ENDPOINT.quote(params, address(this)); + MessagingReceipt memory receipt = LAYER_ZERO_ENDPOINT.send{ value: msgFee.nativeFee }(params, refundAddress); + return receipt.guid; } + + receive() external payable {} } diff --git a/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroEndpointV2.sol b/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroEndpointV2.sol new file mode 100644 index 00000000..6ced7ba9 --- /dev/null +++ b/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroEndpointV2.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0; + +struct MessagingParams { + uint32 dstEid; + bytes32 receiver; + bytes message; + bytes options; + bool payInLzToken; +} + +struct MessagingReceipt { + bytes32 guid; + uint64 nonce; + MessagingFee fee; +} + +struct MessagingFee { + uint256 nativeFee; + uint256 lzTokenFee; +} + +struct Origin { + uint32 srcEid; + bytes32 sender; + uint64 nonce; +} + +interface ILayerZeroEndpointV2 { + event PacketSent(bytes encodedPayload, bytes options, address sendLibrary); + + event PacketVerified(Origin origin, address receiver, bytes32 payloadHash); + + event PacketDelivered(Origin origin, address receiver); + + event LzReceiveAlert( + address indexed receiver, + address indexed executor, + Origin origin, + bytes32 guid, + uint256 gas, + uint256 value, + bytes message, + bytes extraData, + bytes reason + ); + + event LzTokenSet(address token); + + event DelegateSet(address sender, address delegate); + + function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory); + + function send( + MessagingParams calldata _params, + address _refundAddress + ) external payable returns (MessagingReceipt memory); + + function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external; + + function verifiable(Origin calldata _origin, address _receiver) external view returns (bool); + + function initializable(Origin calldata _origin, address _receiver) external view returns (bool); + + function lzReceive( + Origin calldata _origin, + address _receiver, + bytes32 _guid, + bytes calldata _message, + bytes calldata _extraData + ) external payable; + + // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order + function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external; + + function setLzToken(address _lzToken) external; + + function lzToken() external view returns (address); + + function nativeToken() external view returns (address); + + function setDelegate(address _delegate) external; +} diff --git a/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroReceiver.sol b/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroReceiver.sol index af1c7a87..124c24e3 100644 --- a/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroReceiver.sol +++ b/packages/evm/contracts/adapters/LayerZero/interfaces/ILayerZeroReceiver.sol @@ -1,11 +1,17 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.20; +import { Origin } from "./ILayerZeroEndpointV2.sol"; interface ILayerZeroReceiver { - // @notice LayerZero endpoint will invoke this function to deliver the message on the destination - // @param _srcChainId - the source endpoint identifier - // @param _srcAddress - the source sending contract address from the source chain - // @param _nonce - the ordered message nonce - // @param _payload - the signed payload is the UA bytes has encoded to be sent - function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external; + function lzReceive( + Origin calldata _origin, + bytes32 _guid, + bytes calldata _message, + address _executor, + bytes calldata _extraData + ) external payable; + + function allowInitializePath(Origin calldata _origin) external view returns (bool); + + function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64); } diff --git a/packages/evm/package.json b/packages/evm/package.json index 0c6bfec9..63284f03 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -18,6 +18,7 @@ "@nomicfoundation/hardhat-chai-matchers": "^1.0.4", "@nomicfoundation/hardhat-network-helpers": "^1.0.8", "@nomicfoundation/hardhat-toolbox": "^2.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.8", "@nomiclabs/hardhat-ethers": "^2.2.1", "@nomiclabs/hardhat-etherscan": "^3.1.2", "@trivago/prettier-plugin-sort-imports": "^4.0.0", @@ -93,10 +94,14 @@ "@chainlink/contracts-ccip": "0.7.6", "@connext/interfaces": "2.0.0", "@hyperlane-xyz/core": "3.1.10", + "@layerzerolabs/lz-evm-messagelib-v2": "^2.3.29", + "@layerzerolabs/lz-evm-oapp-v2": "^2.3.29", + "@layerzerolabs/lz-evm-protocol-v2": "^2.3.29", "@openzeppelin/contracts-upgradeable": "4.8.1", "@polytope-labs/ismp-solidity": "^0.7.1", "@polytope-labs/solidity-merkle-trees": "0.3.2", "@routerprotocol/evm-gateway-contracts": "1.1.13", + "solidity-bytes-utils": "^0.8.2", "solidity-rlp": "2.0.8" } } diff --git a/packages/evm/tasks/deploy/adapters/layerzero.ts b/packages/evm/tasks/deploy/adapters/layerzero.ts index aed9a324..b2cd195d 100644 --- a/packages/evm/tasks/deploy/adapters/layerzero.ts +++ b/packages/evm/tasks/deploy/adapters/layerzero.ts @@ -9,7 +9,8 @@ import type { LayerZeroReporter__factory } from "../../../types/factories/contra import { verify } from "../index" task("deploy:LayerZeroAdapter") - .addParam("lzEndpoint", "address of the LayerZero endpoint contract") + .addParam("lzendpoint", "address of the LayerZero endpoint contract") + .addParam("delegate", "address of delegate") .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying LayerZeroAdapter...") @@ -17,7 +18,7 @@ task("deploy:LayerZeroAdapter") const layerZeroAdapterFactory: LayerZeroAdapter__factory = ( await hre.ethers.getContractFactory("LayerZeroAdapter") ) - const constructorArguments = [taskArguments.lzEndpoint] as const + const constructorArguments = [taskArguments.lzendpoint, taskArguments.delegate] as const const layerZeroAdapter: LayerZeroAdapter = ( await layerZeroAdapterFactory.connect(signers[0]).deploy(...constructorArguments) ) @@ -27,9 +28,12 @@ task("deploy:LayerZeroAdapter") }) task("deploy:LayerZeroReporter") - .addParam("headerStorage", "address of the header storage contract") + .addParam("headerstorage", "address of the header storage contract") .addParam("yaho", "address of the Yaho contract") - .addParam("lzEndpoint", "address of the LayerZero endpoint contract") + .addParam("lzendpoint", "address of the LayerZero endpoint contract") + .addParam("delegate", "address of delegate") + .addParam("refundaddress", "refund address") + .addParam("defaultfee", "default fee") .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying LayerZeroReporter...") @@ -37,7 +41,14 @@ task("deploy:LayerZeroReporter") const layerZeroReporterFactory: LayerZeroReporter__factory = ( await hre.ethers.getContractFactory("LayerZeroReporter") ) - const constructorArguments = [taskArguments.headerStorage, taskArguments.yaho, taskArguments.lzEndpoint] as const + const constructorArguments = [ + taskArguments.headerstorage, + taskArguments.yaho, + taskArguments.lzendpoint, + taskArguments.delegate, + taskArguments.refundaddress, + taskArguments.defaultfee, + ] as const const layerZeroReporter: LayerZeroReporter = ( await layerZeroReporterFactory.connect(signers[0]).deploy(...constructorArguments) ) diff --git a/packages/evm/tasks/deploy/adapters/wormhole.ts b/packages/evm/tasks/deploy/adapters/wormhole.ts index b61ed770..5dd59e2f 100644 --- a/packages/evm/tasks/deploy/adapters/wormhole.ts +++ b/packages/evm/tasks/deploy/adapters/wormhole.ts @@ -4,18 +4,13 @@ import type { TaskArguments } from "hardhat/types" import { verify } from ".." import type { WormholeAdapter } from "../../../types/contracts/adapters/Wormhole/WormholeAdapter" -import type { WormholeHeaderReporter } from "../../../types/contracts/adapters/Wormhole/WormholeHeaderReporter" -import type { WormholeMessageRelay } from "../../../types/contracts/adapters/Wormhole/WormholeMessageRelay" +import type { WormholeReporter } from "../../../types/contracts/adapters/Wormhole/WormholeReporter" import type { WormholeAdapter__factory } from "../../../types/factories/contracts/adapters/Wormhole/WormholeAdapter__factory" -import type { WormholeHeaderReporter__factory } from "../../../types/factories/contracts/adapters/Wormhole/WormholeHeaderReporter__factory" -import type { WormholeMessageRelay__factory } from "../../../types/factories/contracts/adapters/Wormhole/WormholeMessageRelay__factory" +import type { WormholeReporter__factory } from "../../../types/factories/contracts/adapters/Wormhole/WormholeReporter__factory" // Deploy on destination chain task("deploy:Wormhole:Adapter") .addParam("wormhole", "address of the Wormhole contract", undefined, types.string) - .addParam("reporter", "address of the hash reporter", undefined, types.string) - .addParam("chainId", "source chain id", undefined, types.string) - .addParam("wormholeChainId", "wormhole source chain id", undefined, types.string) .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying WormholeAdapter...") @@ -23,12 +18,7 @@ task("deploy:Wormhole:Adapter") const wormholeAdapterFactory: WormholeAdapter__factory = ( await hre.ethers.getContractFactory("WormholeAdapter") ) - const constructorArguments = [ - taskArguments.wormhole, - taskArguments.reporter, - taskArguments.chainId, - taskArguments.wormholeChainId, - ] as const + const constructorArguments = [taskArguments.wormhole] as const const wormholeAdapter: WormholeAdapter = ( await wormholeAdapterFactory.connect(signers[0]).deploy(...constructorArguments) ) @@ -38,41 +28,22 @@ task("deploy:Wormhole:Adapter") }) // Deploy source chain -task("deploy:Wormhole:HeaderReporter") - .addParam("wormhole", "address of the Wormhole contract", undefined, types.string) - .addParam("headerStorage", "address of the header storage contract", undefined, types.string) - .addFlag("verify", "whether to verify the contract on Etherscan") - .setAction(async function (taskArguments: TaskArguments, hre) { - console.log("Deploying WormholeHeaderReporter...") - const signers: SignerWithAddress[] = await hre.ethers.getSigners() - const wormholeHeaderReporterFactory: WormholeHeaderReporter__factory = ( - await hre.ethers.getContractFactory("WormholeHeaderReporter") - ) - const constructorArguments = [taskArguments.wormhole, taskArguments.headerStorage] as const - const wormholeHeaderReporter: WormholeHeaderReporter = ( - await wormholeHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments) - ) - await wormholeHeaderReporter.deployed() - console.log("WormholeHeaderReporter deployed to:", wormholeHeaderReporter.address) - if (taskArguments.verify) await verify(hre, wormholeHeaderReporter, constructorArguments) - }) - -// Deploy source chain -task("deploy:Wormhole:MessageRelay") - .addParam("wormhole", "address of the Wormhole contract", undefined, types.string) +task("deploy:Wormhole:Reporter") .addParam("yaho", "address of the Yaho contract", undefined, types.string) + .addParam("wormhole", "address of the Wormhole contract", undefined, types.string) + .addParam("headerstorage", "address of the header storage contract", undefined, types.string) .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { - console.log("Deploying WormholeMessageRelay...") + console.log("Deploying WormholeReporter...") const signers: SignerWithAddress[] = await hre.ethers.getSigners() - const wormholeMessageRelayFactory: WormholeMessageRelay__factory = ( - await hre.ethers.getContractFactory("WormholeMessageRelay") + const wormholeReporterFactory: WormholeReporter__factory = ( + await hre.ethers.getContractFactory("WormholeReporter") ) - const constructorArguments = [taskArguments.wormhole, taskArguments.yaho] as const - const wormholeMessageRelay: WormholeMessageRelay = ( - await wormholeMessageRelayFactory.connect(signers[0]).deploy(...constructorArguments) + const constructorArguments = [taskArguments.headerstorage, taskArguments.yaho, taskArguments.wormhole] as const + const wormholeReporter: WormholeReporter = ( + await wormholeReporterFactory.connect(signers[0]).deploy(...constructorArguments) ) - await wormholeMessageRelay.deployed() - console.log("WormholeMessageRelay deployed to:", wormholeMessageRelay.address) - if (taskArguments.verify) await verify(hre, wormholeMessageRelay, constructorArguments) + await wormholeReporter.deployed() + console.log("WormholeHeaderReporter deployed to:", wormholeReporter.address) + if (taskArguments.verify) await verify(hre, wormholeReporter, constructorArguments) }) diff --git a/packages/executor/Dockerfile b/packages/executor/Dockerfile new file mode 100644 index 00000000..9c01c83b --- /dev/null +++ b/packages/executor/Dockerfile @@ -0,0 +1,23 @@ +FROM node:18-alpine + +WORKDIR /usr/src/app + +# Copy root package.json and yarn.lock for dependency resolution +COPY ../../package.json ../../yarn.lock ./ + +# Copy both executor and common package folders +COPY ./packages/common ./packages/common +COPY ./packages/executor ./packages/executor + + +# Install dependencies at root level +RUN yarn install + +# Compile both common and executor packages +WORKDIR /usr/src/app/packages/common +RUN yarn compile + +WORKDIR /usr/src/app/packages/executor +RUN yarn compile + +CMD ["yarn", "start"] \ No newline at end of file diff --git a/packages/executor/README.md b/packages/executor/README.md index c9829440..0a0d0449 100644 --- a/packages/executor/README.md +++ b/packages/executor/README.md @@ -1,6 +1,7 @@ # executor -The Executor is a service utilized to execute messages once they have achieved consensus, meaning when adapters have reached consensus on the message. +The Executor is a service utilized to execute messages once they have achieved consensus, meaning when adapters have +reached consensus on the message.   @@ -9,6 +10,7 @@ The Executor is a service utilized to execute messages once they have achieved c   ## Installation + To install the Executor, follow these steps: ```bash @@ -46,3 +48,32 @@ cd packages/executor yarn start dotenv_config_path="your env file" ``` +### Building and Running the Docker Image + +Executor is usually run with Relayer and MongoDB, the `docker-compose.yml` demonstrates how to run these three images +together. + +Run the following command: + +```sh +cd ../.. # To the root level +docker compose up --build mongodb hashi_executor +``` + +### Viewing Logs + +To view the logs from the running container, use: + +```sh +docker logs -f [CONTAINER_ID or CONTAINER_NAME] +``` + +You can find the `CONTAINER_ID` or `CONTAINER_NAME` using `docker ps`. + +### Stopping the relayer + +To stop the running container: + +```sh +docker stop [CONTAINER_ID or CONTAINER_NAME] +``` diff --git a/packages/executor/package.json b/packages/executor/package.json index 8af52a07..39e31722 100644 --- a/packages/executor/package.json +++ b/packages/executor/package.json @@ -12,14 +12,14 @@ "lint": "eslint --ignore-path ./.eslintignore --ext .js,.ts .", "prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yml}\"", "prettier:write": "prettier --write \"**/*.{js,json,md,sol,ts,yml}\"", - "start": "ts-node --esm ./src/index.ts", + "start": "node --loader ts-node/esm ./src/index.ts", "start:dev": "nodemon" }, "dependencies": { "@gnosis/hashi-common": "0.1.0", "dotenv": "^16.3.1", "mongodb": "6.5.0", - "viem": "^2.9.28", + "viem": "2.20.0", "winston": "^3.11.0" }, "devDependencies": { diff --git a/packages/executor/src/index.ts b/packages/executor/src/index.ts index d5217b3a..668b89ae 100644 --- a/packages/executor/src/index.ts +++ b/packages/executor/src/index.ts @@ -1,8 +1,8 @@ -import 'dotenv/config' -import { createWalletClient, http, Chain, publicActions, Log } from "viem" +import "dotenv/config" +import { createWalletClient, http, Chain, publicActions, Log, createPublicClient } from "viem" import { privateKeyToAccount } from "viem/accounts" import * as chains from "viem/chains" -import { MongoClient} from "mongodb" +import { MongoClient } from "mongodb" import { adapterAbi, Batcher, logger, Message, Watcher, yahoAbi, yaruAbi } from "@gnosis/hashi-common" const mongoClient = new MongoClient(process.env.MONGO_DB_URI as string) @@ -12,13 +12,14 @@ const db = mongoClient.db("hashi") const sourceChain = Object.values(chains).find(({ id }) => id.toString() === (process.env.SOURCE_CHAIN_ID as string)) if (!sourceChain) throw new Error("Invalid SOURCE_CHAIN_ID") const targetChain = Object.values(chains).find(({ id }) => id.toString() === (process.env.TARGET_CHAIN_ID as string)) -if (!sourceChain) throw new Error("Invalid TARGET_CHAIN_ID") +if (!targetChain) throw new Error("Invalid TARGET_CHAIN_ID") const sourceClient = createWalletClient({ account: privateKeyToAccount(process.env.PK as `0x${string}`), chain: sourceChain as Chain | undefined, transport: http(process.env.SOURCE_RPC as string), }).extend(publicActions) + const targetClient = createWalletClient({ account: privateKeyToAccount(process.env.PK as `0x${string}`), chain: targetChain as Chain | undefined, @@ -37,7 +38,11 @@ const watchers = adapters.map( service: `ExecutorWatcher:${_adapter.slice(0, 6)}${_adapter.slice(_adapter.length - 4, _adapter.length)}`, watchIntervalTimeMs: Number(process.env.WATCH_INTERVAL_TIME_MS as string), onLogs: async (_logs: Log[]) => { - const messageIds = _logs.map((_log) => _log.topics[1]) + const messageIds = _logs + .map((_log) => _log.topics[1]) + .filter((id): id is `0x${string}` => id !== undefined) // Filter out undefined values + .map((id) => BigInt(id)) // Convert each string to bigint + // NOTE: without setting fromBlock and toBlock, it's not possible to getContractEvents const blockNumber = await sourceClient.getBlockNumber() const messageDispatchedLogs = await sourceClient.getContractEvents({ @@ -49,8 +54,9 @@ const watchers = adapters.map( }, fromBlock: blockNumber - BigInt(process.env.BLOCKS_WINDOW as string), toBlock: blockNumber, + strict: true, }) - + logger.info(`Found corresponding ${messageDispatchedLogs.length} message dispatch logs`) const messages = messageDispatchedLogs.map((_log) => Message.fromLog(_log)) for (const message of messages) { const hashStoredLog = _logs.find((_log) => _log.topics[1] === message.id) as Log diff --git a/packages/executor/tsconfig.json b/packages/executor/tsconfig.json index d69a137b..974c23aa 100644 --- a/packages/executor/tsconfig.json +++ b/packages/executor/tsconfig.json @@ -6,21 +6,31 @@ "esModuleInterop": true, "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "lib": ["es6", "ESNext", "DOM"], + "lib": [ + "es6", + "ESNext", + "DOM" + ], "module": "ESNext", "moduleResolution": "node", "noImplicitAny": true, "outDir": "./dist", + "rootDir": "./src", "removeComments": true, "resolveJsonModule": true, "sourceMap": true, "strict": true, - "target": "ESNext" + "target": "ESNext", + "skipLibCheck": true, }, - "exclude": ["node_modules"], - "include": ["./src/**/*"], + "exclude": [ + "node_modules" + ], + "include": [ + "./src/**/*" + ], "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" } -} +} \ No newline at end of file diff --git a/packages/relayer/.env.example b/packages/relayer/.env.example index 5f80c130..3380a0fd 100644 --- a/packages/relayer/.env.example +++ b/packages/relayer/.env.example @@ -3,6 +3,7 @@ CHAIN_ID= RPC= PK= WATCH_INTERVAL_TIME_MS= +CREATE_BATCH_INTERVAL_TIME_MS= MONGO_DB_URI= MIN_BATCH_SIZE= WHITELISTED_SENDER_ADDRESSES= \ No newline at end of file diff --git a/packages/relayer/Dockerfile b/packages/relayer/Dockerfile new file mode 100644 index 00000000..aec8ea25 --- /dev/null +++ b/packages/relayer/Dockerfile @@ -0,0 +1,23 @@ +FROM node:18-alpine + +WORKDIR /usr/src/app + +# Copy root package.json and yarn.lock for dependency resolution +COPY ../../package.json ../../yarn.lock ./ + +# Copy both executor and common package folders +COPY ./packages/common ./packages/common +COPY ./packages/relayer ./packages/relayer + + +# Install dependencies at root level +RUN yarn install + +# Compile both common and executor packages +WORKDIR /usr/src/app/packages/common +RUN yarn compile + +WORKDIR /usr/src/app/packages/relayer +RUN yarn compile + +CMD ["yarn", "start"] \ No newline at end of file diff --git a/packages/relayer/README.md b/packages/relayer/README.md index da4019d1..812afc57 100644 --- a/packages/relayer/README.md +++ b/packages/relayer/README.md @@ -46,3 +46,33 @@ cd packages/relayer ```bash yarn start dotenv_config_path="your env file" ``` + +### Building and Running the Docker Image + +Relayer is usually run with Executor and MongoDB, the `docker-compose.yml` demonstrates how to run these three images +together. + +Run the following command: + +```sh +cd ../.. # To the root level +docker compose up --build mongodb hashi_relayer +``` + +### Viewing Logs + +To view the logs from the running container, use: + +```sh +docker logs -f [CONTAINER_ID or CONTAINER_NAME] +``` + +You can find the `CONTAINER_ID` or `CONTAINER_NAME` using `docker ps`. + +### Stopping the relayer + +To stop the running container: + +```sh +docker stop [CONTAINER_ID or CONTAINER_NAME] +``` diff --git a/packages/relayer/package.json b/packages/relayer/package.json index 33ab6a51..fbc166b2 100644 --- a/packages/relayer/package.json +++ b/packages/relayer/package.json @@ -12,7 +12,7 @@ "lint": "eslint --ignore-path ./.eslintignore --ext .js,.ts .", "prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yml}\"", "prettier:write": "prettier --write \"**/*.{js,json,md,sol,ts,yml}\"", - "start": "ts-node --esm ./src/index.ts", + "start": "node --loader ts-node/esm ./src/index.ts", "start:dev": "nodemon" }, "dependencies": { diff --git a/packages/relayer/tsconfig.json b/packages/relayer/tsconfig.json index d167b40a..652aa407 100644 --- a/packages/relayer/tsconfig.json +++ b/packages/relayer/tsconfig.json @@ -19,7 +19,8 @@ "resolveJsonModule": true, "sourceMap": true, "strict": true, - "target": "ESNext" + "target": "ESNext", + "skipLibCheck": true, }, "exclude": [ "node_modules" diff --git a/packages/reporter/.dockerignore b/packages/reporter/.dockerignore new file mode 100644 index 00000000..5f7b077a --- /dev/null +++ b/packages/reporter/.dockerignore @@ -0,0 +1,7 @@ +node_modules +npm - debug.log +Dockerfile + .git + .gitignore + .yarn +logs diff --git a/packages/reporter/.env.example b/packages/reporter/.env.example new file mode 100644 index 00000000..8ff12943 --- /dev/null +++ b/packages/reporter/.env.example @@ -0,0 +1,61 @@ +# Fake private key +PRIVATE_KEY= + +REPORTERS_ENABLED=WormholeReporterController,AMBReporterController,LayerZeroReporterController +GNOSIS_RPC_URL=https://rpc.ankr.com/gnosis +MAINNET_RPC_URL=https://eth.llamarpc.com +BSC_RPC_URL=https://bsc-dataseed1.binance.org +GOERLI_RPC_URL= +POLYGON_RPC_URL= +OPTIMISM_RPC_URL=https://optimism.llamarpc.com +ARBITRUM_RPC_URL=https://arbitrum.llamarpc.com +BASE_RPC_URL= +SEPOLIA_RPC_URL= +CHIADO_RPC_URL=https://rpc.chiadochain.net +GOERLI_BEACON_API_URL= + +AMB_REPORTER_HEADERS_GAS=200000 +AXELAR_REPORT_HEADERS_VALUE=0.0001 +CELER_REPORT_HEADERS_VALUE=0.0001 +LAYER_ZERO_REPORT_HEADERS_VALUE=0.00015 +CCIP_REPORT_HEADERS_VALUE=0.002 +CONNEXT_REPORT_HEADERS_VALUE=0.00003 +ZETA_CHAIN_REPORT_HEADERS_VALUE=0.001 + +TELEPATHY_PROOF_API_URL= +TELEPATHY_BLOCK_BUFFER=10 +TELEPATHY_INTERVAL_UPDATE=30000 +ELECTRON_INTERVAL_UPDATE=30000 + +# WORMHOLE_SCAN_BASE_URL=https://api.wormholescan.io # Mainnet +WORMHOLE_SCAN_BASE_URL=https://api.testnet.wormholescan.io/ # Testnet + + +TIME_FETCH_BLOCKS_MS=30000 +BLOCK_BUFFER=10 +QUERY_BLOCK_LENGTH=200 + + + +SOURCE_CHAIN_ID=8453 +DESTINATION_CHAIN_IDS=42161,10,56,137,100 + + +# Mainnet +## Layer Zero + + +GNOSIS_LZ_ADAPTER=0x6602dc9b6bd964C2a11BBdA9B2275308D1Bbc14f +POLYGON_LZ_ADAPTER=0xf4C84D9ced01534f235078A10E8A44A726c4b73c +BSC_LZ_ADAPTER=0x97761F61736Ec2D108a1b6826f5Ee2E02d1B333e +OPTIMISM_LZ_ADAPTER=0x746dfa0251A31e587E97bBe0c58ED67A343280Df +ARBITRUM_LZ_ADAPTER=0x97761F61736Ec2D108a1b6826f5Ee2E02d1B333e +BASE_LZ_ADAPTER=0x5F98c418C10132aA4D1b3c98cE4F68Ef2435e4eC + + +GNOSIS_LZ_REPORTER=0xA3Bc83D557E3f2dDfF4D44966A96397760159D8B +OPTIMISM_LZ_REPORTER=0xB866C6dD03434d8fA792C471b454cb4E72ca35dc +ARBITRUM_LZ_REPORTER=0xf4C84D9ced01534f235078A10E8A44A726c4b73c +POLYGON_LZ_REPORTER=0x231e48AAEaAC6398978a1dBA4Cd38fcA208Ec391 +BSC_LZ_REPORTER=0xc9618e4d4B59570Da67b4fb0E8fC7EB40A5f8462 +BASE_LZ_REPORTER=0x495b872b329eba69F81A749f8A152766851C23b0 diff --git a/packages/reporter/.eslintignore b/packages/reporter/.eslintignore new file mode 100644 index 00000000..b68087da --- /dev/null +++ b/packages/reporter/.eslintignore @@ -0,0 +1,2 @@ +**/dist +**/node_modules \ No newline at end of file diff --git a/packages/reporter/.eslintrc.yml b/packages/reporter/.eslintrc.yml new file mode 100644 index 00000000..27672a35 --- /dev/null +++ b/packages/reporter/.eslintrc.yml @@ -0,0 +1,21 @@ +extends: + - "eslint:recommended" + - "plugin:@typescript-eslint/eslint-recommended" + - "plugin:@typescript-eslint/recommended" + - "prettier" +parser: "@typescript-eslint/parser" +parserOptions: + project: "tsconfig.json" +plugins: + - "@typescript-eslint" +root: true +rules: + "@typescript-eslint/no-floating-promises": + - error + - ignoreIIFE: true + ignoreVoid: true + "@typescript-eslint/no-inferrable-types": "off" + "@typescript-eslint/no-unused-vars": + - error + - argsIgnorePattern: "_" + varsIgnorePattern: "_" diff --git a/packages/reporter/.gitignore b/packages/reporter/.gitignore new file mode 100644 index 00000000..5292519a --- /dev/null +++ b/packages/reporter/.gitignore @@ -0,0 +1 @@ +logs/ \ No newline at end of file diff --git a/packages/reporter/.nvmrc b/packages/reporter/.nvmrc new file mode 100644 index 00000000..0828ab79 --- /dev/null +++ b/packages/reporter/.nvmrc @@ -0,0 +1 @@ +v18 \ No newline at end of file diff --git a/packages/reporter/.prettierignore b/packages/reporter/.prettierignore new file mode 100644 index 00000000..b68087da --- /dev/null +++ b/packages/reporter/.prettierignore @@ -0,0 +1,2 @@ +**/dist +**/node_modules \ No newline at end of file diff --git a/packages/reporter/.prettierrc.yml b/packages/reporter/.prettierrc.yml new file mode 100644 index 00000000..e9bada2e --- /dev/null +++ b/packages/reporter/.prettierrc.yml @@ -0,0 +1,7 @@ +bracketSpacing: true +printWidth: 120 +proseWrap: "always" +singleQuote: false +tabWidth: 2 +trailingComma: "all" +semi: false diff --git a/packages/reporter/.yarnrc.yml b/packages/reporter/.yarnrc.yml new file mode 100644 index 00000000..3186f3f0 --- /dev/null +++ b/packages/reporter/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/packages/reporter/Dockerfile b/packages/reporter/Dockerfile new file mode 100644 index 00000000..cdca8796 --- /dev/null +++ b/packages/reporter/Dockerfile @@ -0,0 +1,15 @@ +FROM node:18-alpine + +WORKDIR /usr/src/app + +# Copy root package.json and yarn.lock for dependency resolution +COPY ../../package.json ../../yarn.lock ./ + +COPY ./packages/reporter ./packages/reporter + +RUN yarn install + +WORKDIR /usr/src/app/packages/reporter +RUN yarn compile + +CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/packages/reporter/README.md b/packages/reporter/README.md new file mode 100644 index 00000000..68e80f2c --- /dev/null +++ b/packages/reporter/README.md @@ -0,0 +1,58 @@ +# reporter + +Script to call Header Reporter contracts of different oracle from source chain to destination chain. + +  + +--- + +  + +## Getting Started + +These instructions will cover the usage information and how to run the code using Docker. + +### Create the .env file + +Configure the mode you want to run by editing the variable in `.env` by checking `.env.example` + +### Building and Running the Docker Image + +On the root's `docker-compose.yml`, run the following command: + +```sh +cd ../.. # To the root level +docker-compose up -d --build --no-deps hashi_reporter +``` + +### Viewing Logs + +To view the logs from the running container, use: + +```sh +docker logs -f [CONTAINER_ID or CONTAINER_NAME] +``` + +You can find the `CONTAINER_ID` or `CONTAINER_NAME` using `docker ps`. + +### Stopping the reporter + +To stop the running container: + +```sh +docker stop [CONTAINER_ID or CONTAINER_NAME] +``` + +  + +--- + +  + +## How to add a new controller + +1. Add a new file under `/controllers`, create the constructor and `onBlocks` function to call block header reporter + contract periodically. +2. Configure the settings under `settings/index.ts`. +3. Add the new controller instant in `index.ts`. +4. Add the env variable in `.env.example`. diff --git a/packages/reporter/nodemon.json b/packages/reporter/nodemon.json new file mode 100644 index 00000000..06dbc7b5 --- /dev/null +++ b/packages/reporter/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts,json", + "ignore": ["src/**/*.spec.ts"], + "exec": "ts-node --esm ./src/index.ts" +} diff --git a/packages/reporter/package.json b/packages/reporter/package.json new file mode 100644 index 00000000..b8cfb84f --- /dev/null +++ b/packages/reporter/package.json @@ -0,0 +1,43 @@ +{ + "name": "reporter", + "version": "0.1.0", + "author": { + "name": "gnosis", + "url": "https://github.com/gnosis" + }, + "type": "module", + "scripts": { + "compile": "tsc", + "lint": "eslint --ignore-path ./.eslintignore --ext .js,.ts .", + "prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yml}\"", + "prettier:write": "prettier --write \"**/*.{js,json,md,sol,ts,yml}\"", + "start:dev": "nodemon" + }, + "dependencies": { + "@chainsafe/persistent-merkle-tree": "^0.6.1", + "@chainsafe/ssz": "^0.14.0", + "@ethereumjs/rlp": "^5.0.1", + "@ethereumjs/trie": "^6.0.1", + "@ethereumjs/util": "^9.0.1", + "@ethereumjs/vm": "^7.1.0", + "@lodestar/types": "^1.12.0", + "async-mutex": "^0.4.0", + "axios": "^1.5.1", + "dotenv": "^16.3.1", + "viem": "^1.16.5", + "winston": "^3.11.0" + }, + "devDependencies": { + "@types/node": "^20.8.9", + "@types/node-cron": "^3.0.9", + "@typescript-eslint/eslint-plugin": "^6.7.5", + "@typescript-eslint/parser": "^6.7.5", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "nodemon": "^3.0.1", + "patch-package": "^8.0.0", + "prettier": "^3.0.3", + "ts-node": "^10.9.2", + "typescript": "~5.2.2" + } +} diff --git a/packages/reporter/src/ABIs/StandardReporterContractABI.json b/packages/reporter/src/ABIs/StandardReporterContractABI.json new file mode 100644 index 00000000..c89a25b3 --- /dev/null +++ b/packages/reporter/src/ABIs/StandardReporterContractABI.json @@ -0,0 +1,169 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedYaho", + "type": "address" + } + ], + "name": "NotYaho", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "blockHeader", + "type": "bytes32" + } + ], + "name": "BlockDispatched", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "messageId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "messageHash", + "type": "bytes32" + } + ], + "name": "MessageDispatched", + "type": "event" + }, + { + "inputs": [], + "name": "HEADER_STORAGE", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "YAHO", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "blockNumbers", + "type": "uint256[]" + } + ], + "name": "dispatchBlocks", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "messageIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "messageHashes", + "type": "bytes32[]" + } + ], + "name": "dispatchMessages", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/packages/reporter/src/ABIs/WormholeABI.json b/packages/reporter/src/ABIs/WormholeABI.json new file mode 100644 index 00000000..d1b59b6a --- /dev/null +++ b/packages/reporter/src/ABIs/WormholeABI.json @@ -0,0 +1,9 @@ +[ + { + "inputs": [{ "internalType": "address", "name": "emitter", "type": "address" }], + "name": "nextSequence", + "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/packages/reporter/src/ABIs/WormholeAdapterABI.json b/packages/reporter/src/ABIs/WormholeAdapterABI.json new file mode 100644 index 00000000..dc59d307 --- /dev/null +++ b/packages/reporter/src/ABIs/WormholeAdapterABI.json @@ -0,0 +1,375 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "wormhole", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "storedBlockHash", + "type": "bytes32" + } + ], + "name": "ConflictingBlockHeader", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "InvalidBlockHeaderLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidBlockHeaderRLP", + "type": "error" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "emitterChainId", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "emitterAddress", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "sequence", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "consistencyLevel", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "guardianSetIndex", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "guardianIndex", + "type": "uint8" + } + ], + "internalType": "struct Signature[]", + "name": "signatures", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "internalType": "struct VM", + "name": "vm", + "type": "tuple" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidMessage", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedWormholeReceive", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "HashStored", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "endpointId", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "reporter", + "type": "address" + } + ], + "name": "ReporterSet", + "type": "event" + }, + { + "inputs": [], + "name": "WORMHOLE", + "outputs": [ + { + "internalType": "contract IWormhole", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "name": "chainIds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "name": "enabledReporters", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "domain", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "blockHeaders", + "type": "bytes[]" + } + ], + "name": "proveAncestralBlockHashes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "wormholeChainId", + "type": "uint16" + }, + { + "internalType": "address", + "name": "reporter", + "type": "address" + } + ], + "name": "setReporterByChain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedVM", + "type": "bytes" + } + ], + "name": "storeHashesByEncodedVM", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/reporter/src/ABIs/WormholeReporterABI.json b/packages/reporter/src/ABIs/WormholeReporterABI.json new file mode 100644 index 00000000..454cd261 --- /dev/null +++ b/packages/reporter/src/ABIs/WormholeReporterABI.json @@ -0,0 +1,216 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "headerStorage", + "type": "address" + }, + { + "internalType": "address", + "name": "yaho", + "type": "address" + }, + { + "internalType": "address", + "name": "wormhole", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedYaho", + "type": "address" + } + ], + "name": "NotYaho", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "blockHeader", + "type": "bytes32" + } + ], + "name": "BlockDispatched", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "messageId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "messageHash", + "type": "bytes32" + } + ], + "name": "MessageDispatched", + "type": "event" + }, + { + "inputs": [], + "name": "HEADER_STORAGE", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROVIDER", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WOMRHOLE", + "outputs": [ + { + "internalType": "contract IWormhole", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "YAHO", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "blockNumbers", + "type": "uint256[]" + } + ], + "name": "dispatchBlocks", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "targetChainId", + "type": "uint256" + }, + { + "internalType": "contract IAdapter", + "name": "adapter", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "messageIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "messageHashes", + "type": "bytes32[]" + } + ], + "name": "dispatchMessages", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/packages/reporter/src/Coordinator.ts b/packages/reporter/src/Coordinator.ts new file mode 100644 index 00000000..4a5be932 --- /dev/null +++ b/packages/reporter/src/Coordinator.ts @@ -0,0 +1,111 @@ +import { Chain } from "viem" +import winston from "winston" +import { Mutex } from "async-mutex" + +import Multiclient from "./MultiClient.js" +import BaseController from "./controllers/BaseController.js" + +interface BlockListenerConfigs { + controllers: any[] + logger: winston.Logger + intervalFetchBlocksMs: number + intervalsUpdateLightClients: { [controllerName: string]: number } + multiclient: Multiclient + sourceChain: Chain + queryBlockLength: number + blockBuffer: number +} + +class Coordinator { + controllers: BaseController[] + logger: winston.Logger + multiclient: Multiclient + intervals: ReturnType[] + sourceChain: Chain + private _queryBlockLength: number + private _blockBuffer: number + private _intervalFetchBlocksMs: number + private _intervalsUpdateLightClients: { [controllerName: string]: number } + private _mutex: Mutex + + constructor(_configs: BlockListenerConfigs) { + if (_configs.queryBlockLength > 256 - _configs.blockBuffer) { + throw new Error(`Please choose a block length less than ${256 - _configs.blockBuffer}!`) + } + + this.controllers = _configs.controllers + this.logger = _configs.logger.child({ service: "Coordinator" }) + this.multiclient = _configs.multiclient + this.sourceChain = _configs.sourceChain + this._queryBlockLength = _configs.queryBlockLength + this._blockBuffer = _configs.blockBuffer + this._intervalFetchBlocksMs = _configs.intervalFetchBlocksMs + this._intervalsUpdateLightClients = _configs.intervalsUpdateLightClients + + this.intervals = [] + this._mutex = new Mutex() + } + + start() { + this.logger.info(`Running ${this.controllers[0].name}`) + this.logger.info(`Source chain: ${this.sourceChain.name}`) + this.logger.info(`Query block length: ${this._queryBlockLength}`) + this.logger.info(`Block Buffer: ${this._blockBuffer}`) + this.logger.info(`Interval to fetch blocks (ms): ${this._intervalFetchBlocksMs}`) + + this.fetchBlocks() + this.intervals.push( + setInterval(() => { + this.fetchBlocks() + }, this._intervalFetchBlocksMs), + ) + + const lgControllers = this.controllers.filter((_controller) => _controller.type === "lightClient") + lgControllers.forEach((_controller) => this.updateLightClientReporterController(_controller)) + + this.intervals.push( + ...lgControllers.map((_controller) => { + return setInterval(() => { + this.updateLightClientReporterController(_controller) + }, this._intervalsUpdateLightClients[_controller.name]) + }), + ) + } + + stop() { + this.intervals.forEach(clearInterval) + } + + async fetchBlocks() { + try { + const client = this.multiclient.getClientByChain(this.sourceChain) + + const currentBlockNumber = await client.getBlockNumber() + const startBlock = currentBlockNumber - BigInt(this._queryBlockLength) + const endBlock = currentBlockNumber - BigInt(this._blockBuffer) + + const blocks = Array.from( + { length: Number(this._queryBlockLength - this._blockBuffer + 1) }, + (_, _index) => startBlock + BigInt(_index), + ) + this.logger.info(`New blocks detected on ${this.sourceChain.name}: [${startBlock},${endBlock}]`) + for (const controller of this.controllers.filter( + (_controller) => _controller.type === "classic" || _controller.type === "native", + )) { + const release = await this._mutex.acquire() + await controller.onBlocks(blocks) + release() + } + } catch (_err) { + this.logger.error(`Error from block listener ${_err}`) + } + } + + async updateLightClientReporterController(_controller: BaseController) { + const release = await this._mutex.acquire() + await _controller.update() + release() + } +} + +export default Coordinator diff --git a/packages/reporter/src/MultiClient.ts b/packages/reporter/src/MultiClient.ts new file mode 100644 index 00000000..d873eeaa --- /dev/null +++ b/packages/reporter/src/MultiClient.ts @@ -0,0 +1,39 @@ +import { createWalletClient, http, Chain, publicActions, WalletClient, PublicClient } from "viem" +import { privateKeyToAccount } from "viem/accounts" + +type ContructorConfigs = { + chains: Chain[] + privateKey: `0x${string}` + rpcUrls: { [chainName: string]: string } +} + +type GetClientsConfigs = { + chain?: Chain | undefined + privateKey: `0x${string}` + rpcUrl: string +} + +const getClient = ({ chain, privateKey, rpcUrl }: GetClientsConfigs) => + createWalletClient({ + account: privateKeyToAccount(privateKey), + chain: chain as Chain | undefined, + transport: http(rpcUrl), + }).extend(publicActions) + +class Multiclient { + private _clients: { [chainName: string]: PublicClient & WalletClient } + + constructor({ chains, privateKey, rpcUrls }: ContructorConfigs) { + this._clients = chains.reduce((_acc: { [chainName: string]: any }, _chain: Chain) => { + const rpcUrl = rpcUrls[_chain.name] + _acc[_chain.name] = getClient({ chain: _chain, privateKey, rpcUrl }) + return _acc + }, {}) + } + + getClientByChain(_chain: Chain): any { + return this._clients[_chain.name] + } +} + +export default Multiclient diff --git a/packages/reporter/src/controllers/BaseController.ts b/packages/reporter/src/controllers/BaseController.ts new file mode 100644 index 00000000..0964dddc --- /dev/null +++ b/packages/reporter/src/controllers/BaseController.ts @@ -0,0 +1,48 @@ +import { Chain } from "viem" +import winston from "winston" + +import Multiclient from "../MultiClient.js" + +export type ControllerType = "classic" | "lightClient" | "native" + +export type BaseControllerConfigs = { + type: ControllerType + sourceChain: Chain + destinationChains?: Chain[] + reporterAddress?: `0x${string}` + reporterAddresses?: { [chainName: string]: `0x${string}` } + adapterAddresses: { [chainName: string]: `0x${string}` } + logger: winston.Logger + multiClient: Multiclient +} + +class BaseController { + name: string + type: ControllerType + sourceChain: Chain + destinationChains?: Chain[] + reporterAddress?: `0x${string}` + reporterAddresses: { [chainName: string]: `0x${string}` } + adapterAddresses: { [chainName: string]: `0x${string}` } + logger: winston.Logger + multiClient: Multiclient + + constructor(configs: BaseControllerConfigs, name: string) { + this.sourceChain = configs.sourceChain + this.destinationChains = configs.destinationChains + this.reporterAddress = configs.reporterAddress + this.reporterAddresses = configs.reporterAddresses || {} + this.adapterAddresses = configs.adapterAddresses + this.multiClient = configs.multiClient + this.type = configs.type + this.name = name + + this.logger = configs.logger.child({ service: this.name }) + } + + onBlocks(_blockNumbers: bigint[]) {} + + update() {} +} + +export default BaseController diff --git a/packages/reporter/src/controllers/StandardReporterController.ts b/packages/reporter/src/controllers/StandardReporterController.ts new file mode 100644 index 00000000..345774d3 --- /dev/null +++ b/packages/reporter/src/controllers/StandardReporterController.ts @@ -0,0 +1,54 @@ +import { Chain, formatEther } from "viem" +import ABI from "../ABIs/StandardReporterContractABI.json" assert { type: "json" } + +import BaseController from "./BaseController.js" + +import { BaseControllerConfigs } from "./BaseController.js" + +interface StandardReporterControllerConfigs extends BaseControllerConfigs { + name: string + reportHeadersValue?: bigint +} + +class StandardReporterController extends BaseController { + private _reportHeadersValue: bigint + + constructor(_configs: StandardReporterControllerConfigs) { + super(_configs, _configs.name) + + this._reportHeadersValue = _configs.reportHeadersValue || BigInt(0) + } + + async onBlocks(_blockNumbers: bigint[]) { + try { + const client = this.multiClient.getClientByChain(this.sourceChain) + const blockNumber = _blockNumbers[_blockNumbers.length - 1] + + let nonce = await client.getTransactionCount({ address: client.account.address }) + for (const chain of this.destinationChains as Chain[]) { + if (!this.adapterAddresses[chain.name]) { + this.logger.info(`Adapter address is missing for ${chain.name}. Skipping...`) + continue + } + + this.logger.info(`reporting block header for block ${blockNumber} on ${chain.name} ...`) + const { request } = await client.simulateContract({ + address: this.reporterAddresses[chain.name], + abi: ABI, + functionName: "dispatchBlocks", + args: [chain.id, this.adapterAddresses[chain.name], [blockNumber]], + value: this._reportHeadersValue, + nonce, + }) + + const txHash = await client.writeContract(request) + this.logger.info(`headers reporter from ${this.sourceChain.name} to ${chain.name}: ${txHash}`) + nonce += 1 + } + } catch (_error) { + this.logger.error(_error) + } + } +} + +export default StandardReporterController diff --git a/packages/reporter/src/controllers/WormholeReporterController.ts b/packages/reporter/src/controllers/WormholeReporterController.ts new file mode 100644 index 00000000..5bbff0ba --- /dev/null +++ b/packages/reporter/src/controllers/WormholeReporterController.ts @@ -0,0 +1,104 @@ +import axios, { AxiosInstance } from "axios" +import { Mutex } from "async-mutex" +import { Chain } from "viem" + +import BaseController from "./BaseController.js" +import sleep from "../utils/sleep.js" +import ReporterABI from "../ABIs/WormholeReporterABI.json" assert { type: "json" } +import AdapterABI from "../ABIs/WormholeAdapterABI.json" assert { type: "json" } +import WormholeABI from "../ABIs/WormholeABI.json" assert { type: "json" } + +import { BaseControllerConfigs } from "./BaseController.js" + +interface WormholeReporterControllerConfigs extends BaseControllerConfigs { + wormholeScanBaseUrl: string + wormholeAddress: `0x${string}` + wormholeChainIds: { [chainName: string]: number } +} + +class WormholeReporterController extends BaseController { + private _wormholeAddress: `0x${string}` + private _wormholeScanClient: AxiosInstance + private _wormholeChainIds: { [chainName: string]: number } + private _mutex: Mutex + + constructor(_configs: WormholeReporterControllerConfigs) { + super(_configs, "WormholeReporterController") + this._wormholeScanClient = axios.create({ baseURL: _configs.wormholeScanBaseUrl }) + this._mutex = new Mutex() + this._wormholeAddress = _configs.wormholeAddress + this._wormholeChainIds = _configs.wormholeChainIds + } + + async onBlocks(_blockNumbers: bigint[]) { + const release = await this._mutex.acquire() + + try { + const wormholeChainId = this._wormholeChainIds[this.sourceChain.name] + const client = this.multiClient.getClientByChain(this.sourceChain) + const blockNumber = _blockNumbers[_blockNumbers.length - 1] + + const nextSequence = await client.readContract({ + address: this._wormholeAddress as `0x${string}`, + abi: WormholeABI, + functionName: "nextSequence", + args: [this.reporterAddress], + }) + + this.logger.info(`reporting block header for block ${blockNumber} ...`) + const { request } = await client.simulateContract({ + address: this.reporterAddress as `0x${string}`, + abi: ReporterABI, + functionName: "dispatchBlocks", + // targetChainId & adapter are not used in _dispatch(), here set to 0 + args: [0, "0x0000000000000000000000000000000000000000", [blockNumber]], + }) + + let txHash = await client.writeContract(request) + this.logger.info(`header reported from ${this.sourceChain.name} to Wormhole Network: ${txHash}`) + + let vaaBytes = null + let sequence = Number(nextSequence) + + while (true) { + try { + this.logger.info("Waiting for signed VAA ...") + + const { data } = await this._wormholeScanClient.get( + `v1/signed_vaa/${wormholeChainId}/000000000000000000000000${this.reporterAddress?.slice(2)}/${sequence}`, + ) + + vaaBytes = "0x" + Buffer.from(data.vaaBytes, "base64").toString("hex") + this.logger.info("Signed VAA available! Proceeding ...") + break + } catch (_err) { + this.logger.info("VAA not available yet ...") + } + await sleep(20000) + } + + for (const chain of this.destinationChains as Chain[]) { + if (!this.adapterAddresses[chain.name]) continue + + const destinationChainClient = this.multiClient.getClientByChain(chain) + + this.logger.info(`Storing header on ${chain.name} ...`) + const { request } = await destinationChainClient.simulateContract({ + address: this.adapterAddresses[chain.name], + abi: AdapterABI, + functionName: "storeHashesByEncodedVM", + args: [vaaBytes], + }) + + txHash = await destinationChainClient.writeContract(request) + this.logger.info(`Header stored on ${chain.name}: ${txHash}!`) + } + } catch (_error) { + this.logger.error(_error) + } finally { + release() + } + } +} + +export default WormholeReporterController diff --git a/packages/reporter/src/index.ts b/packages/reporter/src/index.ts new file mode 100644 index 00000000..0cdc7b67 --- /dev/null +++ b/packages/reporter/src/index.ts @@ -0,0 +1,265 @@ +import { + arbitrum, + arbitrumSepolia, + avalanche, + base, + bsc, + bscTestnet, + gnosis, + gnosisChiado, + goerli, + optimism, + optimismGoerli, + optimismSepolia, + polygon, + mainnet, + sepolia, +} from "viem/chains" +import { Chain } from "viem" + +import Multiclient from "./MultiClient.js" +import StandardReporterController from "./controllers/StandardReporterController.js" +import WormholeReporterController from "./controllers/WormholeReporterController.js" + +import Coordinator from "./Coordinator.js" +import { settings } from "./settings/index.js" +import logger from "./utils/logger.js" + +const main = () => { + const controllersEnabled = process.env.REPORTERS_ENABLED?.split(",") + + const sourceChainId = Number(process.env.SOURCE_CHAIN_ID) + const destinationChainIds = process.env.DESTINATION_CHAIN_IDS?.split(",").map((_chainId) => Number(_chainId)) + const chains = [ + arbitrum, + avalanche, + arbitrumSepolia, + base, + bsc, + bscTestnet, + gnosis, + gnosisChiado, + goerli, + optimism, + optimismGoerli, + polygon, + mainnet, + sepolia, + ] + const sourceChain: Chain = Object.values(chains).find((_chain) => _chain.id === sourceChainId) as Chain + const destinationChains: Chain[] = Object.values(chains).filter((_chain) => destinationChainIds?.includes(_chain.id)) + const unidirectionalAdaptersAddresses = settings.contractAddresses.adapterAddresses.unidirectional as any + const unidirectionalReportersAddresses = settings.contractAddresses.reporterAddresses.unidirectional as any + const lightClientAddresses = settings.contractAddresses.lightClientAddresses as any + + const multiClient = new Multiclient({ + chains: [sourceChain, ...destinationChains], + privateKey: process.env.PRIVATE_KEY as `0x${string}`, + rpcUrls: settings.rpcUrls, + }) + + const ambReporterController = new StandardReporterController({ + name: "AMBReporterController", + type: "classic", + sourceChain, + destinationChains: destinationChains.filter(({ name }) => name === gnosis.name || name === gnosisChiado.name), + logger, + multiClient, + reporterAddresses: { + [gnosis.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosis.name]?.AMBReporter, + [gnosisChiado.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosisChiado.name]?.AMBReporter, + }, + adapterAddresses: { + [gnosis.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[gnosis.name]?.AMBAdapter, + [gnosisChiado.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[gnosisChiado.name]?.AMBAdapter, + }, + }) + + const sygmaReporterController = new StandardReporterController({ + name: "SygmaReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [gnosis.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosis.name]?.SygmaReporter, + }, + adapterAddresses: { + [gnosis.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.Gnosis?.SygmaAdapter, + }, + }) + + const wormholeReporterController = new WormholeReporterController({ + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddress: (settings.contractAddresses.reporterAddresses as any)[sourceChain.name]?.WormholeReporter, + adapterAddresses: { + // [gnosis.name]: (settings.contractAddresses.adapterAddresses as any)?.Gnosis?.WormholeAdapter, + // [optimism.name]: (settings.contractAddresses.adapterAddresses as any)["OP Mainnet"]?.WormholeAdapter, + // [bsc.name]: (settings.contractAddresses.adapterAddresses as any)["BNB Smart Chain"]?.WormholeAdapter, + // [polygon.name]: (settings.contractAddresses.adapterAddresses as any)?.Polygon.WormholeAdapter, + // [avalanche.name]: (settings.contractAddresses.adapterAddresses as any)?.Avalanche.WormholeAdapter, + [gnosisChiado.name]: (settings.contractAddresses.adapterAddresses as any)?.[gnosisChiado.name]?.WormholeAdapter, + }, + wormholeScanBaseUrl: settings.reporterControllers.WormholeReporterController.wormholeScanBaseUrl, + wormholeAddress: (settings.contractAddresses as any)[sourceChain.name]?.Wormhole, + wormholeChainIds: settings.reporterControllers.WormholeReporterController.wormholeChainIds, + }) + + const axelarReporterController = new StandardReporterController({ + name: "AxelarReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [bsc.name]: unidirectionalReportersAddresses[sourceChain.name]?.[bsc.name]?.AxelarReporter, + }, + adapterAddresses: { + [bsc.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[bsc.name]?.AxelarAdapter, + }, + reportHeadersValue: settings.reporterControllers.AxelarReporterController.reportHeadersValue, + }) + + const connextReporterController = new StandardReporterController({ + name: "ConnextReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [gnosis.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosis.name]?.ConnextReporter, + }, + adapterAddresses: { + [gnosis.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[gnosis.name]?.ConnextAdapter, + }, + reportHeadersValue: settings.reporterControllers.ConnextReporterController.reportHeadersValue, + }) + + const celerReporterController = new StandardReporterController({ + name: "CelerReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [polygon.name]: unidirectionalReportersAddresses[sourceChain.name]?.[polygon.name]?.CelerReporter, + }, + adapterAddresses: { + [polygon.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[polygon.name]?.CelerAdapter, + }, + reportHeadersValue: settings.reporterControllers.CelerReporterController.reportHeadersValue, + }) + + const layerZeroReporterController = new StandardReporterController({ + name: "LayerZeroReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [polygon.name]: unidirectionalReportersAddresses[sourceChain.name]?.[polygon.name]?.LayerZeroReporter, + [bsc.name]: unidirectionalReportersAddresses[sourceChain.name]?.[bsc.name]?.LayerZeroReporter, + [gnosis.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosis.name]?.LayerZeroReporter, + [base.name]: unidirectionalReportersAddresses[sourceChain.name]?.[base.name]?.LayerZeroReporter, + [optimism.name]: unidirectionalReportersAddresses[sourceChain.name]?.[optimism.name]?.LayerZeroReporter, + [arbitrum.name]: unidirectionalReportersAddresses[sourceChain.name]?.[arbitrum.name]?.LayerZeroReporter, + }, + adapterAddresses: { + [polygon.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[polygon.name]?.LayerZeroAdapter, + [bsc.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[bsc.name]?.LayerZeroAdapter, + [gnosis.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[gnosis.name]?.LayerZeroAdapter, + [base.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[base.name]?.LayerZeroAdapter, + [optimism.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[optimism.name]?.LayerZeroAdapter, + [arbitrum.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[arbitrum.name]?.LayerZeroAdapter, + }, + reportHeadersValue: settings.reporterControllers.LayerZeroReporterController.reportHeadersValue, + }) + + const hyperlaneReporterController = new StandardReporterController({ + name: "HyperlaneReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [bsc.name]: unidirectionalReportersAddresses[sourceChain.name]?.[bsc.name]?.HyperlaneReporter, + }, + adapterAddresses: { + [bsc.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[bsc.name]?.HyperlaneAdapter, + }, + }) + + const ccipReporterController = new StandardReporterController({ + name: "CCIPReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [optimismGoerli.name]: unidirectionalReportersAddresses[sourceChain.name]?.[optimismGoerli.name]?.CCIPReporter, + [bscTestnet.name]: unidirectionalReportersAddresses[sourceChain.name]?.[bscTestnet.name]?.CCIPReporter, + [avalanche.name]: unidirectionalReportersAddresses[sourceChain.name]?.[avalanche.name]?.CCIPReporter, + [gnosisChiado.name]: unidirectionalReportersAddresses[sourceChain.name]?.[gnosisChiado.name]?.CCIPReporter, + }, + adapterAddresses: { + [optimismGoerli.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[optimismGoerli.name]?.CCIPAdapter, + [bscTestnet.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[bscTestnet.name]?.CCIPAdapter, + [avalanche.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[avalanche.name]?.CCIPAdapter, + [gnosisChiado.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[gnosisChiado.name]?.CCIPAdapter, + }, + reportHeadersValue: settings.reporterControllers.CCIPReporterController.reportHeadersValue, + }) + + const zetaReporterController = new StandardReporterController({ + name: "ZetaReporterController", + type: "classic", + sourceChain, + destinationChains, + logger, + multiClient, + reporterAddresses: { + [bscTestnet.name]: unidirectionalReportersAddresses[sourceChain.name]?.[bscTestnet.name]?.ZetaChainReporter, + }, + adapterAddresses: { + [bscTestnet.name]: unidirectionalAdaptersAddresses[sourceChain.name]?.[bscTestnet.name]?.ZetaChainAdapter, + }, + reportHeadersValue: settings.reporterControllers.ZetaReporterController.reportHeadersValue, + }) + + const coordinator = new Coordinator({ + controllers: [ + ambReporterController, + ccipReporterController, + sygmaReporterController, + wormholeReporterController, + axelarReporterController, + connextReporterController, + celerReporterController, + layerZeroReporterController, + hyperlaneReporterController, + zetaReporterController, + ].filter((_controller) => controllersEnabled?.includes(_controller.name)), + intervalFetchBlocksMs: settings.Coordinator.intervalFetchBlocksMs, + logger, + multiclient: multiClient, + sourceChain, + queryBlockLength: settings.Coordinator.queryBlockLength, + blockBuffer: settings.Coordinator.blockBuffer, + intervalsUpdateLightClients: settings.Coordinator.intervalsUpdateLightClients, + }) + + coordinator.start() +} + +main() diff --git a/packages/reporter/src/settings/index.ts b/packages/reporter/src/settings/index.ts new file mode 100644 index 00000000..c324fe45 --- /dev/null +++ b/packages/reporter/src/settings/index.ts @@ -0,0 +1,484 @@ +import "dotenv/config" +import { parseEther } from "viem" +import { + arbitrum, + arbitrumSepolia, + avalanche, + base, + bsc, + bscTestnet, + gnosis, + gnosisChiado, + goerli, + mainnet, + optimism, + optimismGoerli, + optimismSepolia, + polygon, + sepolia, +} from "viem/chains" + +export const settings = { + Coordinator: { + blockBuffer: Number(process.env.BLOCK_BUFFER), + queryBlockLength: Number(process.env.QUERY_BLOCK_LENGTH), + intervalFetchBlocksMs: Number(process.env.TIME_FETCH_BLOCKS_MS), + intervalsUpdateLightClients: { + TelepathyReporterController: Number(process.env.TELEPATHY_INTERVAL_UPDATE), + ElectronReporterController: Number(process.env.ELECTRON_INTERVAL_UPDATE), + }, + }, + rpcUrls: { + [gnosis.name]: process.env.GNOSIS_RPC_URL as string, + [goerli.name]: process.env.GOERLI_RPC_URL as string, + [mainnet.name]: process.env.MAINNET_RPC_URL as string, + [polygon.name]: process.env.POLYGON_RPC_URL as string, + [optimism.name]: process.env.OPTIMISM_RPC_URL as string, + [bsc.name]: process.env.BSC_RPC_URL as string, + [arbitrum.name]: process.env.ARBITRUM_RPC_URL as string, + [sepolia.name]: process.env.SEPOLIA_RPC_URL as string, + [base.name]: process.env.BASE_RPC_URL as string, + }, + beaconApiUrls: { + [goerli.name]: process.env.GOERLI_BEACON_API_URL as string, + }, + contractAddresses: { + adapterAddresses: { + unidirectional: { + [mainnet.name]: { + [bsc.name]: { + AxelarAdapter: process.env.BSC_AXELAR_ADAPTER_MAINNET as `0x${string}`, + TelepathyAdapter: process.env.BSC_TELEPATHY_ADAPTER as `0x${string}`, + HyperlaneAdapter: process.env.BSC_HYPERLANE_ADAPTER as `0x${string}`, + LayerZeroAdapter: process.env.BSC_MAINNET_LAYER_ZERO_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + TelepathyAdapter: process.env.GNOSIS_TELEPATHY_ADAPTER as `0x${string}`, + AMBAdapter: process.env.GNOSIS_AMB_ADAPTER as `0x${string}`, + ConnextAdapter: process.env.GNOSIS_CONNEXT_ADAPTER_MAINNET as `0x${string}`, + SygmaAdapter: process.env.GNOSIS_SYGMA_ADAPTER as `0x${string}`, // this works with many chains + }, + [polygon.name]: { + TelepathyAdapter: process.env.POLYGON_TELEPATHY_ADAPTER as `0x${string}`, + CelerAdapter: process.env.POLYGON_CELER_ADAPTER_MAINNET as `0x${string}`, + LayerZeroAdapter: process.env.POLYGON_MAINNET_LAYER_ZERO_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + TelepathyAdapter: process.env.OPTIMISM_TELEPATHY_ADAPTER as `0x${string}`, + L2CrossDomainMessengerAdapter: process.env + .OPTIMISM_L2_CROSS_DOMAIN_MESSENGER_ADAPTER_ADDRESS as `0x${string}`, + LayerZeroAdapter: process.env.OPTIMISM_MAINNET_LAYER_ZERO_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + TelepathyAdapter: process.env.ARBITRUM_TELEPATHY_ADAPTER as `0x${string}`, + }, + [avalanche.name]: { + LayerZeroAdapter: process.env.AVALANCHE_LAYER_ZERO_ADAPTER_MAINNET as `0x${string}`, + CCIPAdapter: process.env.AVALANCHE_CCIP_ADAPTER_MAINNET as `0x${string}`, + }, + [base.name]: { + LayerZeroAdapter: process.env.BASE_MAINNET_LAYER_ZERO_ADAPTER as `0x${string}`, + }, + /*[goerli.name]: { + AMBReporter: "0xedc0b1d3de4496e0d917af42f29cb71eb2982319" as `0x${string}`, + SygmaReporter: "0x2f96d347c932ac73b56e9352ecc0707e25173d88" as `0x${string}`, + },*/ + }, + [base.name]: { + [polygon.name]: { + LayerZeroAdapter: process.env.POLYGON_LZ_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroAdapter: process.env.BSC_LZ_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroAdapter: process.env.GNOSIS_LZ_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroAdapter: process.env.ARBITRUM_LZ_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroAdapter: process.env.OPTIMISM_LZ_ADAPTER as `0x${string}`, + }, + }, + [gnosis.name]: { + [polygon.name]: { + LayerZeroAdapter: process.env.POLYGON_LZ_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroAdapter: process.env.BSC_LZ_ADAPTER as `0x${string}`, + }, + [base.name]: { + LayerZeroAdapter: process.env.BASE_LZ_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroAdapter: process.env.ARBITRUM_LZ_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroAdapter: process.env.OPTIMISM_LZ_ADAPTER as `0x${string}`, + }, + }, + [polygon.name]: { + [base.name]: { + LayerZeroAdapter: process.env.BASE_LZ_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroAdapter: process.env.BSC_LZ_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroAdapter: process.env.GNOSIS_LZ_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroAdapter: process.env.ARBITRUM_LZ_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroAdapter: process.env.OPTIMISM_LZ_ADAPTER as `0x${string}`, + }, + }, + [bsc.name]: { + [polygon.name]: { + LayerZeroAdapter: process.env.POLYGON_LZ_ADAPTER as `0x${string}`, + }, + [base.name]: { + LayerZeroAdapter: process.env.BASE_LZ_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroAdapter: process.env.GNOSIS_LZ_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroAdapter: process.env.ARBITRUM_LZ_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroAdapter: process.env.OPTIMISM_LZ_ADAPTER as `0x${string}`, + }, + }, + [optimism.name]: { + [polygon.name]: { + LayerZeroAdapter: process.env.POLYGON_LZ_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroAdapter: process.env.BSC_LZ_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroAdapter: process.env.GNOSIS_LZ_ADAPTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroAdapter: process.env.ARBITRUM_LZ_ADAPTER as `0x${string}`, + }, + [base.name]: { + LayerZeroAdapter: process.env.BASE_LZ_ADAPTER as `0x${string}`, + }, + }, + [arbitrum.name]: { + [polygon.name]: { + LayerZeroAdapter: process.env.POLYGON_LZ_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroAdapter: process.env.BSC_LZ_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroAdapter: process.env.GNOSIS_LZ_ADAPTER as `0x${string}`, + }, + [base.name]: { + LayerZeroAdapter: process.env.BASE_LZ_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroAdapter: process.env.OPTIMISM_LZ_ADAPTER as `0x${string}`, + }, + }, + [sepolia.name]: { + [optimismSepolia.name]: { + LayerZeroAdapter: process.env.OPTIMISM_SEPOLIA_LZ_ADAPTER as `0x${string}`, + }, + [optimismGoerli.name]: { + CCIPAdapter: process.env.OPTIMISM_GOERLI_CCIP_ADAPTER_SEPOLIA as `0x${string}`, + }, + [bscTestnet.name]: { + CCIPAdapter: process.env.BSC_TESTNET_CCIP_ADAPTER_SEPOLIA as `0x${string}`, + }, + [gnosisChiado.name]: { + AMBAdapter: process.env.SEPOLIA_CHIADO_AMB_ADAPTER as `0x${string}`, + CCIPAdapter: process.env.SEPOLIA_CHIADO_CCIP_ADAPTER as `0x${string}`, + LayerZeroAdapter: process.env.SEPOLIA_CHIADO_LZ_ADAPTER as `0x${string}`, + WormholeAdapter: process.env.SEPOLIA_CHIADO_WORMHOLE_ADAPTER as `0x${string}`, + }, + [arbitrumSepolia.name]: { + LayerZeroAdapter: process.env.SEPOLIA_ARB_LZ_ADAPTER as `0x${string}`, + }, + }, + [goerli.name]: { + [bscTestnet.name]: { + ZetaChainAdapter: process.env.BSC_TESTNET_ZETA_ADAPTER_GOERLI as `0x${string}`, + }, + [gnosisChiado.name]: { + ElectronAdapter: process.env.CHIADO_ELECTRON_ADAPTER_GOERLI as `0x${string}`, + }, + }, + }, + [gnosisChiado.name]: { + WormholeAdapter: process.env.SEPOLIA_CHIADO_WORMHOLE_ADAPTER as `0x${string}`, + }, + [gnosis.name]: { + WormholeAdapter: process.env.GNOSIS_WORMHOLE_ADAPTER as `0x${string}`, + }, + [polygon.name]: { + WormholeAdapter: process.env.POLYGON_WORMHOLE_ADAPTER as `0x${string}`, + }, + [bsc.name]: { + WormholeAdapter: process.env.BSC_WORMHOLE_ADAPTER as `0x${string}`, + }, + [optimism.name]: { + WormholeAdapter: process.env.OPTIMISM_WORMHOLE_ADAPTER as `0x${string}`, + }, + [avalanche.name]: { + WormholeAdapter: process.env.AVALANCHE_WORMHOLE_ADAPTER as `0x${string}`, + }, + }, + reporterAddresses: { + unidirectional: { + [mainnet.name]: { + [bsc.name]: { + AxelarReporter: process.env.MAINNET_AXELAR_REPORTER_BSC as `0x${string}`, + HyperlaneReporter: process.env.MAINNET_HYPERLANE_REPORTER_BSC as `0x${string}`, + LayerZeroReporter: process.env.MAINNET_LAYER_ZERO_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + AMBReporter: process.env.MAINNET_AMB_REPORTER as `0x${string}`, + ConnextReporter: process.env.MAINNET_CONNEXT_REPORTER_GNOSIS as `0x${string}`, + SygmaReporter: process.env.MAINNET_SYGMA_REPORTER_GNOSIS as `0x${string}`, + }, + [optimism.name]: { + L1CrossDomainMessengerHeaderReporter: process.env + .MAINNET_L1_CROSS_DOMAIN_MESSENGER_HEADER_REPORTER_ADDRESS as `0x${string}`, + LayerZeroReporter: process.env.MAINNET_LAYER_ZERO_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.MAINNET_LAYER_ZERO_REPORTER as `0x${string}`, + }, + [polygon.name]: { + CelerReporter: process.env.MAINNET_CELER_REPORTER_POLYGON as `0x${string}`, + LayerZeroReporter: process.env.MAINNET_LAYER_ZERO_REPORTER as `0x${string}`, + }, + [avalanche.name]: { + LayerZeroReporter: process.env.MAINNET_LAYER_ZERO_REPORTER_AVALANCHE as `0x${string}`, + CCIPReporter: process.env.MAINNET_CCIP_REPORTER_AVALANCHE as `0x${string}`, + }, + }, + [base.name]: { + [polygon.name]: { + LayerZeroReporter: process.env.BASE_LZ_REPORTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroReporter: process.env.BASE_LZ_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroReporter: process.env.BASE_LZ_REPORTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroReporter: process.env.BASE_LZ_REPORTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroReporter: process.env.BASE_LZ_REPORTER as `0x${string}`, + }, + }, + [gnosis.name]: { + [polygon.name]: { + LayerZeroReporter: process.env.GNOSIS_LZ_REPORTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroReporter: process.env.GNOSIS_LZ_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.GNOSIS_LZ_REPORTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroReporter: process.env.GNOSIS_LZ_REPORTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroReporter: process.env.GNOSIS_LZ_REPORTER as `0x${string}`, + }, + }, + [optimism.name]: { + [polygon.name]: { + LayerZeroReporter: process.env.OPTIMISM_LZ_REPORTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroReporter: process.env.OPTIMISM_LZ_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.OPTIMISM_LZ_REPORTER as `0x${string}`, + }, + [arbitrum.name]: { + LayerZeroReporter: process.env.OPTIMISM_LZ_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroReporter: process.env.OPTIMISM_LZ_REPORTER as `0x${string}`, + }, + }, + [arbitrum.name]: { + [polygon.name]: { + LayerZeroReporter: process.env.ARBITRUM_LZ_REPORTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroReporter: process.env.ARBITRUM_LZ_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.ARBITRUM_LZ_REPORTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroReporter: process.env.ARBITRUM_LZ_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroReporter: process.env.ARBITRUM_LZ_REPORTER as `0x${string}`, + }, + }, + [polygon.name]: { + [arbitrum.name]: { + LayerZeroReporter: process.env.POLYGON_LZ_REPORTER as `0x${string}`, + }, + [bsc.name]: { + LayerZeroReporter: process.env.POLYGON_LZ_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.POLYGON_LZ_REPORTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroReporter: process.env.POLYGON_LZ_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroReporter: process.env.POLYGON_LZ_REPORTER as `0x${string}`, + }, + }, + [bsc.name]: { + [arbitrum.name]: { + LayerZeroReporter: process.env.BSC_LZ_REPORTER as `0x${string}`, + }, + [polygon.name]: { + LayerZeroReporter: process.env.BSC_LZ_REPORTER as `0x${string}`, + }, + [base.name]: { + LayerZeroReporter: process.env.BSC_LZ_REPORTER as `0x${string}`, + }, + [optimism.name]: { + LayerZeroReporter: process.env.BSC_LZ_REPORTER as `0x${string}`, + }, + [gnosis.name]: { + LayerZeroReporter: process.env.BSC_LZ_REPORTER as `0x${string}`, + }, + }, + [sepolia.name]: { + [optimismSepolia.name]: { + LayerZeroReporter: process.env.SEPOLIA_CHIADO_LZ_REPORTER as `0x${string}`, + }, + [optimismGoerli.name]: { + CCIPReporter: process.env.SEPOLIA_CCIP_REPORTER_OPTIMISM_GOERLI as `0x${string}`, + }, + [bscTestnet.name]: { + CCIPReporter: process.env.SEPOLIA_CCIP_REPORTER_BSC_TESTNET as `0x${string}`, + }, + [gnosisChiado.name]: { + AMBReporter: process.env.SEPOLIA_CHIADO_AMB_REPORTER as `0x${string}`, + CCIPReporter: process.env.SEPOLIA_CHIADO_CCIP_REPORTER as `0x${string}`, + LayerZeroReporter: process.env.SEPOLIA_CHIADO_LZ_REPORTER as `0x${string}`, + WormholeReporter: process.env.SEPOLIA_CHIADO_WORMHOLE_REPORTER as `0x${string}`, + }, + [arbitrumSepolia.name]: { + LayerZeroReporter: process.env.SEPOLIA_ARB_LZ_REPORTER as `0x${string}`, + }, + }, + [goerli.name]: { + [bscTestnet.name]: { + ZetaChainReporter: process.env.GOERLI_ZETA_REPORTER_BSC_TESTNET as `0x${string}`, + }, + }, + }, + [mainnet.name]: { + WormholeHeaderReporter: process.env.MAINNET_WORMHOLE_HEADER_REPORTER as `0x${string}`, + }, + [sepolia.name]: { + WormholeReporter: process.env.SEPOLIA_CHIADO_WORMHOLE_REPORTER as `0x${string}`, + }, + }, + lightClientAddresses: { + [gnosisChiado.name]: { + [goerli.name]: { + ElectronLightClient: process.env.CHIADO_ELECTRON_LIGHT_CLIENT_GOERLI as `0x${string}`, + }, + }, + [gnosis.name]: { + [mainnet.name]: { + TelepathyLightClient: "0x34b5378DE786389a477b40dD710812c250185f83" as `0x${string}`, + }, + }, + [polygon.name]: { + [mainnet.name]: { + TelepathyLightClient: "0x34b5378DE786389a477b40dD710812c250185f83" as `0x${string}`, + }, + }, + [bsc.name]: { + [mainnet.name]: { + TelepathyLightClient: "0x34b5378DE786389a477b40dD710812c250185f83" as `0x${string}`, + }, + }, + [optimism.name]: { + [mainnet.name]: { + TelepathyLightClient: "0x34b5378DE786389a477b40dD710812c250185f83" as `0x${string}`, + }, + }, + [arbitrum.name]: { + [mainnet.name]: { + TelepathyLightClient: "0x34b5378DE786389a477b40dD710812c250185f83" as `0x${string}`, + }, + }, + }, + [goerli.name]: { + HeaderStorage: process.env.GOERLI_HEADER_STORAGE as `0x${string}`, + }, + [mainnet.name]: { + Wormhole: process.env.MAINNET_WORMHOLE_ADDRESS as `0x${string}`, + HeaderStorage: process.env.MAINNET_HEADER_STORAGE as `0x${string}`, + }, + [sepolia.name]: { + Wormhole: process.env.SEPOLIA_WORMHOLE_ADDRESS as `0x${string}`, + }, + }, + reporterControllers: { + AMBReporterController: { + reportHeadersGas: Number(process.env.AMB_REPORTER_HEADERS_GAS), + }, + AxelarReporterController: { + reportHeadersValue: parseEther(process.env.AXELAR_REPORT_HEADERS_VALUE as string), + }, + CCIPReporterController: { + reportHeadersValue: parseEther(process.env.CCIP_REPORT_HEADERS_VALUE as string), + }, + CelerReporterController: { + reportHeadersValue: parseEther(process.env.CELER_REPORT_HEADERS_VALUE as string), + }, + ConnextReporterController: { + reportHeadersValue: parseEther(process.env.CONNEXT_REPORT_HEADERS_VALUE as string), + }, + ElectronReporterController: { + beaconchaBaseUrls: { + [goerli.name]: "https://goerli.beaconcha.in", + }, + }, + LayerZeroReporterController: { + reportHeadersValue: parseEther(process.env.LAYER_ZERO_REPORT_HEADERS_VALUE as string), + }, + TelepathyReporterController: { + baseProofUrl: process.env.TELEPATHY_PROOF_API_URL as string, + }, + WormholeReporterController: { + wormholeAddress: process.env.WORMHOLE_CONTRACT_ADDRESS as `0x${string}`, + wormholeChainIds: { + [mainnet.name]: 2, + [sepolia.name]: 10002, + }, + wormholeScanBaseUrl: process.env.WORMHOLE_SCAN_BASE_URL as string, + }, + ZetaReporterController: { + reportHeadersValue: parseEther(process.env.ZETA_CHAIN_REPORT_HEADERS_VALUE as string), + }, + }, +} diff --git a/packages/reporter/src/utils/logger.ts b/packages/reporter/src/utils/logger.ts new file mode 100644 index 00000000..54055ed2 --- /dev/null +++ b/packages/reporter/src/utils/logger.ts @@ -0,0 +1,28 @@ +import winston from "winston" + +const colors = { + error: "red", + warn: "yellow", + info: "cyan", + debug: "blue", + service: "magenta", +} + +const logger = winston.createLogger({ + level: "info", + format: winston.format.combine( + winston.format.colorize({ + colors, + }), + winston.format.timestamp(), + winston.format.printf(({ timestamp, level, message, service }) => { + const colorize = winston.format.colorize() + return `${timestamp} [${level}] ${colorize.colorize("service", `${service}`)}: ${message}` + }), + ), + transports: [new winston.transports.Console(), new winston.transports.File({ filename: "./logs/application.log" })], +}) + +winston.addColors(colors) + +export default logger diff --git a/packages/reporter/src/utils/proofs.ts b/packages/reporter/src/utils/proofs.ts new file mode 100644 index 00000000..77c17251 --- /dev/null +++ b/packages/reporter/src/utils/proofs.ts @@ -0,0 +1,150 @@ +import { AxiosInstance } from "axios" +import { capella, ssz, phase0 } from "@lodestar/types" +import { ProofType, createProof, SingleProof } from "@chainsafe/persistent-merkle-tree" +import { TransactionType } from "@ethereumjs/tx" +import { hexToBytes, concatBytes, bigIntToBytes, intToBytes } from "@ethereumjs/util" +import { TxReceipt, PostByzantiumTxReceipt, PreByzantiumTxReceipt } from "@ethereumjs/vm" +import { toHexString, fromHexString } from "@chainsafe/ssz" +import { RLP } from "@ethereumjs/rlp" +import { WalletClient, PublicClient, TransactionReceipt, Log } from "viem" +import { Trie } from "@ethereumjs/trie" + +const SLOTS_PER_HISTORICAL_ROOT = 8192 + +export type BeaconId = number | Uint8Array | string + +export const toStringFromBeaconId = (identifier: BeaconId) => { + if (identifier instanceof Uint8Array) { + return toHexString(identifier) + } + return identifier.toString() +} + +export const getState = async (_stateId: BeaconId, _client: AxiosInstance): Promise => { + const { data } = await _client.get(`/eth/v2/debug/beacon/states/${toStringFromBeaconId(_stateId)}`) + return ssz.capella.BeaconState.fromJson(data.data) as capella.BeaconState +} + +export const getHeader = async (_blockId: BeaconId, _client: AxiosInstance): Promise => { + const { data } = await _client.get(`/eth/v1/beacon/headers/${toStringFromBeaconId(_blockId)}`) + return ssz.phase0.BeaconBlockHeader.fromJson(data.data.header.message) +} + +export const getReceiptsRootProof = async (_srcBlockId: BeaconId, _targetBlockId: BeaconId, _client: AxiosInstance) => { + const srcState = await getState(toStringFromBeaconId(_srcBlockId), _client) + const targetState = await getState(toStringFromBeaconId(_targetBlockId), _client) + + const srcView = ssz.capella.BeaconState.toView(srcState as capella.BeaconState) + const targetView = ssz.capella.BeaconState.toView(targetState as capella.BeaconState) + const srcSlot = srcState.slot + const targetSlot = targetState.slot + + const srcHeader = await getHeader(_srcBlockId, _client) + const srcHeaderView = ssz.phase0.BeaconBlockHeader.toView(srcHeader as phase0.BeaconBlockHeader) + + let receiptsRootProof + let receiptsRoot + if (srcSlot == targetSlot) { + const receiptGindex = ssz.capella.BeaconState.getPathInfo(["latestExecutionPayloadHeader", "receiptsRoot"]).gindex + const receiptProof = createProof(targetView.node, { + type: ProofType.single, + gindex: receiptGindex, + }) as SingleProof + receiptsRootProof = receiptProof.witnesses.map(toHexString) + receiptsRoot = toHexString(receiptProof.leaf) + } else if (srcSlot - targetSlot < 8192) { + const headerGindex = ssz.phase0.BeaconBlockHeader.getPathInfo(["stateRoot"]).gindex + const headerProof = createProof(srcHeaderView.node, { + type: ProofType.single, + gindex: headerGindex, + }) as SingleProof + + const stateRootGindex = ssz.capella.BeaconState.getPathInfo([ + "stateRoots", + targetSlot % SLOTS_PER_HISTORICAL_ROOT, + ]).gindex + const proof = createProof(srcView.node, { + type: ProofType.single, + gindex: stateRootGindex, + }) as SingleProof + + const receiptGindex = ssz.capella.BeaconState.getPathInfo(["latestExecutionPayloadHeader", "receiptsRoot"]).gindex + const receiptProof = createProof(targetView.node, { + type: ProofType.single, + gindex: receiptGindex, + }) as SingleProof + receiptsRootProof = receiptProof.witnesses.concat(proof.witnesses).concat(headerProof.witnesses).map(toHexString) + receiptsRoot = toHexString(receiptProof.leaf) + } else { + throw Error("slots are too far") + } + return { receiptsRootProof, receiptsRoot } +} + +// copied from here: https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/vm/src/runBlock.ts +export const encodeReceipt = (receipt: TxReceipt, txType: TransactionType) => { + const encoded = RLP.encode([ + (receipt as PreByzantiumTxReceipt).stateRoot ?? + ((receipt as PostByzantiumTxReceipt).status === 0 ? Uint8Array.from([]) : hexToBytes("0x01")), + bigIntToBytes(receipt.cumulativeBlockGasUsed), + receipt.bitvector, + receipt.logs, + ]) + + if (txType === TransactionType.Legacy) { + return encoded + } + + // Serialize receipt according to EIP-2718: + // `typed-receipt = tx-type || receipt-data` + return concatBytes(intToBytes(txType), encoded) +} + +export const getReceiptProof = async (_hash: `0x${string}`, _client: PublicClient & WalletClient) => { + const receipt = await _client.getTransactionReceipt({ hash: _hash }) + const block = await _client.getBlock({ blockNumber: receipt.blockNumber }) + const receipts = [] as TransactionReceipt[] + for (const hash of block.transactions) { + receipts.push(await _client.getTransactionReceipt({ hash })) + } + + const trie = new Trie() + const encodedReceipts = receipts.map((_receipt) => { + let type = 0 + if (_receipt.type == "eip2930") { + type = 1 + } else if (_receipt.type == "eip1559") { + type = 2 + } else if (_receipt.type != "legacy") { + throw Error(`Unknown receipt type ${_receipt.type}`) + } + + return encodeReceipt( + { + bitvector: fromHexString(_receipt.logsBloom), + cumulativeBlockGasUsed: BigInt(_receipt.cumulativeGasUsed), + logs: _receipt.logs.map((_log: Log) => { + return [ + fromHexString(_log.address), + _log.topics.map((_topic: `0x${string}`) => fromHexString(_topic)), + fromHexString(_log.data), + ] + }), + status: _receipt.status === "success" ? 1 : 0, + } as TxReceipt, + type, + ) + }) + + await Promise.all( + receipts.map((_receipt, _index) => trie.put(RLP.encode(_receipt.transactionIndex), encodedReceipts[_index])), + ) + const receiptKey = RLP.encode(receipt.transactionIndex) + + const root = toHexString(trie.root()) + if (root !== block.receiptsRoot) { + throw Error("The trie.root() and block.receiptsRoot do not match") + } + + return { receiptProof: await trie.createProof(receiptKey), receiptsRoot: block.receiptsRoot } +} diff --git a/packages/reporter/src/utils/sleep.ts b/packages/reporter/src/utils/sleep.ts new file mode 100644 index 00000000..6232eca8 --- /dev/null +++ b/packages/reporter/src/utils/sleep.ts @@ -0,0 +1,8 @@ +const sleep = (_ms: number) => + new Promise((_resolve) => + setTimeout(() => { + _resolve() + }, _ms), + ) + +export default sleep diff --git a/packages/reporter/tsconfig.json b/packages/reporter/tsconfig.json new file mode 100644 index 00000000..974c23aa --- /dev/null +++ b/packages/reporter/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "lib": [ + "es6", + "ESNext", + "DOM" + ], + "module": "ESNext", + "moduleResolution": "node", + "noImplicitAny": true, + "outDir": "./dist", + "rootDir": "./src", + "removeComments": true, + "resolveJsonModule": true, + "sourceMap": true, + "strict": true, + "target": "ESNext", + "skipLibCheck": true, + }, + "exclude": [ + "node_modules" + ], + "include": [ + "./src/**/*" + ], + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node" + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8861a223..3da8cd17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -154,11 +154,54 @@ "@openzeppelin/contracts-upgradeable-4.7.3" "npm:@openzeppelin/contracts-upgradeable@v4.7.3" "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" +"@chainsafe/as-sha256@0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.5.0.tgz#2523fbef2b80b5000f9aa71f4a76e5c2c5c076bb" + integrity sha512-dTIY6oUZNdC5yDTVP5Qc9hAlKAsn0QTQ2DnQvvsbTnKSTbYs3p5RPN0aIUqN0liXei/9h24c7V0dkV44cnWIQA== + "@chainsafe/as-sha256@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== +"@chainsafe/as-sha256@^0.4.1": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.4.2.tgz#21ad1371e2245e430c1a554a05f10d333c6f42cc" + integrity sha512-HJ8GZBRjLeWtRsAXf3EbNsNzmTGpzTFjfpSf4yHkLYC+E52DhT6hwz+7qpj6I/EmFzSUm5tYYvT9K8GZokLQCQ== + +"@chainsafe/hashtree-darwin-arm64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-darwin-arm64/-/hashtree-darwin-arm64-1.0.1.tgz#e2c60090c56a1c8dc8bdff329856184ad32e4cd5" + integrity sha512-+KmEgQMpO7FDL3klAcpXbQ4DPZvfCe0qSaBBrtT4vLF8V1JGm3sp+j7oibtxtOsLKz7nJMiK1pZExi7vjXu8og== + +"@chainsafe/hashtree-linux-arm64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-linux-arm64-gnu/-/hashtree-linux-arm64-gnu-1.0.1.tgz#49d2604a6c9106219448af3eaf76f4da6e44daca" + integrity sha512-p1hnhGq2aFY+Zhdn1Q6L/6yLYNKjqXfn/Pc8jiM0e3+Lf/hB+yCdqYVu1pto26BrZjugCFZfupHaL4DjUTDttw== + +"@chainsafe/hashtree-linux-x64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-linux-x64-gnu/-/hashtree-linux-x64-gnu-1.0.1.tgz#31c5a2bb196b78f04f2bf4bfb5c1bf1f3331f071" + integrity sha512-uCIGuUWuWV0LiB4KLMy6JFa7Jp6NmPl3hKF5BYWu8TzUBe7vSXMZfqTzGxXPggFYN2/0KymfRdG9iDCOJfGRqg== + +"@chainsafe/hashtree@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree/-/hashtree-1.0.1.tgz#587666a261e1da6a37904095ce875fddc53c7c89" + integrity sha512-bleu9FjqBeR/l6W1u2Lz+HsS0b0LLJX2eUt3hOPBN7VqOhidx8wzkVh2S7YurS+iTQtfdK4K5QU9tcTGNrGwDg== + optionalDependencies: + "@chainsafe/hashtree-darwin-arm64" "1.0.1" + "@chainsafe/hashtree-linux-arm64-gnu" "1.0.1" + "@chainsafe/hashtree-linux-x64-gnu" "1.0.1" + +"@chainsafe/persistent-merkle-tree@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.8.0.tgz#18e2f0a5de3a0b59c6e5be8797a78e0d209dd7dc" + integrity sha512-hh6C1JO6SKlr0QGNTNtTLqgGVMA/Bc20wD6CeMHp+wqbFKCULRJuBUxhF4WDx/7mX8QlqF3nFriF/Eo8oYJ4/A== + dependencies: + "@chainsafe/as-sha256" "0.5.0" + "@chainsafe/hashtree" "1.0.1" + "@noble/hashes" "^1.3.0" + "@chainsafe/persistent-merkle-tree@^0.4.2": version "0.4.2" resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff" @@ -173,6 +216,14 @@ dependencies: "@chainsafe/as-sha256" "^0.3.1" +"@chainsafe/persistent-merkle-tree@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.6.1.tgz#37bde25cf6cbe1660ad84311aa73157dc86ec7f2" + integrity sha512-gcENLemRR13+1MED2NeZBMA7FRS0xQPM7L2vhMqvKkjqtFT4YfjSVADq5U0iLuQLhFUJEMVuA8fbv5v+TN6O9A== + dependencies: + "@chainsafe/as-sha256" "^0.4.1" + "@noble/hashes" "^1.3.0" + "@chainsafe/ssz@^0.10.0": version "0.10.2" resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.10.2.tgz#c782929e1bb25fec66ba72e75934b31fd087579e" @@ -181,6 +232,22 @@ "@chainsafe/as-sha256" "^0.3.1" "@chainsafe/persistent-merkle-tree" "^0.5.0" +"@chainsafe/ssz@^0.14.0": + version "0.14.3" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.14.3.tgz#caae48ae2670b2f8b6febed22b0e0619a636f316" + integrity sha512-ldOx4Rk9OC8YMvFdwvHKtRc7KpFRLcXlb9ATCdQ5fHtLT438LRQyxdWFufC9+M8jFHSZcgq31h2BJsSva6sZ0w== + dependencies: + "@chainsafe/as-sha256" "^0.4.1" + "@chainsafe/persistent-merkle-tree" "^0.6.1" + +"@chainsafe/ssz@^0.17.1": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.17.1.tgz#7986afbcad5e6971006d596fdb7dfa34bc195131" + integrity sha512-1ay46QqYcVTBvUnDXTPTi5WTiENu7tIxpZGMDpUWps1/nYBmh/We/UoCF/jO+o/fkcDD3p8xQPlHbcCfy+jyjA== + dependencies: + "@chainsafe/as-sha256" "0.5.0" + "@chainsafe/persistent-merkle-tree" "0.8.0" + "@chainsafe/ssz@^0.9.2": version "0.9.4" resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497" @@ -507,11 +574,113 @@ bufio "^1.0.7" chai "^4.3.4" +"@ethereumjs/block@^5.1.1", "@ethereumjs/block@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-5.3.0.tgz#a22d615f825a3aa123189434263a6c1d43d4ac8f" + integrity sha512-cyphdEB/ywIERqWLRHdAS6muTkAcd6BibMOp6XmGbeWgvtIhe4ArxcMDI78MVstJzT/faihvRI4rKQKy+MpdKQ== + dependencies: + "@ethereumjs/common" "^4.4.0" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/trie" "^6.2.1" + "@ethereumjs/tx" "^5.4.0" + "@ethereumjs/util" "^9.1.0" + ethereum-cryptography "^2.2.1" + +"@ethereumjs/blockchain@^7.1.0": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-7.3.0.tgz#87049c61b51a0870f3c23b5390b6ff9f3e3ae7f0" + integrity sha512-UZXFb6JFeXDHobKhXGAiKf+m5n2ynCtqpgHWCl2nQ3Tol9W7C3By4UFMpAl2E6EyaFhC26Maig9kqCWxfsQ6bQ== + dependencies: + "@ethereumjs/block" "^5.3.0" + "@ethereumjs/common" "^4.4.0" + "@ethereumjs/ethash" "^3.0.4" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/trie" "^6.2.1" + "@ethereumjs/tx" "^5.4.0" + "@ethereumjs/util" "^9.1.0" + debug "^4.3.3" + ethereum-cryptography "^2.2.1" + lru-cache "10.1.0" + +"@ethereumjs/common@^4.2.0", "@ethereumjs/common@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-4.4.0.tgz#fba41612f527a815bf304e98653d6b5fc5d6d4de" + integrity sha512-Fy5hMqF6GsE6DpYTyqdDIJPJgUtDn4dL120zKw+Pswuo+iLyBsEYuSyzMw6NVzD2vDzcBG9fE4+qX4X2bPc97w== + dependencies: + "@ethereumjs/util" "^9.1.0" + +"@ethereumjs/ethash@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-3.0.4.tgz#663524abbc2f5ef6fa9813067eb33808fd8a0309" + integrity sha512-dDc9h4RxEIWr38DxzeFyWlTmc++WeAFysFT6Ru0opoQ8WSM/hM3KH1VfHMPwx6JaqQT89Q/xtHV3CEvWrbwLKw== + dependencies: + "@ethereumjs/block" "^5.3.0" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/util" "^9.1.0" + bigint-crypto-utils "^3.2.2" + ethereum-cryptography "^2.2.1" + +"@ethereumjs/evm@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/evm/-/evm-2.2.1.tgz#07dd48152bb19277980b295a149b0936d2ff67b3" + integrity sha512-equF3QqssDgfZyVDEoMqJUsMCjO9SwgFdpUTc7yHFOU74X43l/MHM+Cqdey+wcBhdU2yOwD9S2AbW6wh7tDYfQ== + dependencies: + "@ethereumjs/common" "^4.2.0" + "@ethereumjs/statemanager" "^2.2.2" + "@ethereumjs/tx" "^5.2.1" + "@ethereumjs/util" "^9.0.2" + "@types/debug" "^4.1.9" + debug "^4.3.3" + ethereum-cryptography "^2.1.3" + rustbn-wasm "^0.2.0" + "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== +"@ethereumjs/rlp@^5.0.1", "@ethereumjs/rlp@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-5.0.2.tgz#c89bd82f2f3bec248ab2d517ae25f5bbc4aac842" + integrity sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA== + +"@ethereumjs/statemanager@^2.2.2": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/statemanager/-/statemanager-2.4.0.tgz#105feb0653942118758b3522ef05c8901d7924b3" + integrity sha512-IBe5kMGsDWlSvNg7QCERwO3BQE1c9hzVIZ9ktZF7xyifFEfA4VNhTMMEpwLuiAIy0l/ZzZiZ17/Iqar+SawMYA== + dependencies: + "@ethereumjs/common" "^4.4.0" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/trie" "^6.2.1" + "@ethereumjs/util" "^9.1.0" + debug "^4.3.3" + ethereum-cryptography "^2.2.1" + js-sdsl "^4.1.4" + lru-cache "10.1.0" + +"@ethereumjs/trie@^6.0.1", "@ethereumjs/trie@^6.1.1", "@ethereumjs/trie@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/trie/-/trie-6.2.1.tgz#11d3e91ffd7d565f468a62c0e3d7952063991fa9" + integrity sha512-MguABMVi/dPtgagK+SuY57rpXFP+Ghr2x+pBDy+e3VmMqUY+WGzFu1QWjBb5/iJ7lINk4CI2Uwsih07Nu9sTSg== + dependencies: + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/util" "^9.1.0" + "@types/readable-stream" "^2.3.13" + debug "^4.3.4" + ethereum-cryptography "^2.2.1" + lru-cache "10.1.0" + readable-stream "^3.6.0" + +"@ethereumjs/tx@^5.2.1", "@ethereumjs/tx@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-5.4.0.tgz#6f47894cc3e2d4e63d87c62b41ed7e8180a1de58" + integrity sha512-SCHnK7m/AouZ7nyoR0MEXw1OO/tQojSbp88t8oxhwes5iZkZCtfFdUrJaiIb72qIpH2FVw6s1k1uP7LXuH7PsA== + dependencies: + "@ethereumjs/common" "^4.4.0" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/util" "^9.1.0" + ethereum-cryptography "^2.2.1" + "@ethereumjs/util@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" @@ -521,6 +690,31 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" +"@ethereumjs/util@^9.0.1", "@ethereumjs/util@^9.0.2", "@ethereumjs/util@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-9.1.0.tgz#75e3898a3116d21c135fa9e29886565609129bce" + integrity sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog== + dependencies: + "@ethereumjs/rlp" "^5.0.2" + ethereum-cryptography "^2.2.1" + +"@ethereumjs/vm@^7.1.0": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-7.2.1.tgz#c53610a8c327aabfdd0a5dedfe32eb3aebb17665" + integrity sha512-exmyhDPlfiBtV/nIPY7gE5iu3QC62F7hf/fALLgQaUS8ezxmJQG2MRaFI1eqYwvsAhfNBfst7K6KcCE4jbGbhw== + dependencies: + "@ethereumjs/block" "^5.1.1" + "@ethereumjs/blockchain" "^7.1.0" + "@ethereumjs/common" "^4.2.0" + "@ethereumjs/evm" "^2.2.1" + "@ethereumjs/rlp" "^5.0.2" + "@ethereumjs/statemanager" "^2.2.2" + "@ethereumjs/trie" "^6.1.1" + "@ethereumjs/tx" "^5.2.1" + "@ethereumjs/util" "^9.0.2" + debug "^4.3.3" + ethereum-cryptography "^2.1.3" + "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -947,6 +1141,35 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@layerzerolabs/lz-evm-messagelib-v2@^2.3.29": + version "2.3.42" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-messagelib-v2/-/lz-evm-messagelib-v2-2.3.42.tgz#224e8b07940b829fc8955ec97987526da17625fd" + integrity sha512-ugCgAueNzrvp2p8LrNFlv/c5nYhDdZq5gCwJ5UzntniHf9enB4NoE3AxGYgMakh26Uckc2pQRzWlbO8+a6GD5g== + +"@layerzerolabs/lz-evm-oapp-v2@^2.3.29": + version "2.3.42" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-oapp-v2/-/lz-evm-oapp-v2-2.3.42.tgz#0cbb5095a7b7cdd1bca6f7c7cee87c7383723db4" + integrity sha512-TDhM8PGsH4nsKwXdoGDV9/USSJwOEGglsNfCUrpCs8EqkQ7rtlMDOvI60oFEebvOjxKSG9OwCcBFvXwaFf0ORQ== + +"@layerzerolabs/lz-evm-protocol-v2@^2.3.29": + version "2.3.42" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-protocol-v2/-/lz-evm-protocol-v2-2.3.42.tgz#171e2332aa83e5d30ebdb64c1b1158d204139153" + integrity sha512-my4tWF57jtwth/oLz3X1hYljPDDZM9wKzU498yk+6FgBtKg6J4uZIUoNh3p3GgUJ+Lde8pVpxqJ5pNchPICxmQ== + +"@lodestar/params@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@lodestar/params/-/params-1.22.0.tgz#e2d436da9ceb742e7ae088a6474468e8e8756a1b" + integrity sha512-wbbeQAG+4YOl1ATsSDJnx7wBk5FXsRl2OrmLkMnJXI67wqvRwWk8WQBx7wjx2hnWWrk0in4/8bTtHz3At1GydQ== + +"@lodestar/types@^1.12.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@lodestar/types/-/types-1.22.0.tgz#5178c5edfaefe8875533545d95707957fa59f80a" + integrity sha512-BB0zgiqmIYmpg1ifDJ4VW0Ka2vkdmM7ju7lAQLk1O666iGWLObhfrLzQ+LbZ8/0h+PnjpDMB55MJSffmnqCkGg== + dependencies: + "@chainsafe/ssz" "^0.17.1" + "@lodestar/params" "^1.22.0" + ethereum-cryptography "^2.0.0" + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -965,6 +1188,13 @@ dependencies: sparse-bitfield "^3.0.3" +"@noble/curves@1.2.0", "@noble/curves@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + "@noble/curves@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" @@ -991,16 +1221,26 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== -"@noble/hashes@1.5.0", "@noble/hashes@^1.4.0", "@noble/hashes@~1.5.0": +"@noble/hashes@1.5.0", "@noble/hashes@^1.3.0", "@noble/hashes@^1.4.0", "@noble/hashes@~1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -1184,6 +1424,21 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz#ec95f23b53cb4e71a1a7091380fa223aad18f156" integrity sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg== +"@nomicfoundation/hardhat-verify@^2.0.8": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.11.tgz#4ce12b592e01ee93a81933924609c233ed00d951" + integrity sha512-lGIo4dNjVQFdsiEgZp3KP6ntLiF7xJEJsbNHfSyIiFCyI0Yv0518ElsFtMC5uCuHEChiBBMrib9jWQvHHT+X3Q== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + lodash.clonedeep "^4.5.0" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" + "@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz#3a9c3b20d51360b20affb8f753e756d553d49557" @@ -1317,6 +1572,11 @@ "@openzeppelin/contracts" "^4.4.1" "@openzeppelin/contracts-upgradeable" "^4.7.3" +"@scure/base@^1.1.1", "@scure/base@~1.1.2": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== + "@scure/base@~1.1.0", "@scure/base@~1.1.6", "@scure/base@~1.1.8": version "1.1.8" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.8.tgz#8f23646c352f020c83bca750a82789e246d42b50" @@ -1331,6 +1591,15 @@ "@noble/secp256k1" "~1.7.0" "@scure/base" "~1.1.0" +"@scure/bip32@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" + integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== + dependencies: + "@noble/curves" "~1.2.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.2" + "@scure/bip32@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" @@ -1348,6 +1617,14 @@ "@noble/hashes" "~1.2.0" "@scure/base" "~1.1.0" +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@scure/bip39@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" @@ -1580,6 +1857,13 @@ dependencies: "@types/node" "*" +"@types/debug@^4.1.9": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + "@types/form-data@0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" @@ -1634,6 +1918,16 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.8.tgz#a7eff5816e070c3b4d803f1d3cd780c4e42934a1" integrity sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw== +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node-cron@^3.0.9": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.11.tgz#70b7131f65038ae63cfe841354c8aba363632344" + integrity sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg== + "@types/node@*": version "22.5.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44" @@ -1958,6 +2252,11 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== +abitype@0.9.8: + version "0.9.8" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" + integrity sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ== + abitype@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6" @@ -2188,6 +2487,13 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-mutex@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c" + integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== + dependencies: + tslib "^2.4.0" + async@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -2253,7 +2559,7 @@ bigint-buffer@^1.1.5: dependencies: bindings "^1.3.0" -bigint-crypto-utils@^3.0.23: +bigint-crypto-utils@^3.0.23, bigint-crypto-utils@^3.2.2: version "3.3.0" resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77" integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg== @@ -3106,6 +3412,10 @@ dotenv@^16.0.3, dotenv@^16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +"ds-test@github:dapphub/ds-test": + version "1.0.0" + resolved "https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -3413,7 +3723,7 @@ ethereum-cryptography@^1.0.3: "@scure/bip32" "1.1.5" "@scure/bip39" "1.1.1" -ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2, ethereum-cryptography@^2.1.3, ethereum-cryptography@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== @@ -3746,6 +4056,11 @@ follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== +forge-std@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/forge-std/-/forge-std-1.1.2.tgz#f4a0eda103538d56f9c563f3cd1fa2fd01bd9378" + integrity sha512-Wfb0iAS9PcfjMKtGpWQw9mXzJxrWD62kJCUqqLcyuI0+VRtJ3j20XembjF3kS20qELYdXft1vD/SPFVWVKMFOw== + form-data@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -4616,6 +4931,11 @@ isomorphic-ws@^4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + isows@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.4.tgz#810cd0d90cc4995c26395d2aa4cfa4037ebdf061" @@ -4905,6 +5225,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.isfunction@^3.0.9: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" @@ -5008,6 +5333,11 @@ loupe@^2.3.6: dependencies: get-func-name "^2.0.1" +lru-cache@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" + integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5621,7 +5951,7 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== -patch-package@8.0.0: +patch-package@8.0.0, patch-package@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== @@ -6069,6 +6399,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rustbn-wasm@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn-wasm/-/rustbn-wasm-0.2.0.tgz#0407521fb55ae69eeb4968d01885d63efd1c4ff9" + integrity sha512-FThvYFNTqrEKGqXuseeg0zR7yROh/6U1617mCHF68OVqrN1tNKRN7Tdwy4WayPVsCmmK+eMxtIZX1qL6JxTkMg== + dependencies: + "@scure/base" "^1.1.1" + rustbn.js@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" @@ -6334,6 +6671,14 @@ solhint@^3.3.7: optionalDependencies: prettier "^2.8.3" +solidity-bytes-utils@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/solidity-bytes-utils/-/solidity-bytes-utils-0.8.2.tgz#763d6a02fd093e93b3a97b742e97d540e66c29bd" + integrity sha512-cqXPYAV2auhpdKSTPuqji0CwpSceZDu95CzqSM/9tDJ2MoMaMsdHTpOIWtVw31BIqqGPNmIChCswzbw0tHaMTw== + dependencies: + ds-test "github:dapphub/ds-test" + forge-std "^1.1.2" + solidity-coverage@^0.8.2: version "0.8.13" resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.13.tgz#8eeada2e82ae19d25568368aa782a2baad0e0ce7" @@ -6771,7 +7116,7 @@ ts-generator@^0.1.1: resolve "^1.8.1" ts-essentials "^1.0.0" -ts-node@^10.8.1, ts-node@^10.9.1: +ts-node@^10.8.1, ts-node@^10.9.1, ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -6907,6 +7252,11 @@ typescript@^4.9.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@~5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" @@ -7001,6 +7351,35 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +viem@2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.20.0.tgz#abff4c2cf733bcc20978e662ea17db117a2881ef" + integrity sha512-cM4vs81HnSNbfceI1MLkx4pCVzbVjl9xiNSv5SCutYjUyFFOVSPDlEyhpg2iHinxx1NM4Qne3END5eLT8rvUdg== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + abitype "1.0.5" + isows "1.0.4" + webauthn-p256 "0.0.5" + ws "8.17.1" + +viem@^1.16.5: + version "1.21.4" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.21.4.tgz#883760e9222540a5a7e0339809202b45fe6a842d" + integrity sha512-BNVYdSaUjeS2zKQgPs+49e5JKocfo60Ib2yiXOWBT6LuVxY1I/6fFX3waEtpXvL1Xn4qu+BVitVtMh9lyThyhQ== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "0.9.8" + isows "1.0.3" + ws "8.13.0" + viem@^2.9.28: version "2.21.7" resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.7.tgz#c933fd3adb6f771e5c5fe2b4d7a14d8701ddc32f" @@ -7162,6 +7541,11 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + ws@8.17.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"