Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
607200e
using failedMessage.refundSendParam in refund
shankars99 Jul 3, 2025
f4f5703
using transfer on hub for retry and refund and slippage check on retr…
shankars99 Jul 3, 2025
281ef47
OFT.send(..,_refundAddress) comment on tx.origin
shankars99 Jul 3, 2025
d8c7279
using SafeTransfer
shankars99 Jul 3, 2025
431a008
rename: CanRetryWithSwap to CanRetryWithSwapOrRefund
shankars99 Jul 3, 2025
e4076b1
gas: optimized _failedGuidState() avoids reading from storage twice
shankars99 Jul 3, 2025
fae8a36
using OZ's convertToAsset and convertToShare
shankars99 Jul 3, 2025
69c03f0
chore: require OFTs and Vault to have the same tokens
shankars99 Jul 3, 2025
cea5b1d
fix comment #14
shankars99 Jul 3, 2025
4cc35eb
comments: make clear about slippage #11
shankars99 Jul 3, 2025
adcfe55
remove inaccessible functions from interface #9
shankars99 Jul 3, 2025
8dac577
improved comments on retry #7
shankars99 Jul 3, 2025
b7c7d54
adding nonReentrant on retryWithSwap #5
shankars99 Jul 3, 2025
78e4aad
remove unused imports #1
shankars99 Jul 3, 2025
ff16730
gas: do not store refundSendParam on retry #17
shankars99 Jul 3, 2025
e5c5193
update to match package changes
shankars99 Jul 3, 2025
2305ecd
fix: refund
shankars99 Jul 4, 2025
2ef95ce
performing refund testing
shankars99 Jul 4, 2025
a93eb55
event emission on retry, refund, retryWithSwap
shankars99 Jul 4, 2025
f109d4a
update to match packages
shankars99 Jul 4, 2025
7c70065
enforcedOptions in retry, refund, and retryWithSwap #19 + cleanup and…
shankars99 Jul 4, 2025
d6141e5
update to match package update
shankars99 Jul 4, 2025
3d1e8e5
retry and refund are atomic - revert when failedMessageTransfer reverts
shankars99 Jul 5, 2025
3983a95
accumulate msg.value supplied in retryWithSwap when sendFailedMessage…
shankars99 Jul 5, 2025
4ba8b55
setting prepaidMsgValue to 0 after sending the stored msgValue to REF…
shankars99 Jul 5, 2025
dc38170
using _removeDust in _checkSlippage() to be equivalent to OFTCore::_d…
shankars99 Jul 5, 2025
7c39294
decouple _checkSlippage() and make it virtual along with _send()
shankars99 Jul 5, 2025
55d7b26
remove unnecessary try...catch in retry() and refund()
shankars99 Jul 5, 2025
12bdda0
set SendParam.amountLD = vaultAmount globally in retryWitSwap for retry
shankars99 Jul 6, 2025
2655e7e
rename arg _oft to match lzCompose definition of _oft and refundOFT
shankars99 Jul 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 59 additions & 28 deletions packages/ovault-evm/contracts/OVaultComposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,7 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {

/// @dev If the destination is the HUB chain, we just transfer the tokens to the receiver
if (_sendParam.dstEid == HUB_EID) {
address _receiver = _sendParam.to.bytes32ToAddress();
uint256 _amountLD = _sendParam.amountLD;
IERC20 token = IERC20(IOFT(_oft).token());
token.transfer(_receiver, _amountLD);
if (msg.value > 0) {
(bool sent, ) = _receiver.call{ value: msg.value }("");
require(sent, "Failed to send Ether");
}
emit SentOnHub(_receiver, _oft, _amountLD);
_executeHubTransfer(_oft, _sendParam.to.bytes32ToAddress(), _sendParam.amountLD);
return;
}

Expand All @@ -159,53 +151,77 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {
/// @dev Permissionless function to send back the message to the source chain
/// @dev Always possible unless the lzCompose() fails due to an Out-Of-Gas panic
function refund(bytes32 _guid, bytes calldata _extraOptions) external payable nonReentrant {
if (failedGuidState(_guid) != FailedState.CanOnlyRefund) revert CanNotRefund(_guid);

Comment thread
shankars99 marked this conversation as resolved.
Outdated
FailedMessage memory failedMessage = failedMessages[_guid];
delete failedMessages[_guid];

SendParam memory refundSendParam = failedMessage.refundSendParam;
if (failedGuidState(_guid) != FailedState.CanOnlyRefund) revert CanNotRefund(_guid);
address refundOft = failedMessage.oft;
Comment thread
shankars99 marked this conversation as resolved.
Outdated

/// @dev If the destination is the HUB chain, we just transfer the tokens to the receiver
if (refundSendParam.dstEid == HUB_EID) {
_executeHubTransfer(refundOft, refundSendParam.to.bytes32ToAddress(), refundSendParam.amountLD);
return;
}

refundSendParam.extraOptions = _extraOptions;

delete failedMessages[_guid];
_send(failedMessage.refundOFT, refundSendParam);
emit Refunded(_guid, failedMessage.refundOFT);
_send(refundOft, refundSendParam);
emit Refunded(_guid, refundOft);
}

/// @dev Permissionless function to retry the message with more gas
/// @dev Failure case when there is a LayerZero config issue - ex: dvn config
function retry(bytes32 _guid, bytes calldata _extraOptions) external payable nonReentrant {
FailedMessage memory failedMessage = failedMessages[_guid];
if (failedGuidState(_guid) != FailedState.CanOnlyRetry) revert CanNotRetry(_guid);

FailedMessage memory failedMessage = failedMessages[_guid];
delete failedMessages[_guid];

SendParam memory sendParam = failedMessage.sendParam;
address retryOFT = failedMessage.oft;

/// @dev If the destination is the HUB chain, we just transfer the tokens to the receiver
if (sendParam.dstEid == HUB_EID) {
_executeHubTransfer(retryOFT, sendParam.to.bytes32ToAddress(), sendParam.amountLD);
return;
}

sendParam.extraOptions = _extraOptions;

delete failedMessages[_guid];
_send(failedMessage.oft, sendParam);
emit Retried(_guid, failedMessage.oft);
_send(retryOFT, sendParam);
emit Retried(_guid, retryOFT);
}

/// @dev Retry mechanism for transactions that failed due to slippage. This can revert.
/// @dev Failure case when there is a LayerZero config issue - ex: dvn config
function retryWithSwap(bytes32 _guid, bytes calldata _extraOptions) external payable {
FailedMessage memory failedMessage = failedMessages[_guid];
if (failedGuidState(_guid) != FailedState.CanRetryWithSwap) revert CanNotRetry(_guid);

FailedMessage memory failedMessage = failedMessages[_guid];
uint256 srcAmount = failedMessage.refundSendParam.amountLD;
delete failedMessages[_guid];

SendParam memory sendParam = failedMessage.sendParam;
sendParam.extraOptions = _extraOptions;
address retryOFT = failedMessage.oft;

uint256 amountLd = failedMessage.refundSendParam.amountLD;
sendParam.amountLD = this.executeOVaultActionWithSlippageCheck(
failedMessage.refundOFT,
srcAmount,
sendParam.minAmountLD
);

delete failedMessages[_guid];
sendParam.amountLD = _executeOVaultAction(failedMessage.refundOFT, amountLd);
/// @dev If the destination is the HUB chain, we just transfer the tokens to the receiver
if (sendParam.dstEid == HUB_EID) {
_executeHubTransfer(retryOFT, sendParam.to.bytes32ToAddress(), sendParam.amountLD);
return;
}

_send(failedMessage.oft, sendParam);
emit Sent(_guid, failedMessage.oft);
}
sendParam.extraOptions = _extraOptions;

/// @dev Internal function to send the message to the target OFT
function _send(address _oft, SendParam memory _sendParam) internal {
IOFT(_oft).send{ value: msg.value }(_sendParam, MessagingFee(msg.value, 0), tx.origin);
_send(retryOFT, sendParam);
emit Sent(_guid, retryOFT);
}

function _executeOVaultAction(address _oft, uint256 _amount) internal returns (uint256 vaultAmount) {
Expand All @@ -216,6 +232,21 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {
}
}

/// @dev Internal function to send the message to the target OFT
function _send(address _oft, SendParam memory _sendParam) internal {
IOFT(_oft).send{ value: msg.value }(_sendParam, MessagingFee(msg.value, 0), tx.origin);
}

function _executeHubTransfer(address _oft, address _receiver, uint256 _amountLD) internal {
IERC20 token = IERC20(IOFT(_oft).token());
token.transfer(_receiver, _amountLD);
if (msg.value > 0) {
(bool sent, ) = _receiver.call{ value: msg.value }("");
require(sent, "Failed to send Ether");
}
emit SentOnHub(_receiver, _oft, _amountLD);
Comment thread
shankars99 marked this conversation as resolved.
Outdated
}

/// @dev Helper to check if the target OFT does not have a peer set for the destination chain OR if our target chain is the not the same as the HUB chain
function _isInvalidPeer(address _oft, uint32 _dstEid) internal view returns (bool) {
return _dstEid != HUB_EID && IOAppCore(_oft).peers(_dstEid) == bytes32(0);
Expand Down