Skip to content

Commit

Permalink
Merge pull request #163 from valory-xyz/wormhole
Browse files Browse the repository at this point in the history
feat: wormhole relayer for arbitrary msg.value-s
  • Loading branch information
kupermind authored Nov 7, 2024
2 parents 3e7193d + 89af5ed commit b8ca72b
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ fa17ac186753911eb9d2ae0a4ab59db5f7e8e563:scripts/deployment/bridges/solana/test/
9fe74d22fc7a98851616d58985e0355914662552:scripts/deployment/globals_mainnet.json:generic-api-key:1
9fe74d22fc7a98851616d58985e0355914662552:scripts/deployment/globals_mainnet.json:generic-api-key:2
391af0ca125e1e7f03149195d4bd04d023bb8b0f:scripts/deployment/globals_mainnet.json:generic-api-key:1
bbdf1f63af520cbcf4df1ba814772edb23fa1558:scripts/deployment/globals_mainnet.json:generic-api-key:1
150 changes: 150 additions & 0 deletions abis/0.8.28/WormholeRelayer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "WormholeRelayer",
"sourceName": "contracts/bridges/WormholeRelayer.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_wormholeRelayer",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "provided",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expected",
"type": "uint256"
}
],
"name": "LowerThan",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuard",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "ZeroValue",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "leftovers",
"type": "uint256"
}
],
"name": "LeftoversRefunded",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "targetChain",
"type": "uint16"
},
{
"internalType": "address",
"name": "targetAddress",
"type": "address"
},
{
"internalType": "bytes",
"name": "payload",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "receiverValue",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "gasLimit",
"type": "uint256"
},
{
"internalType": "uint16",
"name": "refundChain",
"type": "uint16"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "sendPayloadToEvm",
"outputs": [
{
"internalType": "uint64",
"name": "sequence",
"type": "uint64"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "wormholeRelayer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x60a060405260015f553480156012575f5ffd5b50604051610826380380610826833981016040819052602f91603f565b6001600160a01b0316608052606a565b5f60208284031215604e575f5ffd5b81516001600160a01b03811681146063575f5ffd5b9392505050565b60805161079761008f5f395f8181606e0152818161020301526103da01526107975ff3fe608060405260043610610028575f3560e01c80634b5ca6f41461002c578063da25b7251461005d575b5f5ffd5b61003f61003a3660046104d6565b6100b5565b60405167ffffffffffffffff90911681526020015b60405180910390f35b348015610068575f5ffd5b506100907f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610054565b5f60015f5411156100f2576040517f8beb9d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f5573ffffffffffffffffffffffffffffffffffffffff8716158061012d575073ffffffffffffffffffffffffffffffffffffffff8216155b15610164576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61ffff8816158061017457508551155b8061017d575083155b8061018a575061ffff8316155b156101c1576040517f7c946ed700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc23ee3c300000000000000000000000000000000000000000000000000000000815261ffff8916600482015260248101869052604481018590525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063c23ee3c3906064016040805180830381865afa15801561025c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102809190610619565b509050348111156102cb576040517f491a2bb1000000000000000000000000000000000000000000000000000000008152346004820152602481018290526044015b60405180910390fd5b5f6102d6823461063b565b9050801561039d576040515f90329083908381818185875af1925050503d805f811461031d576040519150601f19603f3d011682016040523d82523d5f602084013e610322565b606091505b5050905080610366576040517f1c43b976000000000000000000000000000000000000000000000000000000008152326004820152602481018390526044016102c2565b60405182815232907f8e49ed3e274fbea1556bdfaa9a37a0c28445bfe26d57beaf9f04fd517aa417419060200160405180910390a2505b6040517f4b5ca6f400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634b5ca6f490849061041d908e908e908e908e908e908e908e90600401610679565b60206040518083038185885af1158015610439573d5f5f3e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061045e9190610733565b60015f559a9950505050505050505050565b803561ffff81168114610481575f5ffd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610481575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f5f5f5f5f5f5f60e0888a0312156104ec575f5ffd5b6104f588610470565b965061050360208901610486565b9550604088013567ffffffffffffffff81111561051e575f5ffd5b8801601f81018a1361052e575f5ffd5b803567ffffffffffffffff811115610548576105486104a9565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff821117156105b4576105b46104a9565b6040528181528282016020018c10156105cb575f5ffd5b816020840160208301375f9181016020019190915295505060608801359350608088013592506105fd60a08901610470565b915061060b60c08901610486565b905092959891949750929550565b5f5f6040838503121561062a575f5ffd5b505080516020909101519092909150565b81810381811115610673577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b61ffff8816815273ffffffffffffffffffffffffffffffffffffffff8716602082015260e060408201525f86518060e0840152806020890161010085015e5f61010082850101526101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505085606083015284608083015261ffff841660a083015261072760c083018473ffffffffffffffffffffffffffffffffffffffff169052565b98975050505050505050565b5f60208284031215610743575f5ffd5b815167ffffffffffffffff8116811461075a575f5ffd5b939250505056fea264697066735822122080362fd425a0ad8c3722a03f29d26a8c8118ef255714070a663ec545a85a33bb64736f6c634300081c0033",
"deployedBytecode": "0x608060405260043610610028575f3560e01c80634b5ca6f41461002c578063da25b7251461005d575b5f5ffd5b61003f61003a3660046104d6565b6100b5565b60405167ffffffffffffffff90911681526020015b60405180910390f35b348015610068575f5ffd5b506100907f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610054565b5f60015f5411156100f2576040517f8beb9d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f5573ffffffffffffffffffffffffffffffffffffffff8716158061012d575073ffffffffffffffffffffffffffffffffffffffff8216155b15610164576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61ffff8816158061017457508551155b8061017d575083155b8061018a575061ffff8316155b156101c1576040517f7c946ed700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc23ee3c300000000000000000000000000000000000000000000000000000000815261ffff8916600482015260248101869052604481018590525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063c23ee3c3906064016040805180830381865afa15801561025c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102809190610619565b509050348111156102cb576040517f491a2bb1000000000000000000000000000000000000000000000000000000008152346004820152602481018290526044015b60405180910390fd5b5f6102d6823461063b565b9050801561039d576040515f90329083908381818185875af1925050503d805f811461031d576040519150601f19603f3d011682016040523d82523d5f602084013e610322565b606091505b5050905080610366576040517f1c43b976000000000000000000000000000000000000000000000000000000008152326004820152602481018390526044016102c2565b60405182815232907f8e49ed3e274fbea1556bdfaa9a37a0c28445bfe26d57beaf9f04fd517aa417419060200160405180910390a2505b6040517f4b5ca6f400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634b5ca6f490849061041d908e908e908e908e908e908e908e90600401610679565b60206040518083038185885af1158015610439573d5f5f3e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061045e9190610733565b60015f559a9950505050505050505050565b803561ffff81168114610481575f5ffd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610481575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f5f5f5f5f5f5f60e0888a0312156104ec575f5ffd5b6104f588610470565b965061050360208901610486565b9550604088013567ffffffffffffffff81111561051e575f5ffd5b8801601f81018a1361052e575f5ffd5b803567ffffffffffffffff811115610548576105486104a9565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff821117156105b4576105b46104a9565b6040528181528282016020018c10156105cb575f5ffd5b816020840160208301375f9181016020019190915295505060608801359350608088013592506105fd60a08901610470565b915061060b60c08901610486565b905092959891949750929550565b5f5f6040838503121561062a575f5ffd5b505080516020909101519092909150565b81810381811115610673577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b61ffff8816815273ffffffffffffffffffffffffffffffffffffffff8716602082015260e060408201525f86518060e0840152806020890161010085015e5f61010082850101526101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505085606083015284608083015261ffff841660a083015261072760c083018473ffffffffffffffffffffffffffffffffffffffff169052565b98975050505050505050565b5f60208284031215610743575f5ffd5b815167ffffffffffffffff8116811461075a575f5ffd5b939250505056fea264697066735822122080362fd425a0ad8c3722a03f29d26a8c8118ef255714070a663ec545a85a33bb64736f6c634300081c0033",
"linkReferences": {},
"deployedLinkReferences": {}
}
153 changes: 153 additions & 0 deletions contracts/bridges/WormholeRelayer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IWormhole {
/// @dev Returns the price to request a relay to chain `targetChain`, using the default delivery provider
///
/// @param targetChain in Wormhole Chain ID format
/// @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
/// @param gasLimit gas limit with which to call `targetAddress`.
/// @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
/// @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
/// if a refundAddress is specified.
/// Note: This value can be overridden by the delivery provider on the target chain. The returned value here should be considered to be a
/// promise by the delivery provider of the amount of refund per gas unused that will be returned to the refundAddress at the target chain.
/// If a delivery provider decides to override, this will be visible as part of the emitted Delivery event on the target chain.
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit
) external view returns (uint256 nativePriceQuote, uint256 targetChainRefundPerGasUnused);

/// @dev Publishes an instruction for the default delivery provider
/// to relay a payload to the address `targetAddress` on chain `targetChain`
/// with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
///
/// Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
/// `targetAddress` must implement the IWormholeReceiver interface
///
/// This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
///
/// @param targetChain in Wormhole Chain ID format
/// @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
/// @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
/// @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
/// @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
/// `targetChainRefundPerGasUnused` rate quoted by the delivery provider
/// @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
/// @param refundAddress The address on `refundChain` to deliver any refund to
/// @return sequence sequence number of published VAA containing delivery instructions
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
}

/// @dev Provided zero address.
error ZeroAddress();

/// @dev Zero value when it has to be different from zero.
error ZeroValue();

/// @dev Received lower value than the expected one.
/// @param provided Provided value is lower.
/// @param expected Expected value.
error LowerThan(uint256 provided, uint256 expected);

/// @dev Failure of a native token transfer.
/// @param to Address `to`.
/// @param amount Token amount.
error TransferFailed(address to, uint256 amount);

// @dev Reentrancy guard.
error ReentrancyGuard();

/// @title WormholeRelayer - Smart contract for the contract interaction with wormhole relayer with any msg.value
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
contract WormholeRelayer {
event LeftoversRefunded(address indexed sender, uint256 leftovers);

// L1 Wormhole Relayer address that sends the message across the bridge
address public immutable wormholeRelayer;

// Reentrancy lock
uint256 internal _locked = 1;

/// @dev WormholeRelayer constructor.
/// @param _wormholeRelayer Wormhole relayer address.
constructor(address _wormholeRelayer) {
wormholeRelayer = _wormholeRelayer;
}

/// @dev Relays a payload via Wormhole relayer to the address `targetAddress` on chain `targetChain`
/// with gas limit `gasLimit` and `msg.value` equal to `receiverValue`.
/// @notice This function takes arbitrary `msg.value` and adjusts the cost for relayer exact amount.
/// @param targetChain in Wormhole Chain ID format.
/// @param targetAddress address to call on targetChain (that implements IWormholeReceiver).
/// @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`.
/// @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress`
/// in `targetChain` currency units.
/// @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
/// `targetChainRefundPerGasUnused` rate quoted by the delivery provider.
/// @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format.
/// @param refundAddress The address on `refundChain` to deliver any refund to.
/// @return sequence Sequence number of published VAA containing delivery instructions.
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence) {
if (_locked > 1) {
revert ReentrancyGuard();
}
_locked = 2;

// Check for zero addresses
if (targetAddress == address(0) || refundAddress == address(0)) {
revert ZeroAddress();
}

// Check for zero values
if (targetChain == 0 || payload.length == 0 || gasLimit == 0 || refundChain == 0) {
revert ZeroValue();
}

// Get the message cost in order to adjust leftovers
(uint256 cost, ) = IWormhole(wormholeRelayer).quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit);

// Check fot msg.value to cover the cost
if (cost > msg.value) {
revert LowerThan(msg.value, cost);
}

// Return value leftovers
uint256 leftovers = msg.value - cost;

// Send leftover amount back to the sender, if any
if (leftovers > 0) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = tx.origin.call{value: leftovers}("");
if (!success) {
revert TransferFailed(tx.origin, leftovers);
}

emit LeftoversRefunded(tx.origin, leftovers);
}

// Send payload via the Wormhole relayer with exact required cost
sequence = IWormhole(wormholeRelayer).sendPayloadToEvm{value: cost}(targetChain, targetAddress, payload,
receiverValue, gasLimit, refundChain, refundAddress);

_locked = 1;
}
}
1 change: 0 additions & 1 deletion scripts/deployment/deploy_23_vote_weighting.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*global process*/

const { expect } = require("chai");
const { ethers } = require("hardhat");
const { LedgerSigner } = require("@anders-t/ethers-ledger");

Expand Down
1 change: 0 additions & 1 deletion scripts/deployment/deploy_24_burner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*global process*/

const { expect } = require("chai");
const { ethers } = require("hardhat");
const { LedgerSigner } = require("@anders-t/ethers-ledger");

Expand Down
Loading

0 comments on commit b8ca72b

Please sign in to comment.