Compare commits

...

4 Commits

Author SHA1 Message Date
derpy-duck 44f35d1dfb Cross chain refund test works except the last assert 2023-04-06 22:17:33 +00:00
derpy-duck b6395092a8 Test for Cross chain refunds 2023-04-06 22:04:21 +00:00
derpy-duck 4df8b4d838 return to old constants in test 2023-04-06 21:21:57 +00:00
derpy-duck 65ecddf2f4 Tests pass! 2023-04-06 21:17:32 +00:00
11 changed files with 275 additions and 132 deletions

View File

@ -18,7 +18,7 @@ contract CoreRelayer is CoreRelayerDelivery {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -53,13 +53,13 @@ contract CoreRelayer is CoreRelayerDelivery {
});
sequence = send(
IWormholeRelayer.Send(
targetChain,
targetAddress,
targetChain,
targetAddress,
refundAddress,
refundChain,
maxTransactionFee,
receiverValue,
payload,
maxTransactionFee,
receiverValue,
payload,
getDefaultRelayParams()
),
messageInfos,
@ -78,7 +78,7 @@ contract CoreRelayer is CoreRelayerDelivery {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -106,13 +106,13 @@ contract CoreRelayer is CoreRelayerDelivery {
) external payable returns (uint64 sequence) {
sequence = send(
IWormholeRelayer.Send(
targetChain,
targetAddress,
refundAddress,
targetChain,
targetAddress,
refundAddress,
refundChain,
maxTransactionFee,
receiverValue,
payload,
maxTransactionFee,
receiverValue,
payload,
getDefaultRelayParams()
),
messageInfos,
@ -174,7 +174,7 @@ contract CoreRelayer is CoreRelayerDelivery {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -199,13 +199,13 @@ contract CoreRelayer is CoreRelayerDelivery {
) external payable {
forward(
IWormholeRelayer.Send(
targetChain,
targetAddress,
refundAddress,
targetChain,
targetAddress,
refundAddress,
refundChain,
maxTransactionFee,
receiverValue,
payload,
maxTransactionFee,
receiverValue,
payload,
getDefaultRelayParams()
),
messageInfos,

View File

@ -103,81 +103,85 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
IWormholeRelayerInternalStructs.DeliveryInstructionsContainer memory deliveryContainer,
IWormholeRelayerInternalStructs.DeliveryVAAInfo memory vaaInfo
) internal {
if (internalInstruction.targetAddress != 0x0) {
if (isContractLocked()) {
revert IDelivery.ReentrantCall();
}
setContractLock(true);
setLockedTargetAddress(fromWormholeFormat(internalInstruction.targetAddress));
IWormholeReceiver.DeliveryData memory deliveryData;
deliveryData.sourceAddress = deliveryContainer.senderAddress;
deliveryData.sourceChain = vaaInfo.sourceChain;
deliveryData.maximumRefund = internalInstruction.maximumRefundTarget;
deliveryData.deliveryHash = vaaInfo.deliveryVaaHash;
deliveryData.payload = internalInstruction.payload;
uint256 preGas = gasleft();
(bool callToInstructionExecutorSucceeded, bytes memory data) = getWormholeRelayerCallerAddress().call{
value: internalInstruction.receiverValueTarget
}(abi.encodeCall(IForwardWrapper.executeInstruction, (internalInstruction, deliveryData, vaaInfo.encodedVMs)));
uint256 postGas = gasleft();
uint256 transactionFeeRefundAmount;
bool callToTargetContractSucceeded = true;
if (callToInstructionExecutorSucceeded) {
(callToTargetContractSucceeded, transactionFeeRefundAmount) = abi.decode(data, (bool, uint256));
} else {
// Calculate the amount of gas used in the call (upperbounding at the gas limit, which shouldn't have been exceeded)
uint256 gasUsed = (preGas - postGas) > internalInstruction.executionParameters.gasLimit
? internalInstruction.executionParameters.gasLimit
: (preGas - postGas);
// Calculate the amount of maxTransactionFee to refund (multiply the maximum refund by the fraction of gas unused)
transactionFeeRefundAmount = (internalInstruction.executionParameters.gasLimit - gasUsed)
* internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
}
// Retrieve the forward instruction created during execution of 'receiveWormholeMessages'
IWormholeRelayerInternalStructs.ForwardInstruction memory forwardInstruction = getForwardInstruction();
//clear forwarding request from storage
clearForwardInstruction();
// unlock the contract
setContractLock(false);
DeliveryStatus status;
if (forwardInstruction.isValid) {
// If the user made a forward/multichainForward request, then try to execute it
emitForward(transactionFeeRefundAmount, forwardInstruction);
status = DeliveryStatus.FORWARD_REQUEST_SUCCESS;
} else {
status = callToTargetContractSucceeded
? (callToInstructionExecutorSucceeded ? DeliveryStatus.SUCCESS : DeliveryStatus.FORWARD_REQUEST_FAILURE)
: DeliveryStatus.RECEIVER_FAILURE;
}
// Emit a status update that can be read by a SDK
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: vaaInfo.sourceChain,
sequence: vaaInfo.sourceSequence,
deliveryVaaHash: vaaInfo.deliveryVaaHash,
status: status
});
if (internalInstruction.targetAddress == 0x0) {
payRefunds(
internalInstruction,
vaaInfo.relayerRefundAddress,
transactionFeeRefundAmount,
callToInstructionExecutorSucceeded && callToTargetContractSucceeded,
forwardInstruction.isValid
internalInstruction, vaaInfo.relayerRefundAddress, internalInstruction.maximumRefundTarget, false, false
);
return;
}
if (isContractLocked()) {
revert IDelivery.ReentrantCall();
}
setContractLock(true);
setLockedTargetAddress(fromWormholeFormat(internalInstruction.targetAddress));
IWormholeReceiver.DeliveryData memory deliveryData;
deliveryData.sourceAddress = deliveryContainer.senderAddress;
deliveryData.sourceChain = vaaInfo.sourceChain;
deliveryData.maximumRefund = internalInstruction.maximumRefundTarget;
deliveryData.deliveryHash = vaaInfo.deliveryVaaHash;
deliveryData.payload = internalInstruction.payload;
uint256 preGas = gasleft();
(bool callToInstructionExecutorSucceeded, bytes memory data) = getWormholeRelayerCallerAddress().call{
value: internalInstruction.receiverValueTarget
}(abi.encodeCall(IForwardWrapper.executeInstruction, (internalInstruction, deliveryData, vaaInfo.encodedVMs)));
uint256 postGas = gasleft();
uint256 transactionFeeRefundAmount;
bool callToTargetContractSucceeded = true;
if (callToInstructionExecutorSucceeded) {
(callToTargetContractSucceeded, transactionFeeRefundAmount) = abi.decode(data, (bool, uint256));
} else {
// Calculate the amount of gas used in the call (upperbounding at the gas limit, which shouldn't have been exceeded)
uint256 gasUsed = (preGas - postGas) > internalInstruction.executionParameters.gasLimit
? internalInstruction.executionParameters.gasLimit
: (preGas - postGas);
// Calculate the amount of maxTransactionFee to refund (multiply the maximum refund by the fraction of gas unused)
transactionFeeRefundAmount = (internalInstruction.executionParameters.gasLimit - gasUsed)
* internalInstruction.maximumRefundTarget / internalInstruction.executionParameters.gasLimit;
}
// Retrieve the forward instruction created during execution of 'receiveWormholeMessages'
IWormholeRelayerInternalStructs.ForwardInstruction memory forwardInstruction = getForwardInstruction();
//clear forwarding request from storage
clearForwardInstruction();
// unlock the contract
setContractLock(false);
DeliveryStatus status;
if (forwardInstruction.isValid) {
// If the user made a forward/multichainForward request, then try to execute it
emitForward(transactionFeeRefundAmount, forwardInstruction);
status = DeliveryStatus.FORWARD_REQUEST_SUCCESS;
} else {
status = callToTargetContractSucceeded
? (callToInstructionExecutorSucceeded ? DeliveryStatus.SUCCESS : DeliveryStatus.FORWARD_REQUEST_FAILURE)
: DeliveryStatus.RECEIVER_FAILURE;
}
// Emit a status update that can be read by a SDK
emit Delivery({
recipientContract: fromWormholeFormat(internalInstruction.targetAddress),
sourceChain: vaaInfo.sourceChain,
sequence: vaaInfo.sourceSequence,
deliveryVaaHash: vaaInfo.deliveryVaaHash,
status: status
});
payRefunds(
internalInstruction,
vaaInfo.relayerRefundAddress,
transactionFeeRefundAmount,
callToInstructionExecutorSucceeded && callToTargetContractSucceeded,
forwardInstruction.isValid
);
}
function payRefunds(
@ -249,7 +253,7 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
multichainSendContainer(
IWormholeRelayer.Send({
targetChain: targetChain,
targetAddress: targetAddress,
targetAddress: bytes32(0x0),
refundAddress: targetAddress,
refundChain: targetChain,
maxTransactionFee: 0,
@ -339,8 +343,8 @@ contract CoreRelayerDelivery is CoreRelayerGovernance {
sourceChain: deliveryVM.emitterChainId,
sourceSequence: deliveryVM.sequence,
deliveryVaaHash: deliveryVM.hash,
relayerRefundAddress:targetParams.relayerRefundAddress,
encodedVMs:targetParams.encodedVMs
relayerRefundAddress: targetParams.relayerRefundAddress,
encodedVMs: targetParams.encodedVMs
})
);
}

View File

@ -357,7 +357,7 @@ contract CoreRelayerMessages is CoreRelayerGetters {
function decodeDeliveryInstruction(bytes memory encoded, uint256 index)
public
pure
returns (IWormholeRelayerInternalStructs.DeliveryInstruction memory instruction, uint256 newIndex )
returns (IWormholeRelayerInternalStructs.DeliveryInstruction memory instruction, uint256 newIndex)
{
// target chain of the delivery instruction
instruction.targetChain = encoded.toUint16(index);
@ -389,7 +389,7 @@ contract CoreRelayerMessages is CoreRelayerGetters {
index += 4;
instruction.payload = encoded.slice(index, payloadLength);
index+= payloadLength;
index += payloadLength;
newIndex = index;
}

View File

@ -48,7 +48,11 @@ contract XmintHub is ERC20, IWormholeReceiver {
}
//This is the function which receives all messages from the remote contracts.
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas) public payable override {
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas)
public
payable
override
{
//The first message should be from the token bridge, so attempt to redeem it.
ITokenBridge.TransferWithPayload memory transferResult =
token_bridge.parseTransferWithPayload(token_bridge.completeTransferWithPayload(vaas[0]));

View File

@ -66,7 +66,11 @@ contract XmintSpoke is IWormholeReceiver {
}
//This function receives messages back from the Hub contract and distributes the tokens to the user.
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas) public payable override {
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas)
public
payable
override
{
//Complete the token bridge transfer
ITokenBridge.TransferWithPayload memory transferResult =
token_bridge.parseTransferWithPayload(token_bridge.completeTransferWithPayload(vaas[0]));

View File

@ -12,5 +12,4 @@ interface IWormholeReceiver {
}
function receiveWormholeMessages(DeliveryData memory deliveryInfo, bytes[] memory signedVaas) external payable;
}

View File

@ -20,7 +20,7 @@ interface IWormholeRelayer {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -55,7 +55,7 @@ interface IWormholeRelayer {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -94,7 +94,7 @@ interface IWormholeRelayer {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'
@ -218,7 +218,7 @@ interface IWormholeRelayer {
* @param targetAddress The address (in Wormhole 32-byte format) on chain 'targetChain' of the contract to which the vaas are delivered.
* This contract must implement the IWormholeReceiver interface, which simply requires a 'receiveWormholeMessage(bytes[] memory vaas, bytes[] memory additionalData)' endpoint
* @param refundAddress The address (in Wormhole 32-byte format) on chain 'targetChain' to which any leftover funds (that weren't used for target chain gas or passed into targetAddress as value) should be sent
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param refundChain The chain where the refund should be sent. If the refundchain is not the targetchain, a new empty delivery will be initiated in order to perform the refund, which is subject to the provider's rates on the target chain.
* @param maxTransactionFee The maximum amount (denominated in source chain (this chain) currency) that you wish to spend on funding gas for the target chain.
* If more gas is needed on the target chain than is paid for, there will be a Receiver Failure.
* Any unused value out of this fee will be refunded to 'refundAddress'

View File

@ -20,7 +20,7 @@ contract AttackForwardIntegration is IWormholeReceiver {
// Capture 30k gas for fees
// This just needs to be enough to pay for the call to the destination address.
uint32 SAFE_DELIVERY_GAS_CAPTURE = 50000;
uint32 SAFE_DELIVERY_GAS_CAPTURE = 30000;
constructor(IWormhole initWormhole, IWormholeRelayer initCoreRelayer, uint16 chainId, address initAttackerReward) {
attackerReward = initAttackerReward;
@ -30,7 +30,11 @@ contract AttackForwardIntegration is IWormholeReceiver {
}
// This is the function which receives all messages from the remote contracts.
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas) public payable override {
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas)
public
payable
override
{
// Do nothing. The attacker doesn't care about this message; he sends it himself.
}

View File

@ -56,7 +56,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
payable
returns (uint64 sequence)
{
sequence = sendMessageGeneral(_message, targetChainId, destination, destination, 0);
sequence = sendMessageGeneral(_message, targetChainId, destination, targetChainId, destination, 0);
}
function sendMessageWithRefundAddress(
@ -65,7 +65,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
address destination,
address refundAddress
) public payable returns (uint64 sequence) {
sequence = sendMessageGeneral(_message, targetChainId, destination, refundAddress, 0);
sequence = sendMessageGeneral(_message, targetChainId, destination, targetChainId, refundAddress, 0);
}
function messageInfosCreator(uint64 sequence1, uint64 sequence2)
@ -105,20 +105,28 @@ contract MockRelayerIntegration is IWormholeReceiver {
uint64 sequence0 = wormhole.publishMessage{value: wormhole.messageFee()}(0, _message, 200);
uint64 sequence1 =
wormhole.publishMessage{value: wormhole.messageFee()}(0, encodeFurtherInstructions(instructions), 200);
sequence = executeSend(targetChainId, destination, refundAddress, 0, messageInfosCreator(sequence0, sequence1));
sequence = executeSend(
targetChainId, destination, targetChainId, refundAddress, 0, messageInfosCreator(sequence0, sequence1)
);
}
function sendMessageGeneral(
bytes memory fullMessage,
uint16 targetChainId,
address destination,
uint16 refundChain,
address refundAddress,
uint256 receiverValue
) public payable returns (uint64 sequence) {
uint64 sequence0 = wormhole.publishMessage{value: wormhole.messageFee()}(0, fullMessage, 200);
uint64 sequence1 = wormhole.publishMessage{value: wormhole.messageFee()}(0, abi.encodePacked(uint8(0)), 200);
sequence = executeSend(
targetChainId, destination, refundAddress, receiverValue, messageInfosCreator(sequence0, sequence1)
targetChainId,
destination,
refundChain,
refundAddress,
receiverValue,
messageInfosCreator(sequence0, sequence1)
);
}
@ -172,6 +180,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
function executeSend(
uint16 targetChainId,
address destination,
uint16 refundChainId,
address refundAddress,
uint256 receiverValue,
IWormholeRelayer.MessageInfo[] memory messageInfos
@ -181,7 +190,7 @@ contract MockRelayerIntegration is IWormholeReceiver {
IWormholeRelayer.Send memory request = IWormholeRelayer.Send({
targetChain: targetChainId,
targetAddress: relayer.toWormholeFormat(address(destination)),
refundChain: targetChainId,
refundChain: refundChainId,
refundAddress: relayer.toWormholeFormat(address(refundAddress)), // This will be ignored on the target chain if the intent is to perform a forward
maxTransactionFee: msg.value - 3 * wormhole.messageFee() - receiverValue,
receiverValue: receiverValue,
@ -194,7 +203,10 @@ contract MockRelayerIntegration is IWormholeReceiver {
);
}
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory wormholeObservations) public payable override {
function receiveWormholeMessages(
IWormholeReceiver.DeliveryData memory deliveryData,
bytes[] memory wormholeObservations
) public payable override {
// loop through the array of wormhole observations from the batch and store each payload
uint256 numObservations = wormholeObservations.length;
bytes[] memory messages = new bytes[](numObservations - 1);

View File

@ -38,7 +38,11 @@ contract ForwardTester is IWormholeReceiver {
WorksCorrectly
}
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas) public payable override {
function receiveWormholeMessages(IWormholeReceiver.DeliveryData memory deliveryData, bytes[] memory vaas)
public
payable
override
{
(IWormhole.VM memory vaa, bool valid, string memory reason) = wormhole.parseAndVerifyVM(vaas[0]);
require(valid, reason);
@ -50,19 +54,35 @@ contract ForwardTester is IWormholeReceiver {
if (action == Action.MultipleForwardsRequested) {
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
wormholeRelayer.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, vaa.emitterChainId, maxTransactionFee, 0, bytes(""), empty
vaa.emitterChainId,
vaa.emitterAddress,
vaa.emitterAddress,
vaa.emitterChainId,
maxTransactionFee,
0,
bytes(""),
empty
);
wormholeRelayer.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, vaa.emitterChainId, maxTransactionFee, 0, bytes(""), empty
vaa.emitterChainId,
vaa.emitterAddress,
vaa.emitterAddress,
vaa.emitterChainId,
maxTransactionFee,
0,
bytes(""),
empty
);
} else if (action == Action.ForwardRequestFromWrongAddress) {
// Emitter must be a wormhole relayer
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
DummyContract dc = new DummyContract(address(wormholeRelayer));
dc.forward(vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, bytes(""), empty);
dc.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, maxTransactionFee, 0, bytes(""), empty
);
} else if (action == Action.MultichainSendEmpty) {
wormholeRelayer.multichainForward(
IWormholeRelayer.MultichainSend(
@ -73,28 +93,56 @@ contract ForwardTester is IWormholeReceiver {
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(vaa.emitterChainId, 1, wormholeRelayer.getDefaultRelayProvider()) - 1;
wormholeRelayer.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, vaa.emitterChainId, maxTransactionFee, 0, bytes(""), empty
vaa.emitterChainId,
vaa.emitterAddress,
vaa.emitterAddress,
vaa.emitterChainId,
maxTransactionFee,
0,
bytes(""),
empty
);
} else if (action == Action.FundsTooMuch) {
// set maximum budget to less than this
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
wormholeRelayer.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, vaa.emitterChainId, maxTransactionFee * 105 / 100 + 1, 0, bytes(""), empty
vaa.emitterChainId,
vaa.emitterAddress,
vaa.emitterAddress,
vaa.emitterChainId,
maxTransactionFee * 105 / 100 + 1,
0,
bytes(""),
empty
);
} else if (action == Action.ReentrantCall) {
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(wormhole.chainId(), 10000, wormholeRelayer.getDefaultRelayProvider());
vm.recordLogs();
wormholeRelayer.send{value: maxTransactionFee + wormhole.messageFee()}(
wormhole.chainId(), vaa.emitterAddress, vaa.emitterAddress, wormhole.chainId(), maxTransactionFee, 0, bytes(""), empty
wormhole.chainId(),
vaa.emitterAddress,
vaa.emitterAddress,
wormhole.chainId(),
maxTransactionFee,
0,
bytes(""),
empty
);
genericRelayer.relay(wormhole.chainId());
} else {
uint256 maxTransactionFee =
wormholeRelayer.quoteGas(vaa.emitterChainId, 10000, wormholeRelayer.getDefaultRelayProvider());
wormholeRelayer.forward(
vaa.emitterChainId, vaa.emitterAddress, vaa.emitterAddress, vaa.emitterChainId, maxTransactionFee, 0, bytes(""), empty
vaa.emitterChainId,
vaa.emitterAddress,
vaa.emitterAddress,
vaa.emitterChainId,
maxTransactionFee,
0,
bytes(""),
empty
);
}
}
@ -118,6 +166,8 @@ contract DummyContract {
bytes memory payload,
IWormholeRelayer.MessageInfo[] memory messages
) public {
wormholeRelayer.forward(chainId, targetAddress, refundAddress, chainId, maxTransactionFee, receiverValue, bytes(""),messages);
wormholeRelayer.forward(
chainId, targetAddress, refundAddress, chainId, maxTransactionFee, receiverValue, bytes(""), messages
);
}
}

View File

@ -299,6 +299,7 @@ contract WormholeRelayerTests is Test {
message,
setup.targetChainId,
address(setup.target.integration),
setup.targetChainId,
address(setup.target.refundAddress),
receiverValueSource
);
@ -328,6 +329,59 @@ contract WormholeRelayerTests is Test {
);
}
function testFundsCorrectForASendCrossChainRefund(
GasParameters memory gasParams,
FeeParameters memory feeParams,
bytes memory message
) public {
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
vm.recordLogs();
uint256 refundAddressBalance = setup.source.refundAddress.balance;
uint256 relayerBalance = setup.target.relayer.balance;
uint256 rewardAddressBalance = setup.source.rewardAddress.balance;
uint256 refundRewardAddressBalance = setup.target.rewardAddress.balance;
uint256 refundRelayerBalance = setup.source.relayer.balance;
uint256 payment = setup.source.coreRelayer.quoteGas(
setup.targetChainId, gasParams.targetGasLimit, address(setup.source.relayProvider)
) + uint256(3) * setup.source.wormhole.messageFee();
setup.source.integration.sendMessageGeneral{value: payment}(
message,
setup.targetChainId,
address(setup.target.integration),
setup.sourceChainId,
address(setup.source.refundAddress),
0
);
genericRelayer.relay(setup.sourceChainId);
genericRelayer.relay(setup.targetChainId);
assertTrue(keccak256(setup.target.integration.getMessage()) == keccak256(message));
uint256 USDcost = (uint256(payment) - uint256(3) * map[setup.sourceChainId].wormhole.messageFee())
* feeParams.sourceNativePrice
- (setup.source.refundAddress.balance - refundAddressBalance) * feeParams.sourceNativePrice;
uint256 relayerProfit = uint256(feeParams.sourceNativePrice)
* (setup.source.rewardAddress.balance - rewardAddressBalance)
- feeParams.targetNativePrice * (relayerBalance - setup.target.relayer.balance);
uint256 refundRelayerProfit = uint256(feeParams.targetNativePrice)
* (setup.target.rewardAddress.balance - refundRewardAddressBalance)
- feeParams.sourceNativePrice * (refundRelayerBalance - setup.source.relayer.balance);
assertTrue(setup.source.rewardAddress.balance > rewardAddressBalance, "The cross chain refund went through");
assertTrue(USDcost - (relayerProfit + refundRelayerProfit) >= 0, "We paid enough");
console.log(USDcost);
console.log((relayerProfit + refundRelayerProfit));
assertTrue(
USDcost - (relayerProfit + refundRelayerProfit) < feeParams.sourceNativePrice,
"We paid the least amount necessary"
);
}
function testFundsCorrectForASendIfReceiveWormholeMessagesReverts(
GasParameters memory gasParams,
FeeParameters memory feeParams,
@ -352,6 +406,7 @@ contract WormholeRelayerTests is Test {
message,
setup.targetChainId,
address(setup.target.integration),
setup.targetChainId,
address(setup.target.refundAddress),
receiverValueSource
);
@ -376,10 +431,9 @@ contract WormholeRelayerTests is Test {
GasParameters memory gasParams,
FeeParameters memory feeParams
) internal returns (uint256) {
vm.assume(
uint256(1) * gasParams.targetGasPrice * feeParams.targetNativePrice
> uint256(1) * gasParams.sourceGasPrice * feeParams.sourceNativePrice
);
vm.assume(uint256(1) * gasParams.targetGasPrice > uint256(1) * gasParams.sourceGasPrice);
vm.assume(uint256(1) * feeParams.targetNativePrice > uint256(1) * feeParams.sourceNativePrice);
vm.assume(
setup.source.coreRelayer.quoteGas(setup.targetChainId, gasFirst, address(setup.source.relayProvider))
@ -401,13 +455,13 @@ contract WormholeRelayerTests is Test {
vm.assume((payment + payment2) < (uint256(2) ** 222));
return payment + payment2;
return (payment + payment2 * 105 / 100 + 1);
}
function testForward(GasParameters memory gasParams, FeeParameters memory feeParams, bytes memory message) public {
StandardSetupTwoChains memory setup = standardAssumeAndSetupTwoChains(gasParams, feeParams, 1000000);
uint256 payment = assumeAndGetForwardPayment(gasParams.targetGasLimit, 700000, setup, gasParams, feeParams);
uint256 payment = assumeAndGetForwardPayment(gasParams.targetGasLimit, 500000, setup, gasParams, feeParams);
vm.recordLogs();
@ -641,7 +695,12 @@ contract WormholeRelayerTests is Test {
vm.deal(address(this), payment + newReceiverValueSource);
setup.source.integration.sendMessageGeneral{value: payment + newReceiverValueSource}(
message, setup.targetChainId, address(setup.target.integration), address(0x0), newReceiverValueSource
message,
setup.targetChainId,
address(setup.target.integration),
setup.targetChainId,
address(0x0),
newReceiverValueSource
);
genericRelayer.relay(setup.sourceChainId);
@ -674,7 +733,12 @@ contract WormholeRelayerTests is Test {
vm.deal(address(this), payment + newReceiverValueSource - 1);
setup.source.integration.sendMessageGeneral{value: payment + newReceiverValueSource - 1}(
message, setup.targetChainId, address(setup.target.integration), address(0x0), newReceiverValueSource - 1
message,
setup.targetChainId,
address(setup.target.integration),
setup.targetChainId,
address(0x0),
newReceiverValueSource - 1
);
genericRelayer.relay(setup.sourceChainId);
@ -1056,7 +1120,7 @@ contract WormholeRelayerTests is Test {
new ForwardTester(address(setup.target.wormhole), address(setup.target.coreRelayer), address(setup.target.wormholeSimulator));
vm.deal(address(forwardTester), type(uint256).max / 2);
stack.targetAddress = setup.source.coreRelayer.toWormholeFormat(address(forwardTester));
stack.payment = assumeAndGetForwardPayment(gasParams.targetGasLimit, 700000, setup, gasParams, feeParams);
stack.payment = assumeAndGetForwardPayment(gasParams.targetGasLimit, 500000, setup, gasParams, feeParams);
stack.wormholeFee = setup.source.wormhole.messageFee();
uint64 sequence =
setup.source.wormhole.publishMessage{value: stack.wormholeFee}(1, abi.encodePacked(uint8(test)), 200);
@ -1091,7 +1155,9 @@ contract WormholeRelayerTests is Test {
IWormholeRelayer.MessageInfo[] memory msgInfoArray = messageInfoArray(0, address(this));
vm.expectRevert(abi.encodeWithSignature("NoDeliveryInProgress()"));
setup.source.coreRelayer.forward(setup.targetChainId, targetAddress, targetAddress, setup.targetChainId, 0, 0, bytes(""), msgInfoArray);
setup.source.coreRelayer.forward(
setup.targetChainId, targetAddress, targetAddress, setup.targetChainId, 0, 0, bytes(""), msgInfoArray
);
}
function testRevertForwardMultipleForwardsRequested(GasParameters memory gasParams, FeeParameters memory feeParams)