From 3a61b04b113fb0d3fc2d98c70394648719c1f290 Mon Sep 17 00:00:00 2001 From: Evan Gray Date: Wed, 13 Aug 2025 09:39:50 -0400 Subject: [PATCH] add custom consistency level and executor --- src/interfaces/ICustomConsistencyLevel.sol | 12 ++++++ src/interfaces/executor/IExecutor.sol | 34 +++++++++++++++++ src/interfaces/executor/IVaaV1Receiver.sol | 8 ++++ src/libraries/ConsistencyConfigMaker.sol | 20 ++++++++++ src/libraries/executor/ExecutorMessages.sol | 40 ++++++++++++++++++++ src/libraries/executor/RelayInstructions.sol | 17 +++++++++ test/CustomConsistencyLevel.t.sol | 15 ++++++++ test/executor/ExecutorMessages.t.sol | 39 +++++++++++++++++++ test/executor/RelayInstructions.t.sol | 37 ++++++++++++++++++ 9 files changed, 222 insertions(+) create mode 100644 src/interfaces/ICustomConsistencyLevel.sol create mode 100644 src/interfaces/executor/IExecutor.sol create mode 100644 src/interfaces/executor/IVaaV1Receiver.sol create mode 100644 src/libraries/ConsistencyConfigMaker.sol create mode 100644 src/libraries/executor/ExecutorMessages.sol create mode 100644 src/libraries/executor/RelayInstructions.sol create mode 100644 test/CustomConsistencyLevel.t.sol create mode 100644 test/executor/ExecutorMessages.t.sol create mode 100644 test/executor/RelayInstructions.t.sol diff --git a/src/interfaces/ICustomConsistencyLevel.sol b/src/interfaces/ICustomConsistencyLevel.sol new file mode 100644 index 00000000..fa5c3e8c --- /dev/null +++ b/src/interfaces/ICustomConsistencyLevel.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +//from https://github.com/wormhole-foundation/wormhole/blob/39081fa2936badf178f8b7e5eb63074d3308bf7d/ethereum/contracts/custom_consistency_level/interfaces/ICustomConsistencyLevel.sol +interface ICustomConsistencyLevel { + //topic0 0xa37f0112e03d41de27266c1680238ff1548c0441ad1e73c82917c000eefdd5ea. + event ConfigSet(address emitterAddress, bytes32 config); + + function configure(bytes32 config) external; + + function getConfiguration(address emitterAddress) external returns (bytes32); +} diff --git a/src/interfaces/executor/IExecutor.sol b/src/interfaces/executor/IExecutor.sol new file mode 100644 index 00000000..ff846403 --- /dev/null +++ b/src/interfaces/executor/IExecutor.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/src/interfaces/IExecutor.sol +interface IExecutor { + struct SignedQuoteHeader { + bytes4 prefix; + address quoterAddress; + bytes32 payeeAddress; + uint16 srcChain; + uint16 dstChain; + uint64 expiryTime; + } + + event RequestForExecution( + address indexed quoterAddress, + uint256 amtPaid, + uint16 dstChain, + bytes32 dstAddr, + address refundAddr, + bytes signedQuote, + bytes requestBytes, + bytes relayInstructions + ); + + function requestExecution( + uint16 dstChain, + bytes32 dstAddr, + address refundAddr, + bytes calldata signedQuote, + bytes calldata requestBytes, + bytes calldata relayInstructions + ) external payable; +} diff --git a/src/interfaces/executor/IVaaV1Receiver.sol b/src/interfaces/executor/IVaaV1Receiver.sol new file mode 100644 index 00000000..c43b2de3 --- /dev/null +++ b/src/interfaces/executor/IVaaV1Receiver.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/src/interfaces/IVaaV1Receiver.sol +//any contract that wishes to receive V1 VAAs from the executor needs to implement `IVaaV1Receiver`. +interface IVaaV1Receiver { + function executeVAAv1(bytes memory msg) external payable; +} diff --git a/src/libraries/ConsistencyConfigMaker.sol b/src/libraries/ConsistencyConfigMaker.sol new file mode 100644 index 00000000..4de1b0b2 --- /dev/null +++ b/src/libraries/ConsistencyConfigMaker.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +//from https://github.com/wormhole-foundation/wormhole/blob/39081fa2936badf178f8b7e5eb63074d3308bf7d/ethereum/contracts/custom_consistency_level/libraries/ConfigMakers.sol +library ConsistencyConfigMaker { + uint8 public constant TYPE_ADDITIONAL_BLOCKS = 1; + + //blocksToWait specifies the number of additional blocks to wait after the consistency level is reached. + function makeAdditionalBlocksConfig(uint8 consistencyLevel, uint16 blocksToWait) + internal + pure + returns (bytes32) + { + return bytes32(((( + uint256(TYPE_ADDITIONAL_BLOCKS) + << 8) | uint256(consistencyLevel)) + << 16) | uint256(blocksToWait)) + << 224; + } +} diff --git a/src/libraries/executor/ExecutorMessages.sol b/src/libraries/executor/ExecutorMessages.sol new file mode 100644 index 00000000..fa5f2ef7 --- /dev/null +++ b/src/libraries/executor/ExecutorMessages.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.19; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/src/libraries/ExecutorMessages.sol +library ExecutorMessages { + bytes4 private constant REQ_VAA_V1 = "ERV1"; + bytes4 private constant REQ_NTT_V1 = "ERN1"; + bytes4 private constant REQ_CCTP_V1 = "ERC1"; + bytes4 private constant REQ_CCTP_V2 = "ERC2"; + + //selector 492f620d. + error PayloadTooLarge(); + + function makeVAAv1Request(uint16 emitterChain, bytes32 emitterAddress, uint64 sequence) + internal + pure + returns (bytes memory) + { + return abi.encodePacked(REQ_VAA_V1, emitterChain, emitterAddress, sequence); + } + + //messageId specifies the manager message id for the NTT transfer. + function makeNTTv1Request(uint16 srcChain, bytes32 srcManager, bytes32 messageId) + internal + pure + returns (bytes memory) + { + return abi.encodePacked(REQ_NTT_V1, srcChain, srcManager, messageId); + } + + function makeCCTPv1Request(uint32 sourceDomain, uint64 nonce) internal pure returns (bytes memory) { + return abi.encodePacked(REQ_CCTP_V1, sourceDomain, nonce); + } + + //this request currently assumes the Executor will auto detect the event off chain. + //that may change in the future, in which case this interface would change. + function makeCCTPv2Request() internal pure returns (bytes memory) { + return abi.encodePacked(REQ_CCTP_V2, uint8(1)); + } +} diff --git a/src/libraries/executor/RelayInstructions.sol b/src/libraries/executor/RelayInstructions.sol new file mode 100644 index 00000000..a273abc0 --- /dev/null +++ b/src/libraries/executor/RelayInstructions.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.19; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/src/libraries/RelayInstructions.sol +library RelayInstructions { + uint8 private constant RECV_INST_TYPE_GAS = uint8(1); + uint8 private constant RECV_INST_TYPE_DROP_OFF = uint8(2); + + //this instruction may be specified more than once. If so, the relayer should sum the values. + function encodeGas(uint128 gasLimit, uint128 msgVal) internal pure returns (bytes memory) { + return abi.encodePacked(RECV_INST_TYPE_GAS, gasLimit, msgVal); + } + + function encodeGasDropOffInstructions(uint128 dropOff, bytes32 recipient) internal pure returns (bytes memory) { + return abi.encodePacked(RECV_INST_TYPE_DROP_OFF, dropOff, recipient); + } +} diff --git a/test/CustomConsistencyLevel.t.sol b/test/CustomConsistencyLevel.t.sol new file mode 100644 index 00000000..fbc92393 --- /dev/null +++ b/test/CustomConsistencyLevel.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import {Test, console} from "forge-std/Test.sol"; +import {CONSISTENCY_LEVEL_SAFE} from "../src/constants/ConsistencyLevel.sol"; +import {ConsistencyConfigMaker} from "../src/libraries/ConsistencyConfigMaker.sol"; + +//from https://github.com/wormhole-foundation/wormhole/blob/39081fa2936badf178f8b7e5eb63074d3308bf7d/ethereum/forge-test/CustomConsistencyLevel.t.sol +contract CustomConsistencyLevelTest is Test { + function test_makeAdditionalBlocksConfig() public { + bytes32 expected = 0x01c9002a00000000000000000000000000000000000000000000000000000000; + bytes32 result = ConsistencyConfigMaker.makeAdditionalBlocksConfig(CONSISTENCY_LEVEL_SAFE, 42); + assertEq(expected, result); + } +} diff --git a/test/executor/ExecutorMessages.t.sol b/test/executor/ExecutorMessages.t.sol new file mode 100644 index 00000000..bcfc95e6 --- /dev/null +++ b/test/executor/ExecutorMessages.t.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {ExecutorMessages} from "../../src/libraries/executor/ExecutorMessages.sol"; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/test/ExecutorMessages.t.sol +contract ExecutorMessagesTest is Test { + function test_makeVAAv1Request() public { + uint16 emitterChain = 7; + bytes32 emitterAddress = bytes32(uint256(uint160(0xdeadbeef))); + bytes memory expected = abi.encodePacked("ERV1", emitterChain, emitterAddress, uint64(42)); + bytes memory buf = ExecutorMessages.makeVAAv1Request(emitterChain, emitterAddress, 42); + assertEq(expected, buf); + } + + function test_makeNTTv1Request() public { + uint16 srcChain = 7; + bytes32 srcManager = bytes32(uint256(uint160(0xdeadbeef))); + bytes32 messageId = bytes32(uint256(42)); + bytes memory expected = abi.encodePacked("ERN1", srcChain, srcManager, messageId); + bytes memory buf = ExecutorMessages.makeNTTv1Request(srcChain, srcManager, messageId); + assertEq(expected, buf); + } + + function test_makeCCTPv1Request() public { + uint32 srcDomain = 7; + uint64 nonce = 42; + bytes memory expected = abi.encodePacked("ERC1", srcDomain, nonce); + bytes memory buf = ExecutorMessages.makeCCTPv1Request(srcDomain, nonce); + assertEq(expected, buf); + } + + function test_makeCCTPv2Request() public { + bytes memory expected = abi.encodePacked("ERC2", uint8(1)); + bytes memory buf = ExecutorMessages.makeCCTPv2Request(); + assertEq(expected, buf); + } +} diff --git a/test/executor/RelayInstructions.t.sol b/test/executor/RelayInstructions.t.sol new file mode 100644 index 00000000..3c2f9485 --- /dev/null +++ b/test/executor/RelayInstructions.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {RelayInstructions} from "../../src/libraries/executor/RelayInstructions.sol"; + +//from https://github.com/wormholelabs-xyz/example-messaging-executor/blob/ec5daea3c03f8860a62c23e28db5c6dc8771a9ce/evm/test/RelayInstructions.t.sol +contract RelayInstructionsTest is Test { + function test_encodeGas() public { + uint128 gasLimit = 123456000; + uint128 msgVal = 42000; + bytes memory expected = abi.encodePacked(uint8(1), gasLimit, msgVal); + bytes memory buf = RelayInstructions.encodeGas(gasLimit, msgVal); + assertEq(expected, buf); + } + + function test_encodeGasDropOffInstructions() public { + uint128 dropOff = 123456000; + bytes32 recipient = bytes32(uint256(uint160(0xdeadbeef))); + bytes memory expected = abi.encodePacked(uint8(2), dropOff, recipient); + bytes memory buf = RelayInstructions.encodeGasDropOffInstructions(dropOff, recipient); + assertEq(expected, buf); + } + + function test_multipleInstructions() public { + uint128 gasLimit = 123456000; + uint128 msgVal = 42000; + uint128 dropOff = 123456000; + bytes32 recipient = bytes32(uint256(uint160(0xdeadbeef))); + bytes memory expected = abi.encodePacked(uint8(1), gasLimit, msgVal, uint8(2), dropOff, recipient); + bytes memory buf = abi.encodePacked( + RelayInstructions.encodeGas(gasLimit, msgVal), + RelayInstructions.encodeGasDropOffInstructions(dropOff, recipient) + ); + assertEq(expected, buf); + } +}