Compare commits
4 Commits
3886b7fde8
...
44f35d1dfb
Author | SHA1 | Date |
---|---|---|
derpy-duck | 44f35d1dfb | |
derpy-duck | b6395092a8 | |
derpy-duck | 4df8b4d838 | |
derpy-duck | 65ecddf2f4 |
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]));
|
||||
|
|
|
@ -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]));
|
||||
|
|
|
@ -12,5 +12,4 @@ interface IWormholeReceiver {
|
|||
}
|
||||
|
||||
function receiveWormholeMessages(DeliveryData memory deliveryInfo, bytes[] memory signedVaas) external payable;
|
||||
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue