Skip to content

Commit

Permalink
evm: Add adapter with executor
Browse files Browse the repository at this point in the history
  • Loading branch information
bruce-riley committed Dec 3, 2024
1 parent 7544aab commit 8f0b523
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 118 deletions.
File renamed without changes.
53 changes: 53 additions & 0 deletions evm/script/DeployWormholeGuardiansAdapterWithExecutor.s.sol
Original file line number Diff line number Diff line change
@@ -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));
}
}
File renamed without changes.
49 changes: 49 additions & 0 deletions evm/sh/deployWormholeGuardiansAdapterWithExecutor.sh
Original file line number Diff line number Diff line change
@@ -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"
12 changes: 5 additions & 7 deletions evm/src/WormholeGuardiansAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down
54 changes: 54 additions & 0 deletions evm/src/WormholeGuardiansAdapterWithExecutor.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
117 changes: 6 additions & 111 deletions evm/test/WormholeGuardiansAdapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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"
);
}

Expand Down
Loading

0 comments on commit 8f0b523

Please sign in to comment.