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:
parent
a1df44b3ee
commit
f7de3d649d
|
@ -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}("");
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'
|
|
@ -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,
|
||||||
|
|
|
@ -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'
|
Loading…
Reference in New Issue