From 8f0b5230d6db315b7de3b166a013d21015939593 Mon Sep 17 00:00:00 2001 From: Bruce Riley Date: Tue, 3 Dec 2024 15:11:48 -0600 Subject: [PATCH] evm: Add adapter with executor --- ...l => DeployWormholeGuardiansAdapter.s.sol} | 0 ...WormholeGuardiansAdapterWithExecutor.s.sol | 53 ++++++ ...r.sh => deployWormholeGuardiansAdapter.sh} | 0 ...loyWormholeGuardiansAdapterWithExecutor.sh | 49 +++++ evm/src/WormholeGuardiansAdapter.sol | 12 +- .../WormholeGuardiansAdapterWithExecutor.sol | 54 ++++++ evm/test/WormholeGuardiansAdapter.t.sol | 117 +----------- ...WormholeGuardiansAdapterWithExecutor.t.sol | 168 ++++++++++++++++++ evm/test/mocks/MockEndpoint.sol | 37 ++++ evm/test/mocks/MockExecutor.sol | 37 ++++ evm/test/mocks/MockWormhole.sol | 80 +++++++++ 11 files changed, 489 insertions(+), 118 deletions(-) rename evm/script/{DeployWormholeTransceiver.s.sol => DeployWormholeGuardiansAdapter.s.sol} (100%) create mode 100644 evm/script/DeployWormholeGuardiansAdapterWithExecutor.s.sol rename evm/sh/{deployWormholeTransceiver.sh => deployWormholeGuardiansAdapter.sh} (100%) create mode 100755 evm/sh/deployWormholeGuardiansAdapterWithExecutor.sh create mode 100644 evm/src/WormholeGuardiansAdapterWithExecutor.sol create mode 100644 evm/test/WormholeGuardiansAdapterWithExecutor.t.sol create mode 100644 evm/test/mocks/MockEndpoint.sol create mode 100644 evm/test/mocks/MockExecutor.sol create mode 100644 evm/test/mocks/MockWormhole.sol diff --git a/evm/script/DeployWormholeTransceiver.s.sol b/evm/script/DeployWormholeGuardiansAdapter.s.sol similarity index 100% rename from evm/script/DeployWormholeTransceiver.s.sol rename to evm/script/DeployWormholeGuardiansAdapter.s.sol diff --git a/evm/script/DeployWormholeGuardiansAdapterWithExecutor.s.sol b/evm/script/DeployWormholeGuardiansAdapterWithExecutor.s.sol new file mode 100644 index 0000000..e1a5639 --- /dev/null +++ b/evm/script/DeployWormholeGuardiansAdapterWithExecutor.s.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import { + WormholeGuardiansAdapterWithExecutor, + wormholeGuardiansAdapterWithExecutorVersionString +} from "../src/WormholeGuardiansAdapterWithExecutor.sol"; +import "forge-std/Script.sol"; + +// DeployWormholeGuardiansAdapterWithExecutor is a forge script to deploy the WormholeGuardiansAdapterWithExecutor contract. Use ./sh/deployWormholeGuardiansAdapterWithExecutor.sh to invoke this. +contract DeployWormholeGuardiansAdapterWithExecutor is Script { + function test() public {} // Exclude this from coverage report. + + function dryRun( + uint16 ourChain, + address admin, + address endpoint, + address executor, + address wormhole, + uint8 consistencyLevel + ) public { + _deploy(ourChain, admin, endpoint, executor, wormhole, consistencyLevel); + } + + function run( + uint16 ourChain, + address admin, + address endpoint, + address executor, + address wormhole, + uint8 consistencyLevel + ) public returns (address deployedAddress) { + vm.startBroadcast(); + (deployedAddress) = _deploy(ourChain, admin, endpoint, executor, wormhole, consistencyLevel); + vm.stopBroadcast(); + } + + function _deploy( + uint16 ourChain, + address admin, + address endpoint, + address executor, + address wormhole, + uint8 consistencyLevel + ) internal returns (address deployedAddress) { + bytes32 salt = keccak256(abi.encodePacked(wormholeGuardiansAdapterWithExecutorVersionString)); + WormholeGuardiansAdapterWithExecutor wormholeGuardiansAdapterWithExecutor = new WormholeGuardiansAdapterWithExecutor{ + salt: salt + }(ourChain, admin, endpoint, executor, wormhole, consistencyLevel); + + return (address(wormholeGuardiansAdapterWithExecutor)); + } +} diff --git a/evm/sh/deployWormholeTransceiver.sh b/evm/sh/deployWormholeGuardiansAdapter.sh similarity index 100% rename from evm/sh/deployWormholeTransceiver.sh rename to evm/sh/deployWormholeGuardiansAdapter.sh diff --git a/evm/sh/deployWormholeGuardiansAdapterWithExecutor.sh b/evm/sh/deployWormholeGuardiansAdapterWithExecutor.sh new file mode 100755 index 0000000..540ac9b --- /dev/null +++ b/evm/sh/deployWormholeGuardiansAdapterWithExecutor.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# +# This script deploys the WormholeGuardiansAdapterWithExecutor contract. +# Usage: RPC_URL= MNEMONIC= OUR_CHAIN_ID= EVM_CHAIN_ID= ADMIN= ENDPOINT= EXECUTOR= WORMHOLE= CONSISTENCY_LEVEL= ./sh/deployWormholeGuardiansAdapterWithExecutor.sh +# tilt: ENDPOINT=0x1aBE68277AE236083947f2551FEe8b885efCA8f5 EXECUTOR= ./sh/deployWormholeGuardiansAdapterWithExecutor.sh +# + +[[ -z $ENDPOINT ]] && { echo "Missing ENDPOINT"; exit 1; } +[[ -z $EXECUTOR ]] && { echo "Missing EXECUTOR"; exit 1; } + +if [ "${RPC_URL}X" == "X" ]; then + RPC_URL=http://localhost:8545 +fi + +if [ "${MNEMONIC}X" == "X" ]; then + MNEMONIC=0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d +fi + +if [ "${OUR_CHAIN_ID}X" == "X" ]; then + OUR_CHAIN_ID=2 +fi + +if [ "${EVM_CHAIN_ID}X" == "X" ]; then + EVM_CHAIN_ID=1337 +fi + +if [ "${ADMIN}X" == "X" ]; then + ADMIN=0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 +fi + +if [ "${WORMHOLE}X" == "X" ]; then + WORMHOLE=0xC89Ce4735882C9F0f0FE26686c53074E09B0D550 +fi + +if [ "${CONSISTENCY_LEVEL}X" == "X" ]; then + CONSISTENCY_LEVEL=200 +fi + +forge script ./script/DeployWormholeGuardiansAdapterWithExecutor.s.sol:DeployWormholeGuardiansAdapterWithExecutor \ + --sig "run(uint16,address,address,address,address,uint8)" $OUR_CHAIN_ID $ADMIN $ENDPOINT $EXECUTOR $WORMHOLE $CONSISTENCY_LEVEL \ + --rpc-url "$RPC_URL" \ + --private-key "$MNEMONIC" \ + --broadcast ${FORGE_ARGS} + +returnInfo=$(cat ./broadcast/DeployWormholeGuardiansAdapterWithExecutor.s.sol/$EVM_CHAIN_ID/run-latest.json) + +DEPLOYED_ADDRESS=$(jq -r '.returns.deployedAddress.value' <<< "$returnInfo") +echo "Deployed adapter address: $DEPLOYED_ADDRESS" diff --git a/evm/src/WormholeGuardiansAdapter.sol b/evm/src/WormholeGuardiansAdapter.sol index 5ebeb76..44ea2fe 100644 --- a/evm/src/WormholeGuardiansAdapter.sol +++ b/evm/src/WormholeGuardiansAdapter.sol @@ -13,12 +13,10 @@ string constant wormholeGuardiansAdapterVersionString = "WormholeGuardiansAdapte contract WormholeGuardiansAdapter is IWormholeGuardiansAdapter { using BytesParsing for bytes; // Used by _decodePayload - string public constant versionString = wormholeGuardiansAdapterVersionString; - address public admin; - address public pendingAdmin; - // ==================== Immutables =============================================== + address public admin; + address public pendingAdmin; uint16 public immutable ourChain; IEndpointAdapter public immutable endpoint; IWormhole public immutable wormhole; @@ -168,8 +166,8 @@ contract WormholeGuardiansAdapter is IWormholeGuardiansAdapter { // =============== Interface =============================================================== /// @inheritdoc IAdapter - function getAdapterType() external pure returns (string memory) { - return versionString; + function getAdapterType() external pure virtual returns (string memory) { + return wormholeGuardiansAdapterVersionString; } /// @inheritdoc IAdapter @@ -186,7 +184,7 @@ contract WormholeGuardiansAdapter is IWormholeGuardiansAdapter { UniversalAddress dstAddr, bytes32 payloadHash, address // refundAddr - ) external payable onlyEndpoint { + ) external payable virtual onlyEndpoint { bytes memory payload = _encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); wormhole.publishMessage{value: msg.value}(0, payload, consistencyLevel); emit MessageSent(srcAddr, dstChain, dstAddr, sequence, payloadHash); diff --git a/evm/src/WormholeGuardiansAdapterWithExecutor.sol b/evm/src/WormholeGuardiansAdapterWithExecutor.sol new file mode 100644 index 0000000..f09dd65 --- /dev/null +++ b/evm/src/WormholeGuardiansAdapterWithExecutor.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +import "example-messaging-executor/evm/src/interfaces/IExecutor.sol"; + +import "./WormholeGuardiansAdapter.sol"; + +string constant wormholeGuardiansAdapterWithExecutorVersionString = "WormholeGuardiansAdapterWithExecutor-0.0.1"; + +contract WormholeGuardiansAdapterWithExecutor is WormholeGuardiansAdapter { + // ==================== Immutables =============================================== + + IExecutor public immutable executor; + + // ==================== Constructor ============================================== + + constructor( + uint16 _ourChain, + address _admin, + address _endpoint, + address _executor, + address _wormhole, + uint8 _consistencyLevel + ) WormholeGuardiansAdapter(_ourChain, _admin, _endpoint, _wormhole, _consistencyLevel) { + assert(_executor != address(0)); + executor = IExecutor(_executor); + } + + /// @inheritdoc IAdapter + function getAdapterType() external pure override returns (string memory) { + return wormholeGuardiansAdapterWithExecutorVersionString; + } + + /// @inheritdoc IAdapter + /// @dev The caller should set the delivery price in msg.value. + function sendMessage( + UniversalAddress srcAddr, + uint64 sequence, + uint16 dstChain, + UniversalAddress dstAddr, + bytes32 payloadHash, + address refundAddr + ) external payable override onlyEndpoint { + bytes memory payload = _encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); + wormhole.publishMessage{value: msg.value}(0, payload, consistencyLevel); + emit MessageSent(srcAddr, dstChain, dstAddr, sequence, payloadHash); + + // TODO: These should be passed in. + bytes memory signedQuote = new bytes(0); + bytes memory relayInstructions = new bytes(0); + + executor.requestExecution(dstChain, dstAddr.toBytes32(), refundAddr, signedQuote, payload, relayInstructions); + } +} diff --git a/evm/test/WormholeGuardiansAdapter.t.sol b/evm/test/WormholeGuardiansAdapter.t.sol index b38d2b3..c7f3d03 100644 --- a/evm/test/WormholeGuardiansAdapter.t.sol +++ b/evm/test/WormholeGuardiansAdapter.t.sol @@ -2,15 +2,16 @@ pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; -import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; import "../src/WormholeGuardiansAdapter.sol"; import "../src/interfaces/IWormholeGuardiansAdapter.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockWormhole.sol"; contract WormholeGuardiansAdapterForTest is WormholeGuardiansAdapter { - constructor(uint16 _ourChain, address _admin, address _endpoint, address srcWormhole, uint8 _consistencyLevel) - WormholeGuardiansAdapter(_ourChain, _admin, _endpoint, srcWormhole, _consistencyLevel) + constructor(uint16 _ourChain, address _admin, address _endpoint, address _wormhole, uint8 _consistencyLevel) + WormholeGuardiansAdapter(_ourChain, _admin, _endpoint, _wormhole, _consistencyLevel) {} function encodePayload( @@ -38,112 +39,6 @@ contract WormholeGuardiansAdapterForTest is WormholeGuardiansAdapter { } } -contract MockWormhole { - uint256 public constant fixedMessageFee = 250; - - uint16 public immutable ourChain; - - bool public validFlag; - string public invalidReason; - - // These are incremented on calls. - uint256 public messagesSent; - uint32 public seqSent; - - // These are set on calls. - uint256 public lastDeliveryPrice; - uint32 public lastNonce; - uint8 public lastConsistencyLevel; - bytes public lastPayload; - bytes public lastVaa; - bytes32 public lastVaaHash; - - constructor(uint16 _ourChain) { - ourChain = _ourChain; - validFlag = true; - } - - function setValidFlag(bool v, string memory reason) external { - validFlag = v; - invalidReason = reason; - } - - function messageFee() external pure returns (uint256) { - return fixedMessageFee; - } - - function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel) - external - payable - returns (uint64 sequence) - { - seqSent += 1; - sequence = seqSent; - - lastDeliveryPrice = msg.value; - lastNonce = nonce; - lastConsistencyLevel = consistencyLevel; - lastPayload = payload; - messagesSent += 1; - - bytes32 sender = UniversalAddressLibrary.fromAddress(msg.sender).toBytes32(); - bytes32 hash = keccak256(payload); - - lastVaa = abi.encode(ourChain, sender, sequence, hash, payload); - lastVaaHash = hash; - } - - function parseAndVerifyVM(bytes calldata encodedVM) - external - view - returns (IWormhole.VM memory vm, bool valid, string memory reason) - { - valid = validFlag; - reason = invalidReason; - - // These are the fields that the adapter uses: - // vm.emitterChainId - // vm.emitterAddress - // vm.hash - // vm.payload - - (vm.emitterChainId, vm.emitterAddress, vm.sequence, vm.hash, vm.payload) = - abi.decode(encodedVM, (uint16, bytes32, uint64, bytes32, bytes)); - } -} - -contract MockEndpoint { - uint16 public immutable ourChain; - - // These are set on calls. - uint16 public lastSourceChain; - UniversalAddress public lastSourceAddress; - uint64 public lastSequence; - uint16 public lastDestinationChain; - UniversalAddress public lastDestinationAddress; - bytes32 public lastPayloadHash; - - constructor(uint16 _ourChain) { - ourChain = _ourChain; - } - - function attestMessage( - uint16 srcChain, - UniversalAddress srcAddr, - uint64 sequence, - uint16 dstChain, - UniversalAddress destinationAddress, - bytes32 payloadHash - ) external { - lastSourceChain = srcChain; - lastSourceAddress = srcAddr; - lastSequence = sequence; - lastDestinationChain = dstChain; - lastDestinationAddress = destinationAddress; - lastPayloadHash = payloadHash; - } -} - contract WormholeGuardiansAdapterTest is Test { address admin = address(0xabcdef); MockEndpoint srcEndpoint; @@ -379,8 +274,8 @@ contract WormholeGuardiansAdapterTest is Test { function test_getAdapterType() public view { require( keccak256(abi.encodePacked(srcAdapter.getAdapterType())) - == keccak256(abi.encodePacked(srcAdapter.versionString())), - "srcAdapter type mismatch" + == keccak256(abi.encodePacked(wormholeGuardiansAdapterVersionString)), + "adapter type mismatch" ); } diff --git a/evm/test/WormholeGuardiansAdapterWithExecutor.t.sol b/evm/test/WormholeGuardiansAdapterWithExecutor.t.sol new file mode 100644 index 0000000..1220703 --- /dev/null +++ b/evm/test/WormholeGuardiansAdapterWithExecutor.t.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; +import "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; +import "../src/WormholeGuardiansAdapterWithExecutor.sol"; +import "../src/interfaces/IWormholeGuardiansAdapter.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; +import "./mocks/MockWormhole.sol"; + +contract WormholeGuardiansAdapterWithExecutorForTest is WormholeGuardiansAdapterWithExecutor { + constructor( + uint16 _ourChain, + address _admin, + address _endpoint, + address _executor, + address _wormhole, + uint8 _consistencyLevel + ) WormholeGuardiansAdapterWithExecutor(_ourChain, _admin, _endpoint, _executor, _wormhole, _consistencyLevel) {} + + function encodePayload( + UniversalAddress srcAddr, + uint64 sequence, + uint16 dstChain, + UniversalAddress dstAddr, + bytes32 payloadHash + ) external pure returns (bytes memory payload) { + return _encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); + } +} + +contract WormholeGuardiansAdapterWithExecutorTest is Test { + address admin = address(0xabcdef); + MockEndpoint srcEndpoint; + MockEndpoint destEndpoint; + MockExecutor srcExecutor; + MockExecutor destExecutor; + address endpointAddr; + address executorAddr; + address integratorAddr = address(0xabcded); + address userA = address(0xabcdec); + address someoneElse = address(0xabcdeb); + MockWormhole srcWormhole; + WormholeGuardiansAdapterWithExecutorForTest public srcAdapter; + MockWormhole destWormhole; + WormholeGuardiansAdapterWithExecutorForTest public destAdapter; + uint8 consistencyLevel = 200; + + uint16 ourChain = 42; + + uint16 srcChain = 42; + uint16 destChain = 43; + + uint16 peerChain1 = 1; + address peerAddress1 = address(0x123456); + + uint16 peerChain2 = 2; + address peerAddress2 = address(0x123456); + + uint16 peerChain3 = 3; + + function setUp() public { + srcEndpoint = new MockEndpoint(srcChain); + endpointAddr = address(srcEndpoint); + destEndpoint = new MockEndpoint(destChain); + + srcExecutor = new MockExecutor(srcChain); + executorAddr = address(srcExecutor); + destExecutor = new MockExecutor(destChain); + + srcWormhole = new MockWormhole(srcChain); + destWormhole = new MockWormhole(destChain); + + srcAdapter = new WormholeGuardiansAdapterWithExecutorForTest( + srcChain, admin, endpointAddr, executorAddr, address(srcWormhole), consistencyLevel + ); + + destAdapter = new WormholeGuardiansAdapterWithExecutorForTest( + destChain, admin, address(destEndpoint), address(destExecutor), address(destWormhole), consistencyLevel + ); + + // Give everyone some money to play with. + vm.deal(integratorAddr, 1 ether); + vm.deal(endpointAddr, 1 ether); + vm.deal(userA, 1 ether); + vm.deal(someoneElse, 1 ether); + } + + function test_init() public view { + require(srcAdapter.ourChain() == ourChain, "ourChain is not right"); + require(srcAdapter.admin() == admin, "admin is not right"); + require(address(srcAdapter.endpoint()) == endpointAddr, "endpoint is not right"); + require(address(srcAdapter.executor()) == executorAddr, "executor is not right"); + require(address(srcAdapter.wormhole()) == address(srcWormhole), "srcWormhole is not right"); + require(srcAdapter.consistencyLevel() == consistencyLevel, "consistencyLevel is not right"); + } + + function test_invalidInit() public { + // ourChain can't be zero. + vm.expectRevert(); + new WormholeGuardiansAdapterWithExecutor( + 0, admin, address(destEndpoint), address(destExecutor), address(destWormhole), consistencyLevel + ); + + // admin can't be zero. + vm.expectRevert(); + new WormholeGuardiansAdapterWithExecutor( + destChain, address(0), address(destEndpoint), address(destExecutor), address(destWormhole), consistencyLevel + ); + + // endpoint can't be zero. + vm.expectRevert(); + new WormholeGuardiansAdapterWithExecutor( + destChain, admin, address(0), address(destExecutor), address(destWormhole), consistencyLevel + ); + + // executor can't be zero. + vm.expectRevert(); + new WormholeGuardiansAdapterWithExecutor( + destChain, admin, address(destEndpoint), address(0), address(destWormhole), consistencyLevel + ); + + // wormhole can't be zero. + vm.expectRevert(); + new WormholeGuardiansAdapterWithExecutor( + destChain, admin, address(destEndpoint), address(destExecutor), address(0), consistencyLevel + ); + } + + function test_getAdapterType() public view { + require( + keccak256(abi.encodePacked(srcAdapter.getAdapterType())) + == keccak256(abi.encodePacked(wormholeGuardiansAdapterWithExecutorVersionString)), + "adapter type mismatch" + ); + } + + // TODO: This should do something different! + function test_sendMessage() public { + UniversalAddress srcAddr = UniversalAddressLibrary.fromAddress(address(userA)); + uint16 dstChain = peerChain1; + UniversalAddress dstAddr = UniversalAddressLibrary.fromAddress(address(peerAddress1)); + uint64 sequence = 42; + bytes32 payloadHash = keccak256("message one"); + address refundAddr = userA; + uint256 deliverPrice = 382; + + vm.startPrank(endpointAddr); + srcAdapter.sendMessage{value: deliverPrice}(srcAddr, sequence, dstChain, dstAddr, payloadHash, refundAddr); + + require(srcWormhole.messagesSent() == 1, "Message count is wrong"); + require(srcWormhole.lastNonce() == 0, "Nonce is wrong"); + require(srcWormhole.lastConsistencyLevel() == consistencyLevel, "Consistency level is wrong"); + require(srcWormhole.lastDeliveryPrice() == deliverPrice, "Deliver price is wrong"); + + bytes memory expectedPayload = srcAdapter.encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); + require( + keccak256(abi.encodePacked(srcWormhole.lastPayload())) == keccak256(expectedPayload), "Payload is wrong" + ); + + // Only the endpoint can call send message. + vm.startPrank(someoneElse); + vm.expectRevert(abi.encodeWithSelector(IAdapter.CallerNotEndpoint.selector, someoneElse)); + srcAdapter.sendMessage{value: deliverPrice}(srcAddr, sequence, dstChain, dstAddr, payloadHash, refundAddr); + } +} diff --git a/evm/test/mocks/MockEndpoint.sol b/evm/test/mocks/MockEndpoint.sol new file mode 100644 index 0000000..5c5deb6 --- /dev/null +++ b/evm/test/mocks/MockEndpoint.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 "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; + +contract MockEndpoint { + uint16 public immutable ourChain; + + // These are set on calls. + uint16 public lastSourceChain; + UniversalAddress public lastSourceAddress; + uint64 public lastSequence; + uint16 public lastDestinationChain; + UniversalAddress public lastDestinationAddress; + bytes32 public lastPayloadHash; + + constructor(uint16 _ourChain) { + ourChain = _ourChain; + } + + function attestMessage( + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + uint16 dstChain, + UniversalAddress destinationAddress, + bytes32 payloadHash + ) external { + lastSourceChain = srcChain; + lastSourceAddress = srcAddr; + lastSequence = sequence; + lastDestinationChain = dstChain; + lastDestinationAddress = destinationAddress; + lastPayloadHash = payloadHash; + } +} diff --git a/evm/test/mocks/MockExecutor.sol b/evm/test/mocks/MockExecutor.sol new file mode 100644 index 0000000..f601e97 --- /dev/null +++ b/evm/test/mocks/MockExecutor.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 "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; + +contract MockExecutor { + uint16 public immutable ourChain; + + // These are set on calls. + uint16 public lastDstChain; + bytes32 public lastDstAddr; + address public lastRefundAddr; + bytes public lastSignedQuote; + bytes public lastRequestBytes; + bytes public lastRelayInstructions; + + constructor(uint16 _ourChain) { + ourChain = _ourChain; + } + + function requestExecution( + uint16 dstChain, + bytes32 dstAddr, + address refundAddr, + bytes calldata signedQuote, + bytes calldata requestBytes, + bytes calldata relayInstructions + ) external { + lastDstChain = dstChain; + lastDstAddr = dstAddr; + lastRefundAddr = refundAddr; + lastSignedQuote = signedQuote; + lastRequestBytes = requestBytes; + lastRelayInstructions = relayInstructions; + } +} diff --git a/evm/test/mocks/MockWormhole.sol b/evm/test/mocks/MockWormhole.sol new file mode 100644 index 0000000..439cf74 --- /dev/null +++ b/evm/test/mocks/MockWormhole.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; +import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; + +contract MockWormhole { + uint256 public constant fixedMessageFee = 250; + + uint16 public immutable ourChain; + + bool public validFlag; + string public invalidReason; + + // These are incremented on calls. + uint256 public messagesSent; + uint32 public seqSent; + + // These are set on calls. + uint256 public lastDeliveryPrice; + uint32 public lastNonce; + uint8 public lastConsistencyLevel; + bytes public lastPayload; + bytes public lastVaa; + bytes32 public lastVaaHash; + + constructor(uint16 _ourChain) { + ourChain = _ourChain; + validFlag = true; + } + + function setValidFlag(bool v, string memory reason) external { + validFlag = v; + invalidReason = reason; + } + + function messageFee() external pure returns (uint256) { + return fixedMessageFee; + } + + function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel) + external + payable + returns (uint64 sequence) + { + seqSent += 1; + sequence = seqSent; + + lastDeliveryPrice = msg.value; + lastNonce = nonce; + lastConsistencyLevel = consistencyLevel; + lastPayload = payload; + messagesSent += 1; + + bytes32 sender = UniversalAddressLibrary.fromAddress(msg.sender).toBytes32(); + bytes32 hash = keccak256(payload); + + lastVaa = abi.encode(ourChain, sender, sequence, hash, payload); + lastVaaHash = hash; + } + + function parseAndVerifyVM(bytes calldata encodedVM) + external + view + returns (IWormhole.VM memory vm, bool valid, string memory reason) + { + valid = validFlag; + reason = invalidReason; + + // These are the fields that the adapter uses: + // vm.emitterChainId + // vm.emitterAddress + // vm.hash + // vm.payload + + (vm.emitterChainId, vm.emitterAddress, vm.sequence, vm.hash, vm.payload) = + abi.decode(encodedVM, (uint16, bytes32, uint64, bytes32, bytes)); + } +}