Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions packages/ovault-composer/contracts/OVaultComposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s

import { IOFT, SendParam, MessagingFee } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
import { IOAppCore } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol";
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";

import { IOVaultComposer, FailedMessage, FailedState } from "./interfaces/IOVaultComposer.sol";
Expand All @@ -22,6 +23,7 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {
address public immutable SHARE_OFT;
address public immutable OVAULT;
address public immutable ENDPOINT;
uint32 public immutable COMPOSER_EID;

mapping(bytes32 guid => FailedMessage) public failedMessages;

Expand All @@ -36,6 +38,7 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {
SHARE_OFT = IOVault(_ovault).SHARE_OFT();
ASSET_OFT = IOVault(_ovault).ASSET_OFT();
ENDPOINT = address(IOAppCore(ASSET_OFT).endpoint());
COMPOSER_EID = ILayerZeroEndpointV2(ENDPOINT).eid();

// Approve the adapter to spend the share tokens held by this contract
IERC20(share).approve(OVAULT, type(uint256).max);
Expand Down Expand Up @@ -129,6 +132,10 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {

/// @dev Dirty swapping amountLD and minAmountLD to 1e18 and 0 to avoid Slippage issue on the target OFT quoteSend()
function validateTargetOFTConfig(address _oft, SendParam memory _sendParam) external view {
if (COMPOSER_EID == _sendParam.dstEid) {
return;
}

_sendParam.amountLD = 1e18;
_sendParam.minAmountLD = 0;

Expand All @@ -138,6 +145,18 @@ contract OVaultComposer is IOVaultComposer, ReentrancyGuard {
/// @dev External call for try...catch logic in lzCompose()
function send(address _oft, SendParam calldata _sendParam) external payable nonReentrant {
if (msg.sender != address(this)) revert OnlySelf(msg.sender);
if (_sendParam.dstEid == COMPOSER_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);
return;
}
_send(_oft, _sendParam);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface IOVaultComposer is IOAppComposer {
/// ========================== EVENTS =====================================
event DecodeFailed(bytes32 indexed guid, address indexed oft, bytes message);
event Sent(bytes32 indexed guid, address indexed oft);
event SentOnHub(address indexed receiver, address indexed oft, uint256 amountLD);
event SendFailed(bytes32 indexed guid, address indexed oft);
event Refunded(bytes32 indexed guid, address indexed oft);
event Retried(bytes32 indexed guid, address indexed oft);
Expand Down
45 changes: 45 additions & 0 deletions packages/ovault-composer/test/composer/OVaultComposer_Unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,51 @@ contract OVaultComposerUnitTest is OVaultComposerBaseTest {
assertEq(shareOFT_arb.totalSupply(), 0);
}

function test_lzCompose_pass_on_hub() public {
bytes32 guid = _randomGUID();
assetOFT_arb.mint(address(OVaultComposerArb), TOKENS_TO_SEND);

SendParam memory internalSendParam = SendParam(
OVaultComposerArb.COMPOSER_EID(),
addressToBytes32(userA),
TOKENS_TO_SEND,
0,
OPTIONS_LZRECEIVE_2M,
"",
""
);

bytes memory composeMsg = _createComposePayload(ETH_EID, internalSendParam, TOKENS_TO_SEND, userA);

vm.expectEmit(true, true, true, true, address(assetOFT_arb));
emit IERC20.Transfer(address(OVaultComposerArb), address(oVault_arb), TOKENS_TO_SEND);

vm.expectEmit(true, true, true, true, address(shareOFT_arb));
emit IERC20.Transfer(address(0), address(OVaultComposerArb), TOKENS_TO_SEND);

vm.expectEmit(true, true, true, true, address(oVault_arb));
emit IERC4626Adapter.Deposit(
address(OVaultComposerArb),
address(OVaultComposerArb),
TOKENS_TO_SEND,
TOKENS_TO_SEND
);

vm.expectEmit(true, true, true, true, address(OVaultComposerArb));
emit IOVaultComposer.SentOnHub(userA, address(shareOFT_arb), TOKENS_TO_SEND);

assertEq(assetOFT_arb.totalSupply(), assetOFT_arb.balanceOf(address(OVaultComposerArb)), TOKENS_TO_SEND);
assertEq(shareOFT_arb.totalSupply(), 0);

vm.prank(arbEndpoint);
OVaultComposerArb.lzCompose{ value: 1 ether }(address(assetOFT_arb), guid, composeMsg, arbExecutor, "");

assertEq(uint256(OVaultComposerArb.failedGuidState(guid)), uint256(FailedState.NotFound));

assertEq(assetOFT_arb.totalSupply(), assetOFT_arb.balanceOf(address(oVault_arb)), TOKENS_TO_SEND);
assertEq(shareOFT_arb.totalSupply(), shareOFT_arb.balanceOf(address(userA)), TOKENS_TO_SEND);
}

function test_lzCompose_fail_invalid_payload() public {
bytes32 guid = _randomGUID();
assetOFT_arb.mint(address(OVaultComposerArb), TOKENS_TO_SEND);
Expand Down
Loading