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;
import "../libraries/external/BytesLib.sol";
import "../interfaces/IWormholeRelayer.sol";
import "../interfaces/IWormholeReceiver.sol";
import "../interfaces/IDelivery.sol";
import "./CoreRelayerGovernance.sol";
import "./CoreRelayerStructs.sol";
contract CoreRelayer is CoreRelayerGovernance {
using BytesLib for bytes;
enum DeliveryStatus {
SUCCESS,
RECEIVER_FAILURE,
@ -28,113 +26,39 @@ contract CoreRelayer is CoreRelayerGovernance {
DeliveryStatus status
);
error FundsTooMuch();
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)
function send(IWormholeRelayer.Send memory request, uint32 nonce, address relayProvider)
public
payable
returns (uint64 sequence)
{
Send[] memory requests = new Send[](1);
requests[0] = request;
MultichainSend memory container = MultichainSend({relayProviderAddress: address(provider), requests: requests});
return multichainSend(container, nonce);
return multichainSend(multichainSendContainer(request, relayProvider), nonce);
}
function forward(Send memory request, uint32 nonce, IRelayProvider provider) public payable {
Send[] memory requests = new Send[](1);
requests[0] = request;
MultichainSend memory container = MultichainSend({relayProviderAddress: address(provider), requests: requests});
return multichainForward(container, request.targetChain, nonce);
function forward(IWormholeRelayer.Send memory request, uint32 nonce, address relayProvider) public payable {
return multichainForward(multichainSendContainer(request, relayProvider), nonce);
}
function resend(ResendByTx memory request, uint32 nonce, IRelayProvider provider)
function resend(IWormholeRelayer.ResendByTx memory request, uint32 nonce, address relayProvider)
public
payable
returns (uint64 sequence)
{
(uint256 requestFee, uint256 maximumRefund, uint256 receiverValueTarget, bool isSufficient, uint8 reason) =
verifyFunding(
VerifyFundingCalculation({
provider: provider,
sourceChain: chainId(),
targetChain: request.targetChain,
maxTransactionFeeSource: request.newMaxTransactionFee,
receiverValueSource: request.newReceiverValue,
isDelivery: false
})
);
updateWormholeMessageFee();
bool isSufficient = request.newMaxTransactionFee + request.newReceiverValue + wormholeMessageFee() <= msg.value;
if (!isSufficient) {
if (reason == 26) {
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();
revert IWormholeRelayer.MsgValueTooLow();
}
sequence = emitRedelivery(
request,
nonce,
provider.getConsistencyLevel(),
receiverValueTarget,
maximumRefund,
provider,
wormhole,
wormholeMessageFee
IRelayProvider provider = IRelayProvider(relayProvider);
RedeliveryByTxHashInstruction memory instruction = convertResendToRedeliveryInstruction(request, provider);
checkRedeliveryInstruction(instruction, provider);
sequence = wormhole().publishMessage{value: wormholeMessageFee()}(
nonce, encodeRedeliveryInstruction(instruction), provider.getConsistencyLevel()
);
//Send the delivery fees to the specified address of the provider.
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);
pay(provider.getRewardAddress(), msg.value - wormholeMessageFee());
}
/**
@ -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 generates a VAA with the encoded DeliveryInstructions
*/
function multichainSend(MultichainSend memory deliveryRequests, uint32 nonce)
function multichainSend(IWormholeRelayer.MultichainSend memory deliveryRequests, uint32 nonce)
public
payable
returns (uint64 sequence)
{
(uint256 totalCost, bool isSufficient, uint8 cause) = sufficientFundsHelper(deliveryRequests, msg.value);
if (!isSufficient) {
if (cause == 26) {
revert MaxTransactionFeeNotEnough();
} else if (cause == 25) {
revert MsgValueTooLow();
} else {
revert FundsTooMuch();
}
updateWormholeMessageFee();
uint256 totalFee = getTotalFeeMultichainSend(deliveryRequests);
if (totalFee > msg.value) {
revert IWormholeRelayer.MsgValueTooLow();
}
if (nonce == 0) {
revert NonceIsZero();
revert IWormholeRelayer.NonceIsZero();
}
// encode the DeliveryInstructions
bytes memory container = convertToEncodedDeliveryInstructions(deliveryRequests, true);
IRelayProvider relayProvider = IRelayProvider(deliveryRequests.relayProviderAddress);
DeliveryInstructionsContainer memory container =
convertMultichainSendToDeliveryInstructionsContainer(deliveryRequests);
checkInstructions(container, IRelayProvider(deliveryRequests.relayProviderAddress));
container.sufficientlyFunded = true;
// emit delivery message
IWormhole wormhole = wormhole();
IRelayProvider provider = IRelayProvider(deliveryRequests.relayProviderAddress);
uint256 wormholeMessageFee = wormhole.messageFee();
sequence = wormhole.publishMessage{value: wormholeMessageFee}(nonce, container, provider.getConsistencyLevel());
sequence = wormhole().publishMessage{value: wormholeMessageFee()}(
nonce, encodeDeliveryInstructionsContainer(container), relayProvider.getConsistencyLevel()
);
//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 generates a VAA with the encoded DeliveryInstructions
*/
function multichainForward(MultichainSend memory deliveryRequests, uint16 rolloverChain, uint32 nonce)
public
payable
{
// Can only forward while a delivery is in process.
function multichainForward(IWormholeRelayer.MultichainSend memory deliveryRequests, uint32 nonce) public payable {
if (!isContractLocked()) {
revert NoDeliveryInProcess();
revert IWormholeRelayer.NoDeliveryInProgress();
}
if (getForwardingRequest().isValid) {
revert CantRequestMultipleForwards();
if (getForwardInstruction().isValid) {
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.
verifyForwardingRequest(deliveryRequests, rolloverChain, nonce);
uint256 totalFee = getTotalFeeMultichainSend(deliveryRequests);
DeliveryInstructionsContainer memory container =
convertMultichainSendToDeliveryInstructionsContainer(deliveryRequests);
checkInstructions(container, IRelayProvider(deliveryRequests.relayProviderAddress));
bytes memory encodedMultichainSend = encodeMultichainSend(deliveryRequests);
setForwardingRequest(
ForwardingRequest({
deliveryRequestsContainer: encodedMultichainSend,
rolloverChain: rolloverChain,
setForwardInstruction(
ForwardInstruction({
container: container,
nonce: nonce,
msgValue: msg.value,
totalFee: totalFee,
sender: msg.sender,
relayProvider: deliveryRequests.relayProviderAddress,
isValid: true
})
);
}
function emitForward(uint256 refundAmount, ForwardingRequest memory forwardingRequest)
function emitForward(uint256 transactionFeeRefundAmount, ForwardInstruction memory forwardInstruction)
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
refundAmount = refundAmount + forwardingRequest.msgValue;
transactionFeeRefundAmount = transactionFeeRefundAmount + forwardInstruction.msgValue;
//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) {
//find the delivery instruction for the rollover chain
uint16 rolloverInstructionIndex = findDeliveryIndex(container, forwardingRequest.rolloverChain);
//calc how much budget is used by chains other than the rollover chain
uint256 rolloverChainCostEstimate = container.requests[rolloverInstructionIndex].maxTransactionFee
+ container.requests[rolloverInstructionIndex].receiverValue;
//uint256 nonrolloverBudget = totalMinimumFees - rolloverChainCostEstimate; //stack too deep
uint256 rolloverBudget = refundAmount - (totalMinimumFees - rolloverChainCostEstimate)
- container.requests[rolloverInstructionIndex].receiverValue;
//overwrite the gas budget on the rollover chain to the remaining budget amount
container.requests[rolloverInstructionIndex].maxTransactionFee = rolloverBudget;
if (forwardIsFunded) {
// the rollover chain is the chain in the first request
uint256 amountUnderMaximum = relayProvider.quoteMaximumBudget(container.instructions[0].targetChain)
- (
wormholeMessageFee() + container.instructions[0].maximumRefundTarget
+ container.instructions[0].receiverValueTarget
);
uint256 convertedExtraAmount = calculateTargetDeliveryMaximumRefund(
container.instructions[0].targetChain,
transactionFeeRefundAmount - forwardInstruction.totalFee,
relayProvider
);
container.instructions[0].maximumRefundTarget +=
(amountUnderMaximum > convertedExtraAmount) ? convertedExtraAmount : amountUnderMaximum;
}
//emit forwarding instruction
bytes memory reencoded = convertToEncodedDeliveryInstructions(container, funded);
IRelayProvider provider = IRelayProvider(container.relayProviderAddress);
IWormhole wormhole = wormhole();
uint64 sequence = wormhole.publishMessage{value: wormhole.messageFee()}(
forwardingRequest.nonce, reencoded, provider.getConsistencyLevel()
wormhole().publishMessage{value: wormholeMessageFee()}(
forwardInstruction.nonce,
encodeDeliveryInstructionsContainer(container),
relayProvider.getConsistencyLevel()
);
// if funded, pay out reward to provider. Otherwise, the delivery code will handle sending a refund.
if (funded) {
pay(provider.getRewardAddress(), refundAmount);
if (forwardIsFunded) {
pay(relayProvider.getRewardAddress(), transactionFeeRefundAmount);
}
//clear forwarding request from cache
clearForwardingRequest();
return (sequence, funded);
clearForwardInstruction();
}
function verifyForwardingRequest(MultichainSend memory container, uint16 rolloverChain, uint32 nonce)
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)
function multichainSendContainer(IWormholeRelayer.Send memory request, address relayProvider)
internal
pure
returns (uint16 deliveryRequestIndex)
returns (IWormholeRelayer.MultichainSend memory container)
{
for (uint16 i = 0; i < container.requests.length; i++) {
if (container.requests[i].targetChain == chainId) {
deliveryRequestIndex = i;
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;
}
IWormholeRelayer.Send[] memory requests = new IWormholeRelayer.Send[](1);
requests[0] = request;
container = IWormholeRelayer.MultichainSend({relayProviderAddress: relayProvider, requests: requests});
}
function _executeDelivery(
IWormhole wormhole,
DeliveryInstruction memory internalInstruction,
bytes[] memory encodedVMs,
bytes32 deliveryVaaHash,
@ -410,11 +208,11 @@ contract CoreRelayer is CoreRelayerGovernance {
uint16 sourceChain,
uint64 sourceSequence
) 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
if (isContractLocked()) {
revert ReentrantCall();
revert IDelivery.ReentrantCall();
}
setContractLock(true);
setLockedTargetAddress(fromWormholeFormat(internalInstruction.targetAddress));
@ -422,7 +220,7 @@ contract CoreRelayer is CoreRelayerGovernance {
uint256 preGas = gasleft();
// call the receiveWormholeMessages endpoint on the target contract
(bool success,) = fromWormholeFormat(internalInstruction.targetAddress).call{
(bool callToTargetContractSucceeded,) = fromWormholeFormat(internalInstruction.targetAddress).call{
gas: internalInstruction.executionParameters.gasLimit,
value: internalInstruction.receiverValueTarget
}(abi.encodeCall(IWormholeReceiver.receiveWormholeMessages, (encodedVMs, new bytes[](0))));
@ -438,126 +236,49 @@ contract CoreRelayer is CoreRelayerGovernance {
: (preGas - postGas);
// refund unused gas budget
uint256 weiToRefund = internalInstruction.receiverValueTarget;
if (success) {
weiToRefund = (internalInstruction.executionParameters.gasLimit - gasUsed)
* internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
}
uint256 transactionFeeRefundAmount = (internalInstruction.executionParameters.gasLimit - gasUsed)
* internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
// unlock the contract
setContractLock(false);
//REVISE decide if we want to always emit a VAA, or only emit a msg when forwarding
// // emit delivery status message
// DeliveryStatus memory status = DeliveryStatus({
// 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();
ForwardInstruction memory forwardingRequest = getForwardInstruction();
DeliveryStatus status;
bool forwardIsFunded = false;
if (forwardingRequest.isValid) {
(, success) = emitForward(weiToRefund, forwardingRequest);
if (success) {
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
});
}
forwardIsFunded = emitForward(transactionFeeRefundAmount, forwardingRequest);
status = forwardIsFunded ? DeliveryStatus.FORWARD_REQUEST_SUCCESS : DeliveryStatus.FORWARD_REQUEST_FAILURE;
} else {
bool sent = pay(payable(fromWormholeFormat(internalInstruction.refundAddress)), weiToRefund);
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
});
}
status = callToTargetContractSucceeded ? DeliveryStatus.SUCCESS : DeliveryStatus.RECEIVER_FAILURE;
}
uint256 receiverValuePaid = (success ? internalInstruction.receiverValueTarget : 0);
uint256 wormholeFeePaid = forwardingRequest.isValid ? wormhole.messageFee() : 0;
uint256 relayerRefundAmount = msg.value - weiToRefund - receiverValuePaid - wormholeFeePaid;
uint256 receiverValueRefundAmount =
(callToTargetContractSucceeded ? 0 : internalInstruction.receiverValueTarget);
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
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) {
return registeredCoreRelayerContract(vm.emitterChainId) == vm.emitterAddress;
}
@ -566,34 +287,34 @@ contract CoreRelayer is CoreRelayerGovernance {
return defaultRelayProvider();
}
function redeliverSingle(TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable {
function redeliverSingle(IDelivery.TargetRedeliveryByTxHashParamsSingle memory targetParams) public payable {
//cache wormhole
IWormhole wormhole = wormhole();
updateWormholeMessageFee();
//validate the redelivery VM
(IWormhole.VM memory redeliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.redeliveryVM);
if (!valid) {
revert InvalidRedeliveryVM(reason);
revert IDelivery.InvalidRedeliveryVM(reason);
}
if (!verifyRelayerVM(redeliveryVM)) {
// Redelivery VM has an invalid emitter
revert InvalidEmitterInRedeliveryVM();
revert IDelivery.InvalidEmitterInRedeliveryVM();
}
RedeliveryByTxHashInstruction memory redeliveryInstruction =
decodeRedeliveryByTxHashInstruction(redeliveryVM.payload);
RedeliveryByTxHashInstruction memory redeliveryInstruction = decodeRedeliveryInstruction(redeliveryVM.payload);
//validate the original delivery VM
IWormhole.VM memory originalDeliveryVM;
(originalDeliveryVM, valid, reason) =
wormhole.parseAndVerifyVM(targetParams.sourceEncodedVMs[redeliveryInstruction.deliveryIndex]);
if (!valid) {
revert InvalidVaa(redeliveryInstruction.deliveryIndex);
revert IDelivery.InvalidVaa(redeliveryInstruction.deliveryIndex, reason);
}
if (!verifyRelayerVM(originalDeliveryVM)) {
// Original Delivery VM has a invalid emitter
revert InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex);
revert IDelivery.InvalidEmitterInOriginalDeliveryVM(redeliveryInstruction.deliveryIndex);
}
DeliveryInstruction memory instruction;
@ -616,7 +337,6 @@ contract CoreRelayer is CoreRelayerGovernance {
}
_executeDelivery(
wormhole,
instruction,
targetParams.sourceEncodedVMs,
originalDeliveryVM.hash,
@ -635,16 +355,16 @@ contract CoreRelayer is CoreRelayerGovernance {
// The same relay provider must be specified when doing a single VAA redeliver.
address providerAddress = fromWormholeFormat(redeliveryInstruction.executionParameters.providerDeliveryAddress);
if (providerAddress != fromWormholeFormat(originalInstruction.executionParameters.providerDeliveryAddress)) {
revert MismatchingRelayProvidersInRedelivery();
revert IDelivery.MismatchingRelayProvidersInRedelivery();
}
// relayer must have covered the necessary funds
if (
msg.value
< redeliveryInstruction.newMaximumRefundTarget + redeliveryInstruction.newReceiverValueTarget
+ wormhole().messageFee()
+ wormholeMessageFee()
) {
revert InsufficientRelayerFunds();
revert IDelivery.InsufficientRelayerFunds();
}
uint16 whChainId = chainId();
@ -670,24 +390,25 @@ contract CoreRelayer is CoreRelayerGovernance {
deliveryInstruction.executionParameters = redeliveryInstruction.executionParameters;
}
function deliverSingle(TargetDeliveryParametersSingle memory targetParams) public payable {
function deliverSingle(IDelivery.TargetDeliveryParametersSingle memory targetParams) public payable {
// cache wormhole instance
IWormhole wormhole = wormhole();
updateWormholeMessageFee();
// validate the deliveryIndex
(IWormhole.VM memory deliveryVM, bool valid, string memory reason) =
wormhole.parseAndVerifyVM(targetParams.encodedVMs[targetParams.deliveryIndex]);
if (!valid) {
revert InvalidVaa(targetParams.deliveryIndex);
revert IDelivery.InvalidVaa(targetParams.deliveryIndex, reason);
}
if (!verifyRelayerVM(deliveryVM)) {
revert InvalidEmitter();
revert IDelivery.InvalidEmitter();
}
DeliveryInstructionsContainer memory container = decodeDeliveryInstructionsContainer(deliveryVM.payload);
//ensure this is a funded delivery, not a failed forward.
if (!container.sufficientlyFunded) {
revert SendNotSufficientlyFunded();
revert IDelivery.SendNotSufficientlyFunded();
}
// 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
if (fromWormholeFormat(deliveryInstruction.executionParameters.providerDeliveryAddress) != msg.sender) {
revert UnexpectedRelayer();
revert IDelivery.UnexpectedRelayer();
}
//make sure relayer passed in sufficient funds
if (
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
if (chainId() != deliveryInstruction.targetChain) {
revert TargetChainIsNotThisChain(deliveryInstruction.targetChain);
revert IDelivery.TargetChainIsNotThisChain(deliveryInstruction.targetChain);
}
_executeDelivery(
wormhole,
deliveryInstruction,
targetParams.encodedVMs,
deliveryVM.hash,
@ -734,107 +454,6 @@ contract CoreRelayer is CoreRelayerGovernance {
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)
public
view
@ -852,33 +471,6 @@ contract CoreRelayer is CoreRelayerGovernance {
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
function quoteReceiverValue(uint16 targetChain, uint256 targetAmount, IRelayProvider provider)
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) {
if (amount > 0) {
(success,) = receiver.call{value: amount}("");

View File

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

View File

@ -7,22 +7,302 @@ import "../libraries/external/BytesLib.sol";
import "./CoreRelayerGetters.sol";
import "./CoreRelayerStructs.sol";
import "../interfaces/IWormholeRelayer.sol";
contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
using BytesLib for bytes;
error InvalidPayloadId(uint8 payloadId);
error InvalidDeliveryInstructionsPayload(uint256 length);
error InvalidSendsPayload(uint256 length);
function decodeRedeliveryByTxHashInstruction(bytes memory encoded)
function getTotalFeeMultichainSend(IWormholeRelayer.MultichainSend memory sendContainer)
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
returns (RedeliveryByTxHashInstruction memory instruction)
{
uint256 index = 0;
instruction.payloadId = encoded.toUint8(index);
if (instruction.payloadId != 2) {
revert InvalidPayloadId(instruction.payloadId);
}
index += 1;
instruction.sourceChain = encoded.toUint16(index);
@ -60,7 +340,7 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
}
function decodeDeliveryInstructionsContainer(bytes memory encoded)
internal
public
pure
returns (DeliveryInstructionsContainer memory)
{
@ -121,82 +401,4 @@ contract CoreRelayerMessages is CoreRelayerStructs, CoreRelayerGetters {
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 "@openzeppelin/contracts/utils/Context.sol";
import "./CoreRelayerStructs.sol";
import {IWormhole} from "../interfaces/IWormhole.sol";
contract CoreRelayerSetters is CoreRelayerState, Context {
error InvalidEvmChainId();
@ -34,6 +35,10 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.provider.wormhole = payable(wh);
}
function updateWormholeMessageFee() internal {
_state.provider.wormholeMessageFee = IWormhole(_state.provider.wormhole).messageFee();
}
function setRelayProvider(address defaultRelayProvider) internal {
_state.defaultRelayProvider = defaultRelayProvider;
}
@ -42,12 +47,12 @@ contract CoreRelayerSetters is CoreRelayerState, Context {
_state.registeredCoreRelayerContract[chainId] = relayerAddress;
}
function setForwardingRequest(CoreRelayerStructs.ForwardingRequest memory request) internal {
_state.forwardingRequest = request;
function setForwardInstruction(CoreRelayerStructs.ForwardInstruction memory request) internal {
_state.forwardInstruction = request;
}
function clearForwardingRequest() internal {
delete _state.forwardingRequest; //TODO is this the best way to accomplish this?
function clearForwardInstruction() internal {
delete _state.forwardInstruction;
}
function setContractLock(bool status) internal {

View File

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

View File

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

View File

@ -3,74 +3,9 @@
pragma solidity ^0.8.0;
import "../interfaces/IWormhole.sol";
import "../interfaces/IWormholeRelayer.sol";
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 {
uint8 payloadId; //1
bool sufficientlyFunded;
@ -83,7 +18,7 @@ abstract contract CoreRelayerStructs {
bytes32 refundAddress;
uint256 maximumRefundTarget;
uint256 receiverValueTarget;
ExecutionParameters executionParameters; //Has the gas limit to execute with
ExecutionParameters executionParameters;
}
struct ExecutionParameters {
@ -105,23 +40,13 @@ abstract contract CoreRelayerStructs {
ExecutionParameters executionParameters;
}
//End Wire Types
//Internal usage structs
struct AllowedEmitterSequence {
// wormhole emitter address
bytes32 emitterAddress;
// wormhole message sequence
uint64 sequence;
}
struct ForwardingRequest {
bytes deliveryRequestsContainer;
uint16 rolloverChain;
struct ForwardInstruction {
DeliveryInstructionsContainer container;
uint32 nonce;
address sender;
uint256 msgValue;
uint256 totalFee;
address relayProvider;
bool isValid;
}
}

View File

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

View File

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

View File

@ -24,7 +24,7 @@ import {
IWormhole__factory,
RelayProvider__factory,
LogMessagePublishedEvent,
CoreRelayerStructs,
IDelivery,
DeliveryInstructionsContainer,
parseDeliveryInstructionsContainer,
parseRedeliveryByTxHashInstruction,
@ -545,7 +545,7 @@ export class GenericRelayerPlugin implements Plugin<WorkflowPayload> {
wallet
)
const input: CoreRelayerStructs.TargetDeliveryParametersSingleStruct = {
const input: IDelivery.TargetDeliveryParametersSingleStruct = {
encodedVMs: payload.vaas,
deliveryIndex: payload.deliveryVaaIndex,
multisendIndex: i,
@ -597,7 +597,7 @@ export class GenericRelayerPlugin implements Plugin<WorkflowPayload> {
const { newReceiverValueTarget, newMaximumRefundTarget } = redelivery.ix
const budget = newReceiverValueTarget.add(newMaximumRefundTarget).add(100)
const input: CoreRelayerStructs.TargetRedeliveryByTxHashParamsSingleStruct = {
const input: IDelivery.TargetRedeliveryByTxHashParamsSingleStruct = {
sourceEncodedVMs: payload.vaas,
redeliveryVM: redelivery.vaa.bytes,
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 type { RelayProvider } from "./ethers-contracts/RelayProvider"
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'