Code cleanup (#109)

* DRY: Remove duplicate structs

* Move encode/decode/convert-request-to-instruction related files to CoreRelayerMessages

* remove rolloverchain; now it is just the chain of the first request

* forge test fix to accomdoate for removing two getters

* DRY: Remove repeated error messages

* Remove miscellaneous comments

* DRY send and forward

* replace relayprovider interface with address to match the IWormholeRelayer interface

* forge fmt

* Remove byteslib from CoreRelayer

* Remove the encoding and decoding of the delivery request -> just store the struct itself!

* forge fmt

* Rewriting of checks in send/resend/forward

* test passes

* DRY - Emit only one event

* DRY

* forge fmt

* Fix typescript error

* using IWormholeRelayer

* Consistent naming

* call wormhole.messageFee() once

* Remove unnecessary line

* Compute the length once, not every iteration

* forge tests pass
This commit is contained in:
derpy-duck 2023-02-27 14:32:57 -05:00 committed by GitHub
parent a1df44b3ee
commit f7de3d649d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 503 additions and 843 deletions

View File

@ -3,15 +3,13 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../libraries/external/BytesLib.sol"; import "../interfaces/IWormholeRelayer.sol";
import "../interfaces/IWormholeReceiver.sol"; import "../interfaces/IWormholeReceiver.sol";
import "../interfaces/IDelivery.sol";
import "./CoreRelayerGovernance.sol"; import "./CoreRelayerGovernance.sol";
import "./CoreRelayerStructs.sol"; import "./CoreRelayerStructs.sol";
contract CoreRelayer is CoreRelayerGovernance { contract CoreRelayer is CoreRelayerGovernance {
using BytesLib for bytes;
enum DeliveryStatus { enum DeliveryStatus {
SUCCESS, SUCCESS,
RECEIVER_FAILURE, RECEIVER_FAILURE,
@ -28,113 +26,39 @@ contract CoreRelayer is CoreRelayerGovernance {
DeliveryStatus status DeliveryStatus status
); );
error FundsTooMuch(); function send(IWormholeRelayer.Send memory request, uint32 nonce, address relayProvider)
error MaxTransactionFeeNotEnough();
error MsgValueTooLow(); // msg.value must cover the budget specified
error NonceIsZero();
error ForwardRequestFromWrongAddress();
error NoDeliveryInProcess();
error CantRequestMultipleForwards();
error RelayProviderDoesNotSupportTargetChain();
error RolloverChainNotIncluded(); // Rollover chain was not included in the forwarding request
error ChainNotFoundInSends(uint16 chainId); // Required chain not found in the delivery requests
error ReentrantCall();
error InvalidEmitterInOriginalDeliveryVM(uint8 index);
error InvalidRedeliveryVM(string reason);
error InvalidEmitterInRedeliveryVM();
error MismatchingRelayProvidersInRedelivery(); // The same relay provider must be specified when doing a single VAA redeliver
error InvalidVaa(uint8 index);
error InvalidEmitter();
error SendNotSufficientlyFunded(); // This delivery request was not sufficiently funded, and must request redelivery
error UnexpectedRelayer(); // Specified relayer is not the relayer delivering the message
error InsufficientRelayerFunds(); // The relayer didn't pass sufficient funds (msg.value does not cover the necessary budget fees)
error AlreadyDelivered(); // The message was already delivered.
error TargetChainIsNotThisChain(uint16 targetChainId);
function send(Send memory request, uint32 nonce, IRelayProvider provider)
public public
payable payable
returns (uint64 sequence) returns (uint64 sequence)
{ {
Send[] memory requests = new Send[](1); return multichainSend(multichainSendContainer(request, relayProvider), nonce);
requests[0] = request;
MultichainSend memory container = MultichainSend({relayProviderAddress: address(provider), requests: requests});
return multichainSend(container, nonce);
} }
function forward(Send memory request, uint32 nonce, IRelayProvider provider) public payable { function forward(IWormholeRelayer.Send memory request, uint32 nonce, address relayProvider) public payable {
Send[] memory requests = new Send[](1); return multichainForward(multichainSendContainer(request, relayProvider), nonce);
requests[0] = request;
MultichainSend memory container = MultichainSend({relayProviderAddress: address(provider), requests: requests});
return multichainForward(container, request.targetChain, nonce);
} }
function resend(ResendByTx memory request, uint32 nonce, IRelayProvider provider) function resend(IWormholeRelayer.ResendByTx memory request, uint32 nonce, address relayProvider)
public public
payable payable
returns (uint64 sequence) returns (uint64 sequence)
{ {
(uint256 requestFee, uint256 maximumRefund, uint256 receiverValueTarget, bool isSufficient, uint8 reason) = updateWormholeMessageFee();
verifyFunding( bool isSufficient = request.newMaxTransactionFee + request.newReceiverValue + wormholeMessageFee() <= msg.value;
VerifyFundingCalculation({
provider: provider,
sourceChain: chainId(),
targetChain: request.targetChain,
maxTransactionFeeSource: request.newMaxTransactionFee,
receiverValueSource: request.newReceiverValue,
isDelivery: false
})
);
if (!isSufficient) { if (!isSufficient) {
if (reason == 26) { revert IWormholeRelayer.MsgValueTooLow();
revert MaxTransactionFeeNotEnough();
} else {
revert FundsTooMuch();
}
}
IWormhole wormhole = wormhole();
uint256 wormholeMessageFee = wormhole.messageFee();
uint256 totalFee = requestFee + wormholeMessageFee;
//Make sure the msg.value covers the budget they specified
if (msg.value < totalFee) {
revert MsgValueTooLow();
} }
sequence = emitRedelivery( IRelayProvider provider = IRelayProvider(relayProvider);
request, RedeliveryByTxHashInstruction memory instruction = convertResendToRedeliveryInstruction(request, provider);
nonce, checkRedeliveryInstruction(instruction, provider);
provider.getConsistencyLevel(),
receiverValueTarget, sequence = wormhole().publishMessage{value: wormholeMessageFee()}(
maximumRefund, nonce, encodeRedeliveryInstruction(instruction), provider.getConsistencyLevel()
provider,
wormhole,
wormholeMessageFee
); );
//Send the delivery fees to the specified address of the provider. //Send the delivery fees to the specified address of the provider.
pay(provider.getRewardAddress(), msg.value - wormholeMessageFee); pay(provider.getRewardAddress(), msg.value - wormholeMessageFee());
}
function emitRedelivery(
ResendByTx memory request,
uint32 nonce,
uint8 consistencyLevel,
uint256 receiverValueTarget,
uint256 maximumRefund,
IRelayProvider provider,
IWormhole wormhole,
uint256 wormholeMessageFee
) internal returns (uint64 sequence) {
bytes memory instruction = convertToEncodedRedeliveryByTxHashInstruction(
request,
receiverValueTarget,
maximumRefund,
calculateTargetGasRedeliveryAmount(request.targetChain, request.newMaxTransactionFee, provider),
provider
);
sequence = wormhole.publishMessage{value: wormholeMessageFee}(nonce, instruction, consistencyLevel);
} }
/** /**
@ -147,37 +71,33 @@ contract CoreRelayer is CoreRelayerGovernance {
* it checks that the passed nonce is not zero (VAAs with a nonce of zero will not be batched) * it checks that the passed nonce is not zero (VAAs with a nonce of zero will not be batched)
* it generates a VAA with the encoded DeliveryInstructions * it generates a VAA with the encoded DeliveryInstructions
*/ */
function multichainSend(MultichainSend memory deliveryRequests, uint32 nonce) function multichainSend(IWormholeRelayer.MultichainSend memory deliveryRequests, uint32 nonce)
public public
payable payable
returns (uint64 sequence) returns (uint64 sequence)
{ {
(uint256 totalCost, bool isSufficient, uint8 cause) = sufficientFundsHelper(deliveryRequests, msg.value); updateWormholeMessageFee();
if (!isSufficient) { uint256 totalFee = getTotalFeeMultichainSend(deliveryRequests);
if (cause == 26) { if (totalFee > msg.value) {
revert MaxTransactionFeeNotEnough(); revert IWormholeRelayer.MsgValueTooLow();
} else if (cause == 25) {
revert MsgValueTooLow();
} else {
revert FundsTooMuch();
}
} }
if (nonce == 0) { if (nonce == 0) {
revert NonceIsZero(); revert IWormholeRelayer.NonceIsZero();
} }
// encode the DeliveryInstructions IRelayProvider relayProvider = IRelayProvider(deliveryRequests.relayProviderAddress);
bytes memory container = convertToEncodedDeliveryInstructions(deliveryRequests, true); DeliveryInstructionsContainer memory container =
convertMultichainSendToDeliveryInstructionsContainer(deliveryRequests);
checkInstructions(container, IRelayProvider(deliveryRequests.relayProviderAddress));
container.sufficientlyFunded = true;
// emit delivery message // emit delivery message
IWormhole wormhole = wormhole(); sequence = wormhole().publishMessage{value: wormholeMessageFee()}(
IRelayProvider provider = IRelayProvider(deliveryRequests.relayProviderAddress); nonce, encodeDeliveryInstructionsContainer(container), relayProvider.getConsistencyLevel()
uint256 wormholeMessageFee = wormhole.messageFee(); );
sequence = wormhole.publishMessage{value: wormholeMessageFee}(nonce, container, provider.getConsistencyLevel());
//pay fee to provider //pay fee to provider
pay(provider.getRewardAddress(), totalCost - wormholeMessageFee); pay(relayProvider.getRewardAddress(), totalFee - wormholeMessageFee());
} }
/** /**
@ -191,218 +111,96 @@ contract CoreRelayer is CoreRelayerGovernance {
* it checks that the passed nonce is not zero (VAAs with a nonce of zero will not be batched) * it checks that the passed nonce is not zero (VAAs with a nonce of zero will not be batched)
* it generates a VAA with the encoded DeliveryInstructions * it generates a VAA with the encoded DeliveryInstructions
*/ */
function multichainForward(MultichainSend memory deliveryRequests, uint16 rolloverChain, uint32 nonce) function multichainForward(IWormholeRelayer.MultichainSend memory deliveryRequests, uint32 nonce) public payable {
public
payable
{
// Can only forward while a delivery is in process.
if (!isContractLocked()) { if (!isContractLocked()) {
revert NoDeliveryInProcess(); revert IWormholeRelayer.NoDeliveryInProgress();
} }
if (getForwardingRequest().isValid) { if (getForwardInstruction().isValid) {
revert CantRequestMultipleForwards(); revert IWormholeRelayer.MultipleForwardsRequested();
}
if (nonce == 0) {
revert IWormholeRelayer.NonceIsZero();
}
if (msg.sender != lockedTargetAddress()) {
revert IWormholeRelayer.ForwardRequestFromWrongAddress();
} }
//We want to catch malformed requests in this function, and only underfunded requests when emitting. uint256 totalFee = getTotalFeeMultichainSend(deliveryRequests);
verifyForwardingRequest(deliveryRequests, rolloverChain, nonce); DeliveryInstructionsContainer memory container =
convertMultichainSendToDeliveryInstructionsContainer(deliveryRequests);
checkInstructions(container, IRelayProvider(deliveryRequests.relayProviderAddress));
bytes memory encodedMultichainSend = encodeMultichainSend(deliveryRequests); setForwardInstruction(
setForwardingRequest( ForwardInstruction({
ForwardingRequest({ container: container,
deliveryRequestsContainer: encodedMultichainSend,
rolloverChain: rolloverChain,
nonce: nonce, nonce: nonce,
msgValue: msg.value, msgValue: msg.value,
totalFee: totalFee,
sender: msg.sender, sender: msg.sender,
relayProvider: deliveryRequests.relayProviderAddress,
isValid: true isValid: true
}) })
); );
} }
function emitForward(uint256 refundAmount, ForwardingRequest memory forwardingRequest) function emitForward(uint256 transactionFeeRefundAmount, ForwardInstruction memory forwardInstruction)
internal internal
returns (uint64, bool) returns (bool forwardIsFunded)
{ {
MultichainSend memory container = decodeMultichainSend(forwardingRequest.deliveryRequestsContainer); DeliveryInstructionsContainer memory container = forwardInstruction.container;
//Add any additional funds which were passed in to the refund amount //Add any additional funds which were passed in to the refund amount
refundAmount = refundAmount + forwardingRequest.msgValue; transactionFeeRefundAmount = transactionFeeRefundAmount + forwardInstruction.msgValue;
//make sure the refund amount covers the native gas amounts //make sure the refund amount covers the native gas amounts
(uint256 totalMinimumFees, bool funded,) = sufficientFundsHelper(container, refundAmount); forwardIsFunded = (transactionFeeRefundAmount >= forwardInstruction.totalFee);
container.sufficientlyFunded = forwardIsFunded;
//REVISE consider deducting the cost of this process from the refund amount? IRelayProvider relayProvider = IRelayProvider(forwardInstruction.relayProvider);
if (funded) { if (forwardIsFunded) {
//find the delivery instruction for the rollover chain // the rollover chain is the chain in the first request
uint16 rolloverInstructionIndex = findDeliveryIndex(container, forwardingRequest.rolloverChain); uint256 amountUnderMaximum = relayProvider.quoteMaximumBudget(container.instructions[0].targetChain)
- (
//calc how much budget is used by chains other than the rollover chain wormholeMessageFee() + container.instructions[0].maximumRefundTarget
uint256 rolloverChainCostEstimate = container.requests[rolloverInstructionIndex].maxTransactionFee + container.instructions[0].receiverValueTarget
+ container.requests[rolloverInstructionIndex].receiverValue; );
//uint256 nonrolloverBudget = totalMinimumFees - rolloverChainCostEstimate; //stack too deep uint256 convertedExtraAmount = calculateTargetDeliveryMaximumRefund(
uint256 rolloverBudget = refundAmount - (totalMinimumFees - rolloverChainCostEstimate) container.instructions[0].targetChain,
- container.requests[rolloverInstructionIndex].receiverValue; transactionFeeRefundAmount - forwardInstruction.totalFee,
relayProvider
//overwrite the gas budget on the rollover chain to the remaining budget amount );
container.requests[rolloverInstructionIndex].maxTransactionFee = rolloverBudget; container.instructions[0].maximumRefundTarget +=
(amountUnderMaximum > convertedExtraAmount) ? convertedExtraAmount : amountUnderMaximum;
} }
//emit forwarding instruction //emit forwarding instruction
bytes memory reencoded = convertToEncodedDeliveryInstructions(container, funded); wormhole().publishMessage{value: wormholeMessageFee()}(
IRelayProvider provider = IRelayProvider(container.relayProviderAddress); forwardInstruction.nonce,
IWormhole wormhole = wormhole(); encodeDeliveryInstructionsContainer(container),
uint64 sequence = wormhole.publishMessage{value: wormhole.messageFee()}( relayProvider.getConsistencyLevel()
forwardingRequest.nonce, reencoded, provider.getConsistencyLevel()
); );
// if funded, pay out reward to provider. Otherwise, the delivery code will handle sending a refund. // if funded, pay out reward to provider. Otherwise, the delivery code will handle sending a refund.
if (funded) { if (forwardIsFunded) {
pay(provider.getRewardAddress(), refundAmount); pay(relayProvider.getRewardAddress(), transactionFeeRefundAmount);
} }
//clear forwarding request from cache //clear forwarding request from cache
clearForwardingRequest(); clearForwardInstruction();
return (sequence, funded);
} }
function verifyForwardingRequest(MultichainSend memory container, uint16 rolloverChain, uint32 nonce) function multichainSendContainer(IWormholeRelayer.Send memory request, address relayProvider)
internal
view
{
if (nonce == 0) {
revert NonceIsZero();
}
if (msg.sender != lockedTargetAddress()) {
revert ForwardRequestFromWrongAddress();
}
bool foundRolloverChain = false;
IRelayProvider selectedProvider = IRelayProvider(container.relayProviderAddress);
for (uint16 i = 0; i < container.requests.length; i++) {
// TODO: Optimization opportunity here by reducing multiple calls to only one with all requested addresses.
if (selectedProvider.getDeliveryAddress(container.requests[i].targetChain) == 0) {
revert RelayProviderDoesNotSupportTargetChain();
}
if (container.requests[i].targetChain == rolloverChain) {
foundRolloverChain = true;
}
}
if (!foundRolloverChain) {
revert RolloverChainNotIncluded();
}
}
function findDeliveryIndex(MultichainSend memory container, uint16 chainId)
internal internal
pure pure
returns (uint16 deliveryRequestIndex) returns (IWormholeRelayer.MultichainSend memory container)
{ {
for (uint16 i = 0; i < container.requests.length; i++) { IWormholeRelayer.Send[] memory requests = new IWormholeRelayer.Send[](1);
if (container.requests[i].targetChain == chainId) { requests[0] = request;
deliveryRequestIndex = i; container = IWormholeRelayer.MultichainSend({relayProviderAddress: relayProvider, requests: requests});
return deliveryRequestIndex;
}
}
revert ChainNotFoundInSends(chainId);
}
/*
By the time this function completes, we must be certain that the specified funds are sufficient to cover
delivery for each one of the deliveryRequests with at least 1 gas on the target chains.
*/
function sufficientFundsHelper(MultichainSend memory deliveryRequests, uint256 funds)
internal
view
returns (uint256 totalFees, bool isSufficient, uint8 reason)
{
totalFees = wormhole().messageFee();
IRelayProvider provider = IRelayProvider(deliveryRequests.relayProviderAddress);
for (uint256 i = 0; i < deliveryRequests.requests.length; i++) {
Send memory request = deliveryRequests.requests[i];
(uint256 requestFee, uint256 maximumRefund, uint256 receiverValueTarget, bool isSufficient, uint8 reason) =
verifyFunding(
VerifyFundingCalculation({
provider: provider,
sourceChain: chainId(),
targetChain: request.targetChain,
maxTransactionFeeSource: request.maxTransactionFee,
receiverValueSource: request.receiverValue,
isDelivery: true
})
);
if (!isSufficient) {
return (0, false, reason);
}
totalFees = totalFees + requestFee;
if (funds < totalFees) {
return (0, false, 25); //"Insufficient funds were provided to cover the delivery fees.");
}
}
return (totalFees, true, 0);
}
struct VerifyFundingCalculation {
IRelayProvider provider;
uint16 sourceChain;
uint16 targetChain;
uint256 maxTransactionFeeSource;
uint256 receiverValueSource;
bool isDelivery;
}
function verifyFunding(VerifyFundingCalculation memory args)
internal
view
returns (
uint256 requestFee,
uint256 maximumRefund,
uint256 receiverValueTarget,
bool isSufficient,
uint8 reason
)
{
requestFee = args.maxTransactionFeeSource + args.receiverValueSource;
receiverValueTarget = convertApplicationBudgetAmount(args.receiverValueSource, args.targetChain, args.provider);
uint256 overheadFeeSource = args.isDelivery
? args.provider.quoteDeliveryOverhead(args.targetChain)
: args.provider.quoteRedeliveryOverhead(args.targetChain);
uint256 overheadBudgetTarget =
assetConversionHelper(args.sourceChain, overheadFeeSource, args.targetChain, 1, 1, true, args.provider);
maximumRefund = args.isDelivery
? calculateTargetDeliveryMaximumRefund(args.targetChain, args.maxTransactionFeeSource, args.provider)
: calculateTargetRedeliveryMaximumRefund(args.targetChain, args.maxTransactionFeeSource, args.provider);
//Make sure the maxTransactionFee covers the minimum delivery cost to the targetChain
if (args.maxTransactionFeeSource < overheadFeeSource) {
isSufficient = false;
reason = 26; //Insufficient msg.value to cover minimum delivery costs.";
}
//Make sure the budget does not exceed the maximum for the provider on that chain; //This added value is totalBudgetTarget
else if (
args.provider.quoteMaximumBudget(args.targetChain)
< (maximumRefund + overheadBudgetTarget + receiverValueTarget)
) {
isSufficient = false;
reason = 27; //"Specified budget exceeds the maximum allowed by the provider";
} else {
isSufficient = true;
reason = 0;
}
} }
function _executeDelivery( function _executeDelivery(
IWormhole wormhole,
DeliveryInstruction memory internalInstruction, DeliveryInstruction memory internalInstruction,
bytes[] memory encodedVMs, bytes[] memory encodedVMs,
bytes32 deliveryVaaHash, bytes32 deliveryVaaHash,
@ -410,11 +208,11 @@ contract CoreRelayer is CoreRelayerGovernance {
uint16 sourceChain, uint16 sourceChain,
uint64 sourceSequence uint64 sourceSequence
) internal { ) internal {
//REVISE Decide whether we want to remove the DeliveryInstructionContainer from encodedVMs. //REVISE Decide whether we want to remove the DeliveryInstructionsContainer from encodedVMs.
// lock the contract to prevent reentrancy // lock the contract to prevent reentrancy
if (isContractLocked()) { if (isContractLocked()) {
revert ReentrantCall(); revert IDelivery.ReentrantCall();
} }
setContractLock(true); setContractLock(true);
setLockedTargetAddress(fromWormholeFormat(internalInstruction.targetAddress)); setLockedTargetAddress(fromWormholeFormat(internalInstruction.targetAddress));
@ -422,7 +220,7 @@ contract CoreRelayer is CoreRelayerGovernance {
uint256 preGas = gasleft(); uint256 preGas = gasleft();
// call the receiveWormholeMessages endpoint on the target contract // call the receiveWormholeMessages endpoint on the target contract
(bool success,) = fromWormholeFormat(internalInstruction.targetAddress).call{ (bool callToTargetContractSucceeded,) = fromWormholeFormat(internalInstruction.targetAddress).call{
gas: internalInstruction.executionParameters.gasLimit, gas: internalInstruction.executionParameters.gasLimit,
value: internalInstruction.receiverValueTarget value: internalInstruction.receiverValueTarget
}(abi.encodeCall(IWormholeReceiver.receiveWormholeMessages, (encodedVMs, new bytes[](0)))); }(abi.encodeCall(IWormholeReceiver.receiveWormholeMessages, (encodedVMs, new bytes[](0))));
@ -438,126 +236,49 @@ contract CoreRelayer is CoreRelayerGovernance {
: (preGas - postGas); : (preGas - postGas);
// refund unused gas budget // refund unused gas budget
uint256 weiToRefund = internalInstruction.receiverValueTarget; uint256 transactionFeeRefundAmount = (internalInstruction.executionParameters.gasLimit - gasUsed)
if (success) { * internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
weiToRefund = (internalInstruction.executionParameters.gasLimit - gasUsed)
* internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
}
// unlock the contract // unlock the contract
setContractLock(false); setContractLock(false);
//REVISE decide if we want to always emit a VAA, or only emit a msg when forwarding ForwardInstruction memory forwardingRequest = getForwardInstruction();
// // emit delivery status message DeliveryStatus status;
// DeliveryStatus memory status = DeliveryStatus({ bool forwardIsFunded = false;
// payloadID: 2,
// batchHash: internalParams.batchVM.hash,
// emitterAddress: internalParams.deliveryId.emitterAddress,
// sequence: internalParams.deliveryId.sequence,
// deliveryCount: uint16(stackTooDeep.attemptedDeliveryCount + 1),
// deliverySuccess: success
// });
// // set the nonce to zero so a batch VAA is not created
// sequence =
// wormhole.publishMessage{value: wormhole.messageFee()}(0, encodeDeliveryStatus(status), consistencyLevel());
ForwardingRequest memory forwardingRequest = getForwardingRequest();
if (forwardingRequest.isValid) { if (forwardingRequest.isValid) {
(, success) = emitForward(weiToRefund, forwardingRequest); forwardIsFunded = emitForward(transactionFeeRefundAmount, forwardingRequest);
if (success) { status = forwardIsFunded ? DeliveryStatus.FORWARD_REQUEST_SUCCESS : DeliveryStatus.FORWARD_REQUEST_FAILURE;
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: sourceChain,
sequence: sourceSequence,
deliveryVaaHash: deliveryVaaHash,
status: DeliveryStatus.FORWARD_REQUEST_SUCCESS
});
} else {
bool sent = pay(payable(fromWormholeFormat(internalInstruction.refundAddress)), weiToRefund);
if (!sent) {
// if refunding fails, pay out full refund to relayer
weiToRefund = 0;
}
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: sourceChain,
sequence: sourceSequence,
deliveryVaaHash: deliveryVaaHash,
status: DeliveryStatus.FORWARD_REQUEST_FAILURE
});
}
} else { } else {
bool sent = pay(payable(fromWormholeFormat(internalInstruction.refundAddress)), weiToRefund); status = callToTargetContractSucceeded ? DeliveryStatus.SUCCESS : DeliveryStatus.RECEIVER_FAILURE;
if (!sent) {
// if refunding fails, pay out full refund to relayer
weiToRefund = 0;
}
if (success) {
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: sourceChain,
sequence: sourceSequence,
deliveryVaaHash: deliveryVaaHash,
status: DeliveryStatus.SUCCESS
});
} else {
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: sourceChain,
sequence: sourceSequence,
deliveryVaaHash: deliveryVaaHash,
status: DeliveryStatus.RECEIVER_FAILURE
});
}
} }
uint256 receiverValuePaid = (success ? internalInstruction.receiverValueTarget : 0); uint256 receiverValueRefundAmount =
uint256 wormholeFeePaid = forwardingRequest.isValid ? wormhole.messageFee() : 0; (callToTargetContractSucceeded ? 0 : internalInstruction.receiverValueTarget);
uint256 relayerRefundAmount = msg.value - weiToRefund - receiverValuePaid - wormholeFeePaid; uint256 refundToRefundAddress = receiverValueRefundAmount + (forwardIsFunded ? 0 : transactionFeeRefundAmount);
bool refundPaidToRefundAddress =
pay(payable(fromWormholeFormat(internalInstruction.refundAddress)), refundToRefundAddress);
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: sourceChain,
sequence: sourceSequence,
deliveryVaaHash: deliveryVaaHash,
status: status
});
uint256 extraRelayerFunds = (
msg.value - internalInstruction.receiverValueTarget - internalInstruction.maximumRefundTarget
- wormholeMessageFee()
);
uint256 relayerRefundAmount = extraRelayerFunds
+ (internalInstruction.maximumRefundTarget - transactionFeeRefundAmount)
+ (forwardingRequest.isValid ? 0 : wormholeMessageFee())
+ (refundPaidToRefundAddress ? 0 : refundToRefundAddress);
// refund the rest to relayer // refund the rest to relayer
pay(relayerRefund, relayerRefundAmount); pay(relayerRefund, relayerRefundAmount);
} }
//REVISE, consider implementing this system into the RelayProvider.
// function requestRewardPayout(uint16 rewardChain, bytes32 receiver, uint32 nonce)
// public
// payable
// returns (uint64 sequence)
// {
// uint256 amount = relayerRewards(msg.sender, rewardChain);
// require(amount > 0, "no current accrued rewards");
// resetRelayerRewards(msg.sender, rewardChain);
// sequence = wormhole().publishMessage{value: msg.value}(
// nonce,
// encodeRewardPayout(
// RewardPayout({
// payloadID: 100,
// fromChain: chainId(),
// chain: rewardChain,
// amount: amount,
// receiver: receiver
// })
// ),
// 20 //REVISE encode finality
// );
// }
// function collectRewards(bytes memory encodedVm) public {
// (IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
// require(valid, reason);
// require(verifyRelayerVM(vm), "invalid emitter");
// RewardPayout memory payout = parseRewardPayout(vm.payload);
// require(payout.chain == chainId());
// payable(address(uint160(uint256(payout.receiver)))).transfer(payout.amount);
// }
function verifyRelayerVM(IWormhole.VM memory vm) internal view returns (bool) { function verifyRelayerVM(IWormhole.VM memory vm) internal view returns (bool) {
return registeredCoreRelayerContract(vm.emitterChainId) == vm.emitterAddress; return registeredCoreRelayerContract(vm.emitterChainId) == vm.emitterAddress;
} }
@ -566,34 +287,34 @@ contract CoreRelayer is CoreRelayerGovernance {
return defaultRelayProvider(); return defaultRelayProvider();
} }
function redeliverSingle(TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable { function redeliverSingle(IDelivery.TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable {
//cache wormhole //cache wormhole
IWormhole wormhole = wormhole(); IWormhole wormhole = wormhole();
updateWormholeMessageFee();
//validate the redelivery VM //validate the redelivery VM
(IWormhole.VM memory redeliveryVM, bool valid, string memory reason) = (IWormhole.VM memory redeliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.redeliveryVM); wormhole.parseAndVerifyVM(targetParams.redeliveryVM);
if (!valid) { if (!valid) {
revert InvalidRedeliveryVM(reason); revert IDelivery.InvalidRedeliveryVM(reason);
} }
if (!verifyRelayerVM(redeliveryVM)) { if (!verifyRelayerVM(redeliveryVM)) {
// Redelivery VM has an invalid emitter // Redelivery VM has an invalid emitter
revert InvalidEmitterInRedeliveryVM(); revert IDelivery.InvalidEmitterInRedeliveryVM();
} }
RedeliveryByTxHashInstruction memory redeliveryInstruction = RedeliveryByTxHashInstruction memory redeliveryInstruction = decodeRedeliveryInstruction(redeliveryVM.payload);
decodeRedeliveryByTxHashInstruction(redeliveryVM.payload);
//validate the original delivery VM //validate the original delivery VM
IWormhole.VM memory originalDeliveryVM; IWormhole.VM memory originalDeliveryVM;
(originalDeliveryVM, valid, reason) = (originalDeliveryVM, valid, reason) =
wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]); wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]);
if (!valid) { if (!valid) {
revert InvalidVaa(redeliveryInstruction.deliveryIndex); revert IDelivery.InvalidVaa(redeliveryInstruction.deliveryIndex, reason);
} }
if (!verifyRelayerVM(originalDeliveryVM)) { if (!verifyRelayerVM(originalDeliveryVM)) {
// Original Delivery VM has a invalid emitter // Original Delivery VM has a invalid emitter
revert InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex); revert IDelivery.InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex);
} }
DeliveryInstruction memory instruction; DeliveryInstruction memory instruction;
@ -616,7 +337,6 @@ contract CoreRelayer is CoreRelayerGovernance {
} }
_executeDelivery( _executeDelivery(
wormhole,
instruction, instruction,
targetParams.sourceEncodedVMs, targetParams.sourceEncodedVMs,
originalDeliveryVM.hash, originalDeliveryVM.hash,
@ -635,16 +355,16 @@ contract CoreRelayer is CoreRelayerGovernance {
// The same relay provider must be specified when doing a single VAA redeliver. // The same relay provider must be specified when doing a single VAA redeliver.
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress); address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress);
if (providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)) { if (providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)) {
revert MismatchingRelayProvidersInRedelivery(); revert IDelivery.MismatchingRelayProvidersInRedelivery();
} }
// relayer must have covered the necessary funds // relayer must have covered the necessary funds
if ( if (
msg.value msg.value
< redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget < redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget
+ wormhole().messageFee() + wormholeMessageFee()
) { ) {
revert InsufficientRelayerFunds(); revert IDelivery.InsufficientRelayerFunds();
} }
uint16 whChainId = chainId(); uint16 whChainId = chainId();
@ -670,24 +390,25 @@ contract CoreRelayer is CoreRelayerGovernance {
deliveryInstruction.executionParameters = redeliveryInstruction.executionParameters; deliveryInstruction.executionParameters = redeliveryInstruction.executionParameters;
} }
function deliverSingle(TargetDeliveryParametersSingle memory targetParams) public payable { function deliverSingle(IDelivery.TargetDeliveryParametersSingle memory targetParams) public payable {
// cache wormhole instance // cache wormhole instance
IWormhole wormhole = wormhole(); IWormhole wormhole = wormhole();
updateWormholeMessageFee();
// validate the deliveryIndex // validate the deliveryIndex
(IWormhole.VM memory deliveryVM, bool valid, string memory reason) = (IWormhole.VM memory deliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]); wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]);
if (!valid) { if (!valid) {
revert InvalidVaa(targetParams.deliveryIndex); revert IDelivery.InvalidVaa(targetParams.deliveryIndex, reason);
} }
if (!verifyRelayerVM(deliveryVM)) { if (!verifyRelayerVM(deliveryVM)) {
revert InvalidEmitter(); revert IDelivery.InvalidEmitter();
} }
DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload); DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload);
//ensure this is a funded delivery, not a failed forward. //ensure this is a funded delivery, not a failed forward.
if (!container.sufficientlyFunded) { if (!container.sufficientlyFunded) {
revert SendNotSufficientlyFunded(); revert IDelivery.SendNotSufficientlyFunded();
} }
// parse the deliveryVM payload into the DeliveryInstructions struct // parse the deliveryVM payload into the DeliveryInstructions struct
@ -695,24 +416,23 @@ contract CoreRelayer is CoreRelayerGovernance {
//make sure the specified relayer is the relayer delivering this message //make sure the specified relayer is the relayer delivering this message
if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) { if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) {
revert UnexpectedRelayer(); revert IDelivery.UnexpectedRelayer();
} }
//make sure relayer passed in sufficient funds //make sure relayer passed in sufficient funds
if ( if (
msg.value msg.value
< deliveryInstruction.maximumRefundTarget + deliveryInstruction.receiverValueTarget + wormhole.messageFee() < deliveryInstruction.maximumRefundTarget + deliveryInstruction.receiverValueTarget + wormholeMessageFee()
) { ) {
revert InsufficientRelayerFunds(); revert IDelivery.InsufficientRelayerFunds();
} }
//make sure this delivery is intended for this chain //make sure this delivery is intended for this chain
if (chainId() != deliveryInstruction.targetChain) { if (chainId() != deliveryInstruction.targetChain) {
revert TargetChainIsNotThisChain(deliveryInstruction.targetChain); revert IDelivery.TargetChainIsNotThisChain(deliveryInstruction.targetChain);
} }
_executeDelivery( _executeDelivery(
wormhole,
deliveryInstruction, deliveryInstruction,
targetParams.encodedVMs, targetParams.encodedVMs,
deliveryVM.hash, deliveryVM.hash,
@ -734,107 +454,6 @@ contract CoreRelayer is CoreRelayerGovernance {
return new bytes(0); return new bytes(0);
} }
function makeRelayerParams(IRelayProvider provider) public pure returns (bytes memory relayerParams) {
//current version is just 1,
relayerParams = abi.encode(1, toWormholeFormat(address(provider)));
}
function getDeliveryInstructionsContainer(bytes memory encoded)
public
view
returns (DeliveryInstructionsContainer memory container)
{
container = decodeDeliveryInstructionsContainer(encoded);
}
function getRedeliveryByTxHashInstruction(bytes memory encoded)
public
view
returns (RedeliveryByTxHashInstruction memory instruction)
{
instruction = decodeRedeliveryByTxHashInstruction(encoded);
}
/**
* Given a targetChain, maxTransactionFee, and a relay provider, this function calculates what the gas limit of the delivery transaction
* should be.
*/
function calculateTargetGasDeliveryAmount(uint16 targetChain, uint256 maxTransactionFee, IRelayProvider provider)
internal
view
returns (uint32 gasAmount)
{
gasAmount = calculateTargetGasDeliveryAmountHelper(
targetChain, maxTransactionFee, provider.quoteDeliveryOverhead(targetChain), provider
);
}
function calculateTargetDeliveryMaximumRefund(
uint16 targetChain,
uint256 maxTransactionFee,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
maximumRefund = calculateTargetDeliveryMaximumRefundHelper(
targetChain, maxTransactionFee, provider.quoteDeliveryOverhead(targetChain), provider
);
}
/**
* Given a targetChain, maxTransactionFee, and a relay provider, this function calculates what the gas limit of the redelivery transaction
* should be.
*/
function calculateTargetGasRedeliveryAmount(uint16 targetChain, uint256 maxTransactionFee, IRelayProvider provider)
internal
view
returns (uint32 gasAmount)
{
gasAmount = calculateTargetGasDeliveryAmountHelper(
targetChain, maxTransactionFee, provider.quoteRedeliveryOverhead(targetChain), provider
);
}
function calculateTargetRedeliveryMaximumRefund(
uint16 targetChain,
uint256 maxTransactionFee,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
maximumRefund = calculateTargetDeliveryMaximumRefundHelper(
targetChain, maxTransactionFee, provider.quoteRedeliveryOverhead(targetChain), provider
);
}
function calculateTargetGasDeliveryAmountHelper(
uint16 targetChain,
uint256 maxTransactionFee,
uint256 deliveryOverhead,
IRelayProvider provider
) internal view returns (uint32 gasAmount) {
if (maxTransactionFee <= deliveryOverhead) {
gasAmount = 0;
} else {
uint256 gas = (maxTransactionFee - deliveryOverhead) / provider.quoteGasPrice(targetChain);
if (gas > type(uint32).max) {
gasAmount = type(uint32).max;
} else {
gasAmount = uint32(gas);
}
}
}
function calculateTargetDeliveryMaximumRefundHelper(
uint16 targetChain,
uint256 maxTransactionFee,
uint256 deliveryOverhead,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
if (maxTransactionFee >= deliveryOverhead) {
uint256 remainder = maxTransactionFee - deliveryOverhead;
maximumRefund = assetConversionHelper(chainId(), remainder, targetChain, 1, 1, false, provider);
} else {
maximumRefund = 0;
}
}
function quoteGas(uint16 targetChain, uint32 gasLimit, IRelayProvider provider) function quoteGas(uint16 targetChain, uint32 gasLimit, IRelayProvider provider)
public public
view view
@ -852,33 +471,6 @@ contract CoreRelayer is CoreRelayerGovernance {
provider.quoteRedeliveryOverhead(targetChain) + (gasLimit * provider.quoteGasPrice(targetChain)); provider.quoteRedeliveryOverhead(targetChain) + (gasLimit * provider.quoteGasPrice(targetChain));
} }
function assetConversionHelper(
uint16 sourceChain,
uint256 sourceAmount,
uint16 targetChain,
uint256 multiplier,
uint256 multiplierDenominator,
bool roundUp,
IRelayProvider provider
) internal view returns (uint256 targetAmount) {
uint256 srcNativeCurrencyPrice = provider.quoteAssetPrice(sourceChain);
if (srcNativeCurrencyPrice == 0) {
revert RelayProviderDoesNotSupportTargetChain();
}
uint256 dstNativeCurrencyPrice = provider.quoteAssetPrice(targetChain);
if (dstNativeCurrencyPrice == 0) {
revert RelayProviderDoesNotSupportTargetChain();
}
uint256 numerator = sourceAmount * srcNativeCurrencyPrice * multiplier;
uint256 denominator = dstNativeCurrencyPrice * multiplierDenominator;
if (roundUp) {
targetAmount = (numerator + denominator - 1) / denominator;
} else {
targetAmount = numerator / denominator;
}
}
//If the integrator pays at least nativeQuote, they should receive at least targetAmount as their application budget //If the integrator pays at least nativeQuote, they should receive at least targetAmount as their application budget
function quoteReceiverValue(uint16 targetChain, uint256 targetAmount, IRelayProvider provider) function quoteReceiverValue(uint16 targetChain, uint256 targetAmount, IRelayProvider provider)
public public
@ -891,81 +483,6 @@ contract CoreRelayer is CoreRelayerGovernance {
); );
} }
//This should invert quoteApplicationBudgetAmount, I.E when a user pays the sourceAmount, they receive at least the value of targetAmount they requested from
//quoteReceiverValue.
function convertApplicationBudgetAmount(uint256 sourceAmount, uint16 targetChain, IRelayProvider provider)
internal
view
returns (uint256 targetAmount)
{
(uint16 buffer, uint16 denominator) = provider.getAssetConversionBuffer(targetChain);
targetAmount = assetConversionHelper(
chainId(), sourceAmount, targetChain, denominator, uint256(0) + denominator + buffer, false, provider
);
}
function convertToEncodedRedeliveryByTxHashInstruction(
ResendByTx memory request,
uint256 receiverValueTarget,
uint256 maximumRefund,
uint32 gasLimit,
IRelayProvider provider
) internal view returns (bytes memory encoded) {
encoded = abi.encodePacked(
uint8(2), //version payload number
uint16(request.sourceChain),
bytes32(request.sourceTxHash),
uint32(request.sourceNonce),
uint16(request.targetChain),
uint8(request.deliveryIndex),
uint8(request.multisendIndex),
maximumRefund,
receiverValueTarget,
uint8(1), //version for ExecutionParameters
gasLimit,
provider.getDeliveryAddress(request.targetChain)
);
}
function convertToEncodedDeliveryInstructions(MultichainSend memory container, bool isFunded)
internal
view
returns (bytes memory encoded)
{
encoded = abi.encodePacked(
uint8(1), //version payload number
uint8(isFunded ? 1 : 0), // sufficiently funded
uint8(container.requests.length) //number of requests in the array
);
// TODO: this probably results in a quadratic algorithm. Further optimization can be done here.
// Append all the messages to the array.
for (uint256 i = 0; i < container.requests.length; i++) {
encoded = appendDeliveryInstruction(
encoded, container.requests[i], IRelayProvider(container.relayProviderAddress)
);
}
}
function appendDeliveryInstruction(bytes memory encoded, Send memory request, IRelayProvider provider)
internal
view
returns (bytes memory newEncoded)
{
newEncoded = abi.encodePacked(
encoded,
request.targetChain,
request.targetAddress,
request.refundAddress,
calculateTargetDeliveryMaximumRefund(request.targetChain, request.maxTransactionFee, provider),
convertApplicationBudgetAmount(request.receiverValue, request.targetChain, provider),
uint8(1), //version for ExecutionParameters
calculateTargetGasDeliveryAmount(request.targetChain, request.maxTransactionFee, provider),
provider.getDeliveryAddress(request.targetChain)
);
}
function pay(address payable receiver, uint256 amount) internal returns (bool success) { function pay(address payable receiver, uint256 amount) internal returns (bool success) {
if (amount > 0) { if (amount > 0) {
(success,) = receiver.call{value: amount}(""); (success,) = receiver.call{value: amount}("");

View File

@ -33,6 +33,10 @@ contract CoreRelayerGetters is CoreRelayerState {
return IWormhole(_state.provider.wormhole); return IWormhole(_state.provider.wormhole);
} }
function wormholeMessageFee() public view returns (uint256) {
return _state.provider.wormholeMessageFee;
}
function chainId() public view returns (uint16) { function chainId() public view returns (uint16) {
return _state.provider.chainId; return _state.provider.chainId;
} }
@ -53,8 +57,8 @@ contract CoreRelayerGetters is CoreRelayerState {
return IRelayProvider(_state.defaultRelayProvider); return IRelayProvider(_state.defaultRelayProvider);
} }
function getForwardingRequest() internal view returns (CoreRelayerStructs.ForwardingRequest memory) { function getForwardInstruction() internal view returns (CoreRelayerStructs.ForwardInstruction memory) {
return _state.forwardingRequest; return _state.forwardInstruction;
} }
function isContractLocked() internal view returns (bool) { function isContractLocked() internal view returns (bool) {

View File

@ -7,22 +7,302 @@ import "../libraries/external/BytesLib.sol";
import "./CoreRelayerGetters.sol"; import "./CoreRelayerGetters.sol";
import "./CoreRelayerStructs.sol"; import "./CoreRelayerStructs.sol";
import "../interfaces/IWormholeRelayer.sol";
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters { contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
using BytesLib for bytes; using BytesLib for bytes;
error InvalidPayloadId(uint8 payloadId); error InvalidPayloadId(uint8 payloadId);
error InvalidDeliveryInstructionsPayload(uint256 length); error InvalidDeliveryInstructionsPayload(uint256 length);
error InvalidSendsPayload(uint256 length);
function decodeRedeliveryByTxHashInstruction(bytes memory encoded) function getTotalFeeMultichainSend(IWormholeRelayer.MultichainSend memory sendContainer)
internal internal
view
returns (uint256 totalFee)
{
totalFee = wormholeMessageFee();
uint256 length = sendContainer.requests.length;
for (uint256 i = 0; i < length; i++) {
IWormholeRelayer.Send memory request = sendContainer.requests[i];
totalFee += request.maxTransactionFee + request.receiverValue;
}
}
function convertMultichainSendToDeliveryInstructionsContainer(IWormholeRelayer.MultichainSend memory sendContainer)
internal
view
returns (DeliveryInstructionsContainer memory instructionsContainer)
{
instructionsContainer.payloadId = 1;
IRelayProvider relayProvider = IRelayProvider(sendContainer.relayProviderAddress);
uint256 length = sendContainer.requests.length;
instructionsContainer.instructions = new DeliveryInstruction[](length);
for (uint256 i = 0; i < length; i++) {
instructionsContainer.instructions[i] =
convertSendToDeliveryInstruction(sendContainer.requests[i], relayProvider);
}
}
function convertSendToDeliveryInstruction(IWormholeRelayer.Send memory send, IRelayProvider relayProvider)
internal
view
returns (DeliveryInstruction memory instruction)
{
instruction.targetChain = send.targetChain;
instruction.targetAddress = send.targetAddress;
instruction.refundAddress = send.refundAddress;
instruction.maximumRefundTarget =
calculateTargetDeliveryMaximumRefund(send.targetChain, send.maxTransactionFee, relayProvider);
instruction.receiverValueTarget =
convertReceiverValueAmount(send.receiverValue, send.targetChain, relayProvider);
instruction.executionParameters = ExecutionParameters({
version: 1,
gasLimit: calculateTargetGasDeliveryAmount(send.targetChain, send.maxTransactionFee, relayProvider),
providerDeliveryAddress: relayProvider.getDeliveryAddress(send.targetChain)
});
}
function checkInstructions(DeliveryInstructionsContainer memory container, IRelayProvider relayProvider)
internal
view
{
uint256 length = container.instructions.length;
for (uint8 i = 0; i < length; i++) {
DeliveryInstruction memory instruction = container.instructions[i];
if (instruction.executionParameters.gasLimit == 0) {
revert IWormholeRelayer.MaxTransactionFeeNotEnough(i);
}
if (
instruction.maximumRefundTarget + instruction.receiverValueTarget + wormholeMessageFee()
> relayProvider.quoteMaximumBudget(instruction.targetChain)
) {
revert IWormholeRelayer.FundsTooMuch(i);
}
}
}
function checkRedeliveryInstruction(RedeliveryByTxHashInstruction memory instruction, IRelayProvider relayProvider)
internal
view
{
if (instruction.executionParameters.gasLimit == 0) {
revert IWormholeRelayer.MaxTransactionFeeNotEnough(0);
}
if (
instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget + wormholeMessageFee()
> relayProvider.quoteMaximumBudget(instruction.targetChain)
) {
revert IWormholeRelayer.FundsTooMuch(0);
}
}
function convertResendToRedeliveryInstruction(IWormholeRelayer.ResendByTx memory send, IRelayProvider relayProvider)
internal
view
returns (RedeliveryByTxHashInstruction memory instruction)
{
instruction.payloadId = 2;
instruction.sourceChain = send.sourceChain;
instruction.sourceTxHash = send.sourceTxHash;
instruction.sourceNonce = send.sourceNonce;
instruction.targetChain = send.targetChain;
instruction.deliveryIndex = send.deliveryIndex;
instruction.multisendIndex = send.multisendIndex;
instruction.newMaximumRefundTarget =
calculateTargetRedeliveryMaximumRefund(send.targetChain, send.newMaxTransactionFee, relayProvider);
instruction.newReceiverValueTarget =
convertReceiverValueAmount(send.newReceiverValue, send.targetChain, relayProvider);
instruction.executionParameters = ExecutionParameters({
version: 1,
gasLimit: calculateTargetGasRedeliveryAmount(send.targetChain, send.newMaxTransactionFee, relayProvider),
providerDeliveryAddress: relayProvider.getDeliveryAddress(send.targetChain)
});
}
function encodeRedeliveryInstruction(RedeliveryByTxHashInstruction memory instruction)
internal
pure
returns (bytes memory encoded)
{
encoded = abi.encodePacked(
instruction.payloadId,
instruction.sourceChain,
instruction.sourceTxHash,
instruction.sourceNonce,
instruction.targetChain,
instruction.deliveryIndex,
instruction.multisendIndex,
instruction.newMaximumRefundTarget,
instruction.newReceiverValueTarget,
instruction.executionParameters.version,
instruction.executionParameters.gasLimit,
instruction.executionParameters.providerDeliveryAddress
);
}
function encodeDeliveryInstructionsContainer(DeliveryInstructionsContainer memory container)
internal
pure
returns (bytes memory encoded)
{
encoded = abi.encodePacked(
container.payloadId, uint8(container.sufficientlyFunded ? 1 : 0), uint8(container.instructions.length)
);
for (uint256 i = 0; i < container.instructions.length; i++) {
encoded = abi.encodePacked(encoded, encodeDeliveryInstruction(container.instructions[i]));
}
}
function encodeDeliveryInstruction(DeliveryInstruction memory instruction)
internal
pure
returns (bytes memory encoded)
{
encoded = abi.encodePacked(
instruction.targetChain,
instruction.targetAddress,
instruction.refundAddress,
instruction.maximumRefundTarget,
instruction.receiverValueTarget,
instruction.executionParameters.version,
instruction.executionParameters.gasLimit,
instruction.executionParameters.providerDeliveryAddress
);
}
/**
* Given a targetChain, maxTransactionFee, and a relay provider, this function calculates what the gas limit of the delivery transaction
* should be.
*/
function calculateTargetGasDeliveryAmount(uint16 targetChain, uint256 maxTransactionFee, IRelayProvider provider)
internal
view
returns (uint32 gasAmount)
{
gasAmount = calculateTargetGasDeliveryAmountHelper(
targetChain, maxTransactionFee, provider.quoteDeliveryOverhead(targetChain), provider
);
}
function calculateTargetDeliveryMaximumRefund(
uint16 targetChain,
uint256 maxTransactionFee,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
maximumRefund = calculateTargetDeliveryMaximumRefundHelper(
targetChain, maxTransactionFee, provider.quoteDeliveryOverhead(targetChain), provider
);
}
/**
* Given a targetChain, maxTransactionFee, and a relay provider, this function calculates what the gas limit of the redelivery transaction
* should be.
*/
function calculateTargetGasRedeliveryAmount(uint16 targetChain, uint256 maxTransactionFee, IRelayProvider provider)
internal
view
returns (uint32 gasAmount)
{
gasAmount = calculateTargetGasDeliveryAmountHelper(
targetChain, maxTransactionFee, provider.quoteRedeliveryOverhead(targetChain), provider
);
}
function calculateTargetRedeliveryMaximumRefund(
uint16 targetChain,
uint256 maxTransactionFee,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
maximumRefund = calculateTargetDeliveryMaximumRefundHelper(
targetChain, maxTransactionFee, provider.quoteRedeliveryOverhead(targetChain), provider
);
}
function calculateTargetGasDeliveryAmountHelper(
uint16 targetChain,
uint256 maxTransactionFee,
uint256 deliveryOverhead,
IRelayProvider provider
) internal view returns (uint32 gasAmount) {
if (maxTransactionFee <= deliveryOverhead) {
gasAmount = 0;
} else {
uint256 gas = (maxTransactionFee - deliveryOverhead) / provider.quoteGasPrice(targetChain);
if (gas > type(uint32).max) {
gasAmount = type(uint32).max;
} else {
gasAmount = uint32(gas);
}
}
}
function calculateTargetDeliveryMaximumRefundHelper(
uint16 targetChain,
uint256 maxTransactionFee,
uint256 deliveryOverhead,
IRelayProvider provider
) internal view returns (uint256 maximumRefund) {
if (maxTransactionFee >= deliveryOverhead) {
uint256 remainder = maxTransactionFee - deliveryOverhead;
maximumRefund = assetConversionHelper(chainId(), remainder, targetChain, 1, 1, false, provider);
} else {
maximumRefund = 0;
}
}
function assetConversionHelper(
uint16 sourceChain,
uint256 sourceAmount,
uint16 targetChain,
uint256 multiplier,
uint256 multiplierDenominator,
bool roundUp,
IRelayProvider provider
) internal view returns (uint256 targetAmount) {
uint256 srcNativeCurrencyPrice = provider.quoteAssetPrice(sourceChain);
if (srcNativeCurrencyPrice == 0) {
revert IWormholeRelayer.RelayProviderDoesNotSupportTargetChain();
}
uint256 dstNativeCurrencyPrice = provider.quoteAssetPrice(targetChain);
if (dstNativeCurrencyPrice == 0) {
revert IWormholeRelayer.RelayProviderDoesNotSupportTargetChain();
}
uint256 numerator = sourceAmount * srcNativeCurrencyPrice * multiplier;
uint256 denominator = dstNativeCurrencyPrice * multiplierDenominator;
if (roundUp) {
targetAmount = (numerator + denominator - 1) / denominator;
} else {
targetAmount = numerator / denominator;
}
}
//This should invert quoteApplicationBudgetAmount, I.E when a user pays the sourceAmount, they receive at least the value of targetAmount they requested from
//quoteReceiverValue.
function convertReceiverValueAmount(uint256 sourceAmount, uint16 targetChain, IRelayProvider provider)
internal
view
returns (uint256 targetAmount)
{
(uint16 buffer, uint16 denominator) = provider.getAssetConversionBuffer(targetChain);
targetAmount = assetConversionHelper(
chainId(), sourceAmount, targetChain, denominator, uint256(0) + denominator + buffer, false, provider
);
}
function decodeRedeliveryInstruction(bytes memory encoded)
public
pure pure
returns (RedeliveryByTxHashInstruction memory instruction) returns (RedeliveryByTxHashInstruction memory instruction)
{ {
uint256 index = 0; uint256 index = 0;
instruction.payloadId = encoded.toUint8(index); instruction.payloadId = encoded.toUint8(index);
if (instruction.payloadId != 2) {
revert InvalidPayloadId(instruction.payloadId);
}
index += 1; index += 1;
instruction.sourceChain = encoded.toUint16(index); instruction.sourceChain = encoded.toUint16(index);
@ -60,7 +340,7 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
} }
function decodeDeliveryInstructionsContainer(bytes memory encoded) function decodeDeliveryInstructionsContainer(bytes memory encoded)
internal public
pure pure
returns (DeliveryInstructionsContainer memory) returns (DeliveryInstructionsContainer memory)
{ {
@ -121,82 +401,4 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
instructions: instructionArray instructions: instructionArray
}); });
} }
function encodeMultichainSend(MultichainSend memory container) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
uint8(1), //version payload number
address(container.relayProviderAddress),
uint8(container.requests.length) //number of requests in the array
);
//Append all the messages to the array.
for (uint256 i = 0; i < container.requests.length; i++) {
Send memory request = container.requests[i];
encoded = abi.encodePacked(
encoded,
request.targetChain,
request.targetAddress,
request.refundAddress,
request.maxTransactionFee,
request.receiverValue,
uint8(request.relayParameters.length),
request.relayParameters
);
}
}
function decodeMultichainSend(bytes memory encoded) internal pure returns (MultichainSend memory) {
uint256 index = 0;
uint8 payloadId = encoded.toUint8(index);
if (payloadId != 1) {
revert InvalidPayloadId(payloadId);
}
index += 1;
address relayProviderAddress = encoded.toAddress(index);
index += 20;
uint8 arrayLen = encoded.toUint8(index);
index += 1;
Send[] memory requestArray = new Send[](arrayLen);
for (uint8 i = 0; i < arrayLen; i++) {
Send memory request;
// target chain of the delivery request
request.targetChain = encoded.toUint16(index);
index += 2;
// target contract address
request.targetAddress = encoded.toBytes32(index);
index += 32;
// address to send the refund to
request.refundAddress = encoded.toBytes32(index);
index += 32;
request.maxTransactionFee = encoded.toUint256(index);
index += 32;
request.receiverValue = encoded.toUint256(index);
index += 32;
uint8 relayParametersLength = encoded.toUint8(index);
index += 1;
request.relayParameters = encoded.slice(index, relayParametersLength);
index += relayParametersLength;
requestArray[i] = request;
}
if (index != encoded.length) {
revert InvalidSendsPayload(encoded.length);
}
return MultichainSend({relayProviderAddress: relayProviderAddress, requests: requestArray});
}
} }

View File

@ -6,6 +6,7 @@ pragma solidity ^0.8.0;
import "./CoreRelayerState.sol"; import "./CoreRelayerState.sol";
import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/utils/Context.sol";
import "./CoreRelayerStructs.sol"; import "./CoreRelayerStructs.sol";
import {IWormhole} from "../interfaces/IWormhole.sol";
contract CoreRelayerSetters is CoreRelayerState, Context { contract CoreRelayerSetters is CoreRelayerState, Context {
error InvalidEvmChainId(); error InvalidEvmChainId();
@ -34,6 +35,10 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.provider.wormhole = payable(wh); _state.provider.wormhole = payable(wh);
} }
function updateWormholeMessageFee() internal {
_state.provider.wormholeMessageFee = IWormhole(_state.provider.wormhole).messageFee();
}
function setRelayProvider(address defaultRelayProvider) internal { function setRelayProvider(address defaultRelayProvider) internal {
_state.defaultRelayProvider = defaultRelayProvider; _state.defaultRelayProvider = defaultRelayProvider;
} }
@ -42,12 +47,12 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.registeredCoreRelayerContract[chainId] = relayerAddress; _state.registeredCoreRelayerContract[chainId] = relayerAddress;
} }
function setForwardingRequest(CoreRelayerStructs.ForwardingRequest memory request) internal { function setForwardInstruction(CoreRelayerStructs.ForwardInstruction memory request) internal {
_state.forwardingRequest = request; _state.forwardInstruction = request;
} }
function clearForwardingRequest() internal { function clearForwardInstruction() internal {
delete _state.forwardingRequest; //TODO is this the best way to accomplish this? delete _state.forwardInstruction;
} }
function setContractLock(bool status) internal { function setContractLock(bool status) internal {

View File

@ -43,8 +43,6 @@ contract CoreRelayerSetup is CoreRelayerSetters, ERC1967Upgrade {
setGovernanceContract(governanceContract); setGovernanceContract(governanceContract);
setEvmChainId(evmChainId); setEvmChainId(evmChainId);
//setRegisteredCoreRelayerContract(chainId, bytes32(uint256(uint160(address(this)))));
_upgradeTo(implementation); _upgradeTo(implementation);
// call initialize function of the new implementation // call initialize function of the new implementation

View File

@ -9,6 +9,7 @@ contract CoreRelayerStorage {
struct Provider { struct Provider {
uint16 chainId; uint16 chainId;
address payable wormhole; address payable wormhole;
uint256 wormholeMessageFee;
uint16 governanceChainId; uint16 governanceChainId;
bytes32 governanceContract; bytes32 governanceContract;
} }
@ -26,7 +27,7 @@ contract CoreRelayerStorage {
// address of the default relay provider on this chain // address of the default relay provider on this chain
address defaultRelayProvider; address defaultRelayProvider;
// Request which will be forwarded from the current delivery. // Request which will be forwarded from the current delivery.
CoreRelayerStructs.ForwardingRequest forwardingRequest; CoreRelayerStructs.ForwardInstruction forwardInstruction;
// mapping of initialized implementations // mapping of initialized implementations
mapping(address => bool) initializedImplementations; mapping(address => bool) initializedImplementations;
// mapping of relayer contracts on other chains // mapping of relayer contracts on other chains

View File

@ -3,74 +3,9 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../interfaces/IWormhole.sol"; import "../interfaces/IWormholeRelayer.sol";
abstract contract CoreRelayerStructs { abstract contract CoreRelayerStructs {
//This first group of structs are external facing API objects,
//which should be considered untrusted and unmodifiable
struct MultichainSend {
address relayProviderAddress;
Send[] requests;
}
// struct TargetDeliveryParameters {
// // encoded batchVM to be delivered on the target chain
// bytes encodedVM;
// // Index of the delivery VM in a batch
// uint8 deliveryIndex;
// uint8 multisendIndex;
// //uint32 targetCallGasOverride;
// }
struct TargetDeliveryParametersSingle {
// encoded batchVM to be delivered on the target chain
bytes[] encodedVMs;
// Index of the delivery VM in a batch
uint8 deliveryIndex;
// Index of the target chain inside the delivery VM
uint8 multisendIndex;
// relayer refund address
address payable relayerRefundAddress;
}
// Optional gasOverride which can be supplied by the relayer
// uint32 targetCallGasOverride;
struct TargetRedeliveryByTxHashParamsSingle {
bytes redeliveryVM;
bytes[] sourceEncodedVMs;
address payable relayerRefundAddress;
}
struct Send {
uint16 targetChain;
bytes32 targetAddress;
bytes32 refundAddress;
uint256 maxTransactionFee;
uint256 receiverValue;
bytes relayParameters;
}
struct ResendByTx {
uint16 sourceChain;
bytes32 sourceTxHash;
uint32 sourceNonce;
uint16 targetChain;
uint8 deliveryIndex;
uint8 multisendIndex;
uint256 newMaxTransactionFee;
uint256 newReceiverValue;
bytes newRelayParameters;
}
struct RelayParameters {
uint8 version; //1
bytes32 providerAddressOverride;
}
//Below this are internal structs
//Wire Types
struct DeliveryInstructionsContainer { struct DeliveryInstructionsContainer {
uint8 payloadId; //1 uint8 payloadId; //1
bool sufficientlyFunded; bool sufficientlyFunded;
@ -83,7 +18,7 @@ abstract contract CoreRelayerStructs {
bytes32 refundAddress; bytes32 refundAddress;
uint256 maximumRefundTarget; uint256 maximumRefundTarget;
uint256 receiverValueTarget; uint256 receiverValueTarget;
ExecutionParameters executionParameters; //Has the gas limit to execute with ExecutionParameters executionParameters;
} }
struct ExecutionParameters { struct ExecutionParameters {
@ -105,23 +40,13 @@ abstract contract CoreRelayerStructs {
ExecutionParameters executionParameters; ExecutionParameters executionParameters;
} }
//End Wire Types struct ForwardInstruction {
DeliveryInstructionsContainer container;
//Internal usage structs
struct AllowedEmitterSequence {
// wormhole emitter address
bytes32 emitterAddress;
// wormhole message sequence
uint64 sequence;
}
struct ForwardingRequest {
bytes deliveryRequestsContainer;
uint16 rolloverChain;
uint32 nonce; uint32 nonce;
address sender; address sender;
uint256 msgValue; uint256 msgValue;
uint256 totalFee;
address relayProvider;
bool isValid; bool isValid;
} }
} }

View File

@ -30,9 +30,10 @@ interface IDelivery {
error InvalidEmitterInRedeliveryVM(); error InvalidEmitterInRedeliveryVM();
error MismatchingRelayProvidersInRedelivery(); // The same relay provider must be specified when doing a single VAA redeliver error MismatchingRelayProvidersInRedelivery(); // The same relay provider must be specified when doing a single VAA redeliver
error UnexpectedRelayer(); // msg.sender must be the provider error UnexpectedRelayer(); // msg.sender must be the provider
error InvalidVaa(uint8 index); error InvalidVaa(uint8 index, string reason);
error InvalidEmitter(); error InvalidEmitter();
error SendNotSufficientlyFunded(); // This delivery request was not sufficiently funded, and must request redelivery error SendNotSufficientlyFunded(); // This delivery request was not sufficiently funded, and must request redelivery
error InsufficientRelayerFunds(); // The relayer didn't pass sufficient funds (msg.value does not cover the necessary budget fees) error InsufficientRelayerFunds(); // The relayer didn't pass sufficient funds (msg.value does not cover the necessary budget fees)
error TargetChainIsNotThisChain(uint16 targetChainId); error TargetChainIsNotThisChain(uint16 targetChainId);
error ReentrantCall(); // A delivery cannot occur during another delivery
} }

View File

@ -263,13 +263,13 @@ interface IWormholeRelayer {
* and let NEEDED_VALUE = (one wormhole message fee) + Sum_(i=0 -> requests.requests.length - 1) [requests.requests[i].maxTransactionFee + requests.requests[i].receiverValue]. * and let NEEDED_VALUE = (one wormhole message fee) + Sum_(i=0 -> requests.requests.length - 1) [requests.requests[i].maxTransactionFee + requests.requests[i].receiverValue].
* The multichainForward will succeed if LEFTOVER_VALUE >= NEEDED_VALUE * The multichainForward will succeed if LEFTOVER_VALUE >= NEEDED_VALUE
* *
* note: If LEFTOVER_VALUE > NEEDED_VALUE, then the maxTransactionFee of the first request in the array of sends will be incremented by 'LEFTOVER_VALUE - NEEDED_VALUE'
*
* @param requests The MultichainSend struct, containing the array of Send requests, as well as the desired relayProviderAddress * @param requests The MultichainSend struct, containing the array of Send requests, as well as the desired relayProviderAddress
* @param rolloverChain If LEFTOVER_VALUE > NEEDED_VALUE, then the maxTransactionFee of one of the requests in the array of sends will be incremented by 'LEFTOVER_VALUE - NEEDED_VALUE'
* Specifically, the 'send' that will have it's maxTransactionFee incremented is the first send in the 'requests.requests' array that has targetChain equal to 'rolloverChain'
* @param nonce The messages to be relayed are all of the emitted wormhole messages in the current transaction that have nonce 'nonce' * @param nonce The messages to be relayed are all of the emitted wormhole messages in the current transaction that have nonce 'nonce'
* *
*/ */
function multichainForward(MultichainSend memory requests, uint16 rolloverChain, uint32 nonce) external payable; function multichainForward(MultichainSend memory requests, uint32 nonce) external payable;
/** /**
* @notice quoteGas tells you how much maxTransactionFee (denominated in current (source) chain currency) must be in order to fund a call to * @notice quoteGas tells you how much maxTransactionFee (denominated in current (source) chain currency) must be in order to fund a call to
@ -354,15 +354,13 @@ interface IWormholeRelayer {
*/ */
function getDefaultRelayParams() external pure returns (bytes memory relayParams); function getDefaultRelayParams() external pure returns (bytes memory relayParams);
error FundsTooMuch(); // (maxTransactionFee, converted to target chain currency) + (receiverValue, converted to target chain currency) is greater than what your chosen relay provider allows error FundsTooMuch(uint8 multisendIndex); // (maxTransactionFee, converted to target chain currency) + (receiverValue, converted to target chain currency) is greater than what your chosen relay provider allows
error MaxTransactionFeeNotEnough(); // maxTransactionFee is less than the minimum needed by your chosen relay provider error MaxTransactionFeeNotEnough(uint8 multisendIndex); // maxTransactionFee is less than the minimum needed by your chosen relay provider
error MsgValueTooLow(); // msg.value is too low error MsgValueTooLow(); // msg.value is too low
// Specifically, (msg.value) + (any leftover funds if this is a forward) is less than (maxTransactionFee + receiverValue), summed over all of your requests if this is a multichainSend/multichainForward // Specifically, (msg.value) + (any leftover funds if this is a forward) is less than (maxTransactionFee + receiverValue), summed over all of your requests if this is a multichainSend/multichainForward
error NonceIsZero(); // Nonce cannot be 0 error NonceIsZero(); // Nonce cannot be 0
error NoDeliveryInProcess(); // Forwards can only be requested within execution of 'receiveWormholeMessages', or when a delivery is in progress error NoDeliveryInProgress(); // Forwards can only be requested within execution of 'receiveWormholeMessages', or when a delivery is in progress
error MultipleForwardsRequested(); // Only one forward can be requested in a transaction error MultipleForwardsRequested(); // Only one forward can be requested in a transaction
error ForwardRequestFromWrongAddress(); // A forward was requested from an address that is not the 'targetAddress' of the original delivery
error RelayProviderDoesNotSupportTargetChain(); // Your relay provider does not support the target chain you specified error RelayProviderDoesNotSupportTargetChain(); // Your relay provider does not support the target chain you specified
error RolloverChainNotIncluded(); // None of the Send structs in your multiForward are for the target chain 'rolloverChain'
error ChainNotFoundInSends(uint16 chainId); // This should never happen. Post a Github Issue if this occurs
error ReentrantCall(); // A delivery cannot occur during another delivery
} }

View File

@ -174,7 +174,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
relayProviderAddress: relayer.getDefaultRelayProvider() relayProviderAddress: relayer.getDefaultRelayProvider()
}); });
relayer.multichainForward(container, sendRequests[0].targetChain, parsed.nonce); relayer.multichainForward(container, parsed.nonce);
} }
} }

View File

@ -10,6 +10,7 @@ import {RelayProviderProxy} from "../contracts/relayProvider/RelayProviderProxy.
import {RelayProviderMessages} from "../contracts/relayProvider/RelayProviderMessages.sol"; import {RelayProviderMessages} from "../contracts/relayProvider/RelayProviderMessages.sol";
import {RelayProviderStructs} from "../contracts/relayProvider/RelayProviderStructs.sol"; import {RelayProviderStructs} from "../contracts/relayProvider/RelayProviderStructs.sol";
import {IWormholeRelayer} from "../contracts/interfaces/IWormholeRelayer.sol"; import {IWormholeRelayer} from "../contracts/interfaces/IWormholeRelayer.sol";
import {IDelivery} from "../contracts/interfaces/IDelivery.sol";
import {CoreRelayer} from "../contracts/coreRelayer/CoreRelayer.sol"; import {CoreRelayer} from "../contracts/coreRelayer/CoreRelayer.sol";
import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol"; import {CoreRelayerStructs} from "../contracts/coreRelayer/CoreRelayerStructs.sol";
import {CoreRelayerSetup} from "../contracts/coreRelayer/CoreRelayerSetup.sol"; import {CoreRelayerSetup} from "../contracts/coreRelayer/CoreRelayerSetup.sol";
@ -404,6 +405,9 @@ contract TestCoreRelayer is Test {
uint256 relayerProfit = uint256(feeParams.sourceNativePrice) uint256 relayerProfit = uint256(feeParams.sourceNativePrice)
* (setup.source.rewardAddress.balance - rewardAddressBalance) * (setup.source.rewardAddress.balance - rewardAddressBalance)
- feeParams.targetNativePrice * (relayerBalance - setup.target.relayer.balance); - feeParams.targetNativePrice * (relayerBalance - setup.target.relayer.balance);
console.log(USDcost);
console.log(relayerProfit);
console.log((USDcost - relayerProfit));
assertTrue(USDcost == relayerProfit, "We paid the exact amount"); assertTrue(USDcost == relayerProfit, "We paid the exact amount");
} }
@ -774,8 +778,8 @@ contract TestCoreRelayer is Test {
IWormhole.VM parsed; IWormhole.VM parsed;
uint256 budget; uint256 budget;
IWormholeRelayer.ResendByTx redeliveryRequest; IWormholeRelayer.ResendByTx redeliveryRequest;
CoreRelayer.TargetDeliveryParametersSingle originalDelivery; IDelivery.TargetDeliveryParametersSingle originalDelivery;
CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle package; IDelivery.TargetRedeliveryByTxHashParamsSingle package;
CoreRelayer.RedeliveryByTxHashInstruction instruction; CoreRelayer.RedeliveryByTxHashInstruction instruction;
} }
@ -871,12 +875,12 @@ contract TestCoreRelayer is Test {
invalidateVM(fakeVM, setup.target.wormholeSimulator); invalidateVM(fakeVM, setup.target.wormholeSimulator);
stack.originalDelivery.encodedVMs[2] = fakeVM; stack.originalDelivery.encodedVMs[2] = fakeVM;
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
stack.parsed = relayerWormhole.parseVM(stack.redeliveryVM); stack.parsed = relayerWormhole.parseVM(stack.redeliveryVM);
stack.instruction = setup.target.coreRelayerFull.getRedeliveryByTxHashInstruction(stack.parsed.payload); stack.instruction = setup.target.coreRelayerFull.decodeRedeliveryInstruction(stack.parsed.payload);
stack.budget = stack.instruction.newMaximumRefundTarget + stack.instruction.newReceiverValueTarget stack.budget = stack.instruction.newMaximumRefundTarget + stack.instruction.newReceiverValueTarget
+ setup.target.wormhole.messageFee(); + setup.target.wormhole.messageFee();
@ -884,12 +888,12 @@ contract TestCoreRelayer is Test {
vm.deal(setup.target.relayer, stack.budget); vm.deal(setup.target.relayer, stack.budget);
vm.prank(setup.target.relayer); vm.prank(setup.target.relayer);
vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint8)", 2)); vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint8,string)", 2, ""));
setup.target.coreRelayerFull.redeliverSingle{value: stack.budget}(stack.package); setup.target.coreRelayerFull.redeliverSingle{value: stack.budget}(stack.package);
stack.originalDelivery.encodedVMs[2] = stack.originalDelivery.encodedVMs[0]; stack.originalDelivery.encodedVMs[2] = stack.originalDelivery.encodedVMs[0];
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
@ -899,7 +903,7 @@ contract TestCoreRelayer is Test {
stack.originalDelivery.encodedVMs[2] = correctVM; stack.originalDelivery.encodedVMs[2] = correctVM;
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
@ -907,7 +911,7 @@ contract TestCoreRelayer is Test {
fakeVM = abi.encodePacked(correctVM); fakeVM = abi.encodePacked(correctVM);
invalidateVM(fakeVM, setup.target.wormholeSimulator); invalidateVM(fakeVM, setup.target.wormholeSimulator);
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
@ -918,7 +922,7 @@ contract TestCoreRelayer is Test {
fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs( fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs(
stack.entries[0], setup.sourceChainId, address(setup.source.integration) stack.entries[0], setup.sourceChainId, address(setup.source.integration)
); );
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
@ -940,7 +944,7 @@ contract TestCoreRelayer is Test {
fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs( fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs(
stack.entries[0], setup.sourceChainId, address(setup.source.coreRelayer) stack.entries[0], setup.sourceChainId, address(setup.source.coreRelayer)
); );
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
@ -948,7 +952,7 @@ contract TestCoreRelayer is Test {
vm.expectRevert(abi.encodeWithSignature("MismatchingRelayProvidersInRedelivery()")); vm.expectRevert(abi.encodeWithSignature("MismatchingRelayProvidersInRedelivery()"));
setup.target.coreRelayerFull.redeliverSingle{value: stack.budget}(stack.package); setup.target.coreRelayerFull.redeliverSingle{value: stack.budget}(stack.package);
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(msg.sender) stack.redeliveryVM, stack.originalDelivery.encodedVMs, payable(msg.sender)
); );
@ -992,7 +996,6 @@ contract TestCoreRelayer is Test {
newRelayParameters: setup.source.coreRelayer.getDefaultRelayParams() newRelayParameters: setup.source.coreRelayer.getDefaultRelayParams()
}); });
setup.source.relayProvider.updatePrice(differentChainId, gasParams.targetGasPrice, feeParams.targetNativePrice); setup.source.relayProvider.updatePrice(differentChainId, gasParams.targetGasPrice, feeParams.targetNativePrice);
setup.source.relayProvider.updatePrice(differentChainId, gasParams.sourceGasPrice, feeParams.sourceNativePrice);
setup.source.relayProvider.updateDeliveryAddress( setup.source.relayProvider.updateDeliveryAddress(
differentChainId, bytes32(uint256(uint160(address(setup.target.relayer)))) differentChainId, bytes32(uint256(uint160(address(setup.target.relayer))))
); );
@ -1009,12 +1012,13 @@ contract TestCoreRelayer is Test {
fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs( fakeVM = relayerWormholeSimulator.fetchSignedMessageFromLogs(
stack.entries[0], setup.sourceChainId, address(setup.source.coreRelayer) stack.entries[0], setup.sourceChainId, address(setup.source.coreRelayer)
); );
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle( stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle(
fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer) fakeVM, stack.originalDelivery.encodedVMs, payable(setup.target.relayer)
); );
redeliveryVmHash = relayerWormhole.parseVM(fakeVM).hash; redeliveryVmHash = relayerWormhole.parseVM(fakeVM).hash;
uint256 txValue = stack.payment + map[differentChainId].wormhole.messageFee(); uint256 txValue = stack.payment * feeParams.sourceNativePrice / feeParams.targetNativePrice + 1
+ map[differentChainId].wormhole.messageFee();
vm.deal(setup.target.relayer, txValue); vm.deal(setup.target.relayer, txValue);
vm.expectEmit(true, true, true, true, address(map[differentChainId].coreRelayer)); vm.expectEmit(true, true, true, true, address(map[differentChainId].coreRelayer));
@ -1028,7 +1032,7 @@ contract TestCoreRelayer is Test {
vm.prank(setup.target.relayer); vm.prank(setup.target.relayer);
map[differentChainId].coreRelayerFull.redeliverSingle{value: txValue}(stack.package); map[differentChainId].coreRelayerFull.redeliverSingle{value: txValue}(stack.package);
stack.package = CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle({ stack.package = IDelivery.TargetRedeliveryByTxHashParamsSingle({
redeliveryVM: correctVM, redeliveryVM: correctVM,
sourceEncodedVMs: stack.originalDelivery.encodedVMs, sourceEncodedVMs: stack.originalDelivery.encodedVMs,
relayerRefundAddress: payable(setup.target.relayer) relayerRefundAddress: payable(setup.target.relayer)
@ -1063,7 +1067,7 @@ contract TestCoreRelayer is Test {
bytes[] encodedVMs; bytes[] encodedVMs;
IWormhole.VM parsed; IWormhole.VM parsed;
uint256 budget; uint256 budget;
CoreRelayer.TargetDeliveryParametersSingle package; IDelivery.TargetDeliveryParametersSingle package;
CoreRelayer.DeliveryInstruction instruction; CoreRelayer.DeliveryInstruction instruction;
} }
@ -1111,7 +1115,7 @@ contract TestCoreRelayer is Test {
stack.encodedVMs[1] = stack.actualVM2; stack.encodedVMs[1] = stack.actualVM2;
stack.encodedVMs[2] = stack.deliveryVM; stack.encodedVMs[2] = stack.deliveryVM;
stack.package = CoreRelayerStructs.TargetDeliveryParametersSingle({ stack.package = IDelivery.TargetDeliveryParametersSingle({
encodedVMs: stack.encodedVMs, encodedVMs: stack.encodedVMs,
deliveryIndex: 2, deliveryIndex: 2,
multisendIndex: 0, multisendIndex: 0,
@ -1120,7 +1124,7 @@ contract TestCoreRelayer is Test {
stack.parsed = relayerWormhole.parseVM(stack.deliveryVM); stack.parsed = relayerWormhole.parseVM(stack.deliveryVM);
stack.instruction = stack.instruction =
setup.target.coreRelayerFull.getDeliveryInstructionsContainer(stack.parsed.payload).instructions[0]; setup.target.coreRelayerFull.decodeDeliveryInstructionsContainer(stack.parsed.payload).instructions[0];
stack.budget = stack.instruction.maximumRefundTarget + stack.instruction.receiverValueTarget stack.budget = stack.instruction.maximumRefundTarget + stack.instruction.receiverValueTarget
+ setup.source.wormhole.messageFee(); + setup.source.wormhole.messageFee();
@ -1178,7 +1182,7 @@ contract TestCoreRelayer is Test {
stack.encodedVMs[1] = stack.actualVM2; stack.encodedVMs[1] = stack.actualVM2;
stack.encodedVMs[2] = fakeVM; stack.encodedVMs[2] = fakeVM;
stack.package = CoreRelayerStructs.TargetDeliveryParametersSingle({ stack.package = IDelivery.TargetDeliveryParametersSingle({
encodedVMs: stack.encodedVMs, encodedVMs: stack.encodedVMs,
deliveryIndex: 2, deliveryIndex: 2,
multisendIndex: 0, multisendIndex: 0,
@ -1187,18 +1191,18 @@ contract TestCoreRelayer is Test {
stack.parsed = relayerWormhole.parseVM(stack.deliveryVM); stack.parsed = relayerWormhole.parseVM(stack.deliveryVM);
stack.instruction = stack.instruction =
setup.target.coreRelayerFull.getDeliveryInstructionsContainer(stack.parsed.payload).instructions[0]; setup.target.coreRelayerFull.decodeDeliveryInstructionsContainer(stack.parsed.payload).instructions[0];
stack.budget = stack.instruction.maximumRefundTarget + stack.instruction.receiverValueTarget stack.budget = stack.instruction.maximumRefundTarget + stack.instruction.receiverValueTarget
+ setup.target.wormhole.messageFee(); + setup.target.wormhole.messageFee();
vm.prank(setup.target.relayer); vm.prank(setup.target.relayer);
vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint8)", 2)); vm.expectRevert(abi.encodeWithSignature("InvalidVaa(uint8,string)", 2, ""));
setup.target.coreRelayerFull.deliverSingle{value: stack.budget}(stack.package); setup.target.coreRelayerFull.deliverSingle{value: stack.budget}(stack.package);
stack.encodedVMs[2] = stack.encodedVMs[0]; stack.encodedVMs[2] = stack.encodedVMs[0];
stack.package = CoreRelayerStructs.TargetDeliveryParametersSingle({ stack.package = IDelivery.TargetDeliveryParametersSingle({
encodedVMs: stack.encodedVMs, encodedVMs: stack.encodedVMs,
deliveryIndex: 2, deliveryIndex: 2,
multisendIndex: 0, multisendIndex: 0,
@ -1211,7 +1215,7 @@ contract TestCoreRelayer is Test {
stack.encodedVMs[2] = stack.deliveryVM; stack.encodedVMs[2] = stack.deliveryVM;
stack.package = CoreRelayerStructs.TargetDeliveryParametersSingle({ stack.package = IDelivery.TargetDeliveryParametersSingle({
encodedVMs: stack.encodedVMs, encodedVMs: stack.encodedVMs,
deliveryIndex: 2, deliveryIndex: 2,
multisendIndex: 0, multisendIndex: 0,
@ -1291,8 +1295,10 @@ contract TestCoreRelayer is Test {
relayParameters: setup.source.coreRelayer.getDefaultRelayParams() relayParameters: setup.source.coreRelayer.getDefaultRelayParams()
}); });
vm.expectRevert(abi.encodeWithSignature("MaxTransactionFeeNotEnough()")); uint256 wormholeFee = setup.source.wormhole.messageFee();
setup.source.coreRelayer.send{value: stack.deliveryOverhead - 1}(
vm.expectRevert(abi.encodeWithSignature("MaxTransactionFeeNotEnough(uint8)", 0));
setup.source.coreRelayer.send{value: stack.deliveryOverhead - 1 + wormholeFee}(
stack.badSend, 1, address(setup.source.relayProvider) stack.badSend, 1, address(setup.source.relayProvider)
); );
@ -1302,7 +1308,7 @@ contract TestCoreRelayer is Test {
setup.targetChainId, uint256(gasParams.targetGasLimit - 1) * gasParams.targetGasPrice setup.targetChainId, uint256(gasParams.targetGasLimit - 1) * gasParams.targetGasPrice
); );
vm.expectRevert(abi.encodeWithSignature("FundsTooMuch()")); vm.expectRevert(abi.encodeWithSignature("FundsTooMuch(uint8)", 0));
setup.source.coreRelayer.send{value: stack.payment}( setup.source.coreRelayer.send{value: stack.payment}(
stack.deliveryRequest, 1, address(setup.source.relayProvider) stack.deliveryRequest, 1, address(setup.source.relayProvider)
); );
@ -1318,7 +1324,7 @@ contract TestCoreRelayer is Test {
mapping(uint256 => bool) nonceCompleted; mapping(uint256 => bool) nonceCompleted;
mapping(bytes32 => CoreRelayer.TargetDeliveryParametersSingle) pastDeliveries; mapping(bytes32 => IDelivery.TargetDeliveryParametersSingle) pastDeliveries;
function genericRelayer(uint16 chainId, uint8 num) internal { function genericRelayer(uint16 chainId, uint8 num) internal {
Vm.Log[] memory entries = truncateRecordedLogs(chainId, num); Vm.Log[] memory entries = truncateRecordedLogs(chainId, num);
@ -1395,20 +1401,19 @@ contract TestCoreRelayer is Test {
function genericRelay( function genericRelay(
Contracts memory contracts, Contracts memory contracts,
uint8 counter, uint8 counter,
bytes memory encodedDeliveryInstructionContainer, bytes memory encodedDeliveryInstructionsContainer,
bytes[] memory encodedVMsToBeDelivered, bytes[] memory encodedVMsToBeDelivered,
IWormhole.VM memory parsedInstruction IWormhole.VM memory parsedInstruction
) internal { ) internal {
uint8 payloadId = parsedInstruction.payload.toUint8(0); uint8 payloadId = parsedInstruction.payload.toUint8(0);
if (payloadId == 1) { if (payloadId == 1) {
CoreRelayer.DeliveryInstructionsContainer memory container = CoreRelayer.DeliveryInstructionsContainer memory container =
contracts.coreRelayerFull.getDeliveryInstructionsContainer(parsedInstruction.payload); contracts.coreRelayerFull.decodeDeliveryInstructionsContainer(parsedInstruction.payload);
for (uint8 k = 0; k < container.instructions.length; k++) { for (uint8 k = 0; k < container.instructions.length; k++) {
uint256 budget = uint256 budget =
container.instructions[k].maximumRefundTarget + container.instructions[k].receiverValueTarget; container.instructions[k].maximumRefundTarget + container.instructions[k].receiverValueTarget;
uint16 targetChain = container.instructions[k].targetChain; uint16 targetChain = container.instructions[k].targetChain;
CoreRelayer.TargetDeliveryParametersSingle memory package = CoreRelayerStructs IDelivery.TargetDeliveryParametersSingle memory package = IDelivery.TargetDeliveryParametersSingle({
.TargetDeliveryParametersSingle({
encodedVMs: encodedVMsToBeDelivered, encodedVMs: encodedVMsToBeDelivered,
deliveryIndex: counter, deliveryIndex: counter,
multisendIndex: k, multisendIndex: k,
@ -1421,15 +1426,15 @@ contract TestCoreRelayer is Test {
} }
} else if (payloadId == 2) { } else if (payloadId == 2) {
CoreRelayer.RedeliveryByTxHashInstruction memory instruction = CoreRelayer.RedeliveryByTxHashInstruction memory instruction =
contracts.coreRelayerFull.getRedeliveryByTxHashInstruction(parsedInstruction.payload); contracts.coreRelayerFull.decodeRedeliveryInstruction(parsedInstruction.payload);
CoreRelayer.TargetDeliveryParametersSingle memory originalDelivery = IDelivery.TargetDeliveryParametersSingle memory originalDelivery =
pastDeliveries[keccak256(abi.encodePacked(instruction.sourceTxHash, instruction.multisendIndex))]; pastDeliveries[keccak256(abi.encodePacked(instruction.sourceTxHash, instruction.multisendIndex))];
uint16 targetChain = instruction.targetChain; uint16 targetChain = instruction.targetChain;
uint256 budget = instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget uint256 budget = instruction.newMaximumRefundTarget + instruction.newReceiverValueTarget
+ map[targetChain].wormhole.messageFee(); + map[targetChain].wormhole.messageFee();
CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingle memory package = CoreRelayerStructs IDelivery.TargetRedeliveryByTxHashParamsSingle memory package = IDelivery
.TargetRedeliveryByTxHashParamsSingle({ .TargetRedeliveryByTxHashParamsSingle({
redeliveryVM: encodedDeliveryInstructionContainer, redeliveryVM: encodedDeliveryInstructionsContainer,
sourceEncodedVMs: originalDelivery.encodedVMs, sourceEncodedVMs: originalDelivery.encodedVMs,
relayerRefundAddress: payable(map[targetChain].relayer) relayerRefundAddress: payable(map[targetChain].relayer)
}); });

View File

@ -14,7 +14,7 @@ import {
loadCoreRelayers, loadCoreRelayers,
loadMockIntegrations, loadMockIntegrations,
} from "../ts-scripts/helpers/env" } from "../ts-scripts/helpers/env"
import { MockRelayerIntegration, CoreRelayerStructs } from "../../sdk/src" import { MockRelayerIntegration, IWormholeRelayer } from "../../sdk/src"
const ETHEREUM_ROOT = `${__dirname}/..` const ETHEREUM_ROOT = `${__dirname}/..`
init() init()
@ -314,7 +314,7 @@ describe("Core Relayer Integration Test - Two Chains", () => {
expect(message).to.not.equal(arbitraryPayload) expect(message).to.not.equal(arbitraryPayload)
console.log("Resending the message"); console.log("Resending the message");
const request: CoreRelayerStructs.ResendByTxStruct = { const request: IWormholeRelayer.ResendByTxStruct = {
sourceChain: sourceChain.chainId, sourceChain: sourceChain.chainId,
sourceTxHash: tx.hash, sourceTxHash: tx.hash,
sourceNonce: 1, sourceNonce: 1,
@ -402,7 +402,7 @@ describe("Core Relayer Integration Test - Two Chains", () => {
// RESEND THE MESSAGE SOMEHOW! // RESEND THE MESSAGE SOMEHOW!
/*console.log("Resending the message"); /*console.log("Resending the message");
const request: CoreRelayerStructs.ResendByTxStruct = { const request: IWormholeRelayer.ResendByTxStruct = {
sourceChain: sourceChain.chainId, sourceChain: sourceChain.chainId,
sourceTxHash: tx.hash, sourceTxHash: tx.hash,
sourceNonce: 1, sourceNonce: 1,

View File

@ -6,4 +6,6 @@ export type { IWormhole, LogMessagePublishedEvent } from "./ethers-contracts/IWo
export { IWormhole__factory } from "./ethers-contracts/factories/IWormhole__factory" export { IWormhole__factory } from "./ethers-contracts/factories/IWormhole__factory"
export type { RelayProvider } from "./ethers-contracts/RelayProvider" export type { RelayProvider } from "./ethers-contracts/RelayProvider"
export { RelayProvider__factory } from "./ethers-contracts/factories/RelayProvider__factory" export { RelayProvider__factory } from "./ethers-contracts/factories/RelayProvider__factory"
export type {IDelivery} from "./ethers-contracts/IDelivery"
export type {IWormholeRelayer} from "./ethers-contracts/IWormholeRelayer"
export * from './structs' export * from './structs'

View File

@ -24,7 +24,7 @@ import {
IWormhole__factory, IWormhole__factory,
RelayProvider__factory, RelayProvider__factory,
LogMessagePublishedEvent, LogMessagePublishedEvent,
CoreRelayerStructs, IDelivery,
DeliveryInstructionsContainer, DeliveryInstructionsContainer,
parseDeliveryInstructionsContainer, parseDeliveryInstructionsContainer,
parseRedeliveryByTxHashInstruction, parseRedeliveryByTxHashInstruction,
@ -545,7 +545,7 @@ export class GenericRelayerPlugin implements Plugin<WorkflowPayload> {
wallet wallet
) )
const input: CoreRelayerStructs.TargetDeliveryParametersSingleStruct = { const input: IDelivery.TargetDeliveryParametersSingleStruct = {
encodedVMs: payload.vaas, encodedVMs: payload.vaas,
deliveryIndex: payload.deliveryVaaIndex, deliveryIndex: payload.deliveryVaaIndex,
multisendIndex: i, multisendIndex: i,
@ -597,7 +597,7 @@ export class GenericRelayerPlugin implements Plugin<WorkflowPayload> {
const { newReceiverValueTarget, newMaximumRefundTarget } = redelivery.ix const { newReceiverValueTarget, newMaximumRefundTarget } = redelivery.ix
const budget = newReceiverValueTarget.add(newMaximumRefundTarget).add(100) const budget = newReceiverValueTarget.add(newMaximumRefundTarget).add(100)
const input: CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingleStruct = { const input: IDelivery.TargetRedeliveryByTxHashParamsSingleStruct = {
sourceEncodedVMs: payload.vaas, sourceEncodedVMs: payload.vaas,
redeliveryVM: redelivery.vaa.bytes, redeliveryVM: redelivery.vaa.bytes,
relayerRefundAddress: wallet.address, relayerRefundAddress: wallet.address,

View File

@ -6,4 +6,6 @@ export type { IWormhole, LogMessagePublishedEvent } from "./ethers-contracts/IWo
export { IWormhole__factory } from "./ethers-contracts/factories/IWormhole__factory" export { IWormhole__factory } from "./ethers-contracts/factories/IWormhole__factory"
export type { RelayProvider } from "./ethers-contracts/RelayProvider" export type { RelayProvider } from "./ethers-contracts/RelayProvider"
export { RelayProvider__factory } from "./ethers-contracts/factories/RelayProvider__factory" export { RelayProvider__factory } from "./ethers-contracts/factories/RelayProvider__factory"
export type {IDelivery} from "./ethers-contracts/IDelivery"
export type {IWormholeRelayer} from "./ethers-contracts/IWormholeRelayer"
export * from './structs' export * from './structs'