Skip to content

Commit

Permalink
feat: swapAndSend()
Browse files Browse the repository at this point in the history
  • Loading branch information
pegahcarter committed Oct 28, 2024
1 parent 2e5d7a1 commit 23dc21e
Showing 1 changed file with 132 additions and 51 deletions.
183 changes: 132 additions & 51 deletions src/contracts/MockReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { IOAppCore } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol";
import { IOAppComposer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppComposer.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { OptionsBuilder } from "@fraxfinance/layerzero-v2-upgradeable/oapp/contracts/oapp/libs/OptionsBuilder.sol";
import { SendParam, MessagingFee, IOFT } from "@fraxfinance/layerzero-v2-upgradeable/oapp/contracts/oft/interfaces/IOFT.sol";

import { FraxtalL2 } from "src/contracts/chain-constants/FraxtalL2.sol";

interface ICurve {
Expand All @@ -24,23 +28,23 @@ contract MockReceiver is IOAppComposer {

address public immutable endpoint;

address public lzFrax = 0x80Eede496655FB9047dd39d9f418d5483ED600df;
address public lzFraxCurve = 0x53f8F4e154F68C2D29a0D06BD50f82bCf1bd95dB;
address public fraxOft = 0x80Eede496655FB9047dd39d9f418d5483ED600df;
address public fraxCurve = 0x53f8F4e154F68C2D29a0D06BD50f82bCf1bd95dB;

address public lzSFrax = 0x5Bff88cA1442c2496f7E475E9e7786383Bc070c0;
address public lzSFraxCurve = 0xd2866eF5A94E741Ec8EDE5FF8e3A1f9C59c5e298;
address public sFraxOft = 0x5Bff88cA1442c2496f7E475E9e7786383Bc070c0;
address public sFraxCurve = 0xd2866eF5A94E741Ec8EDE5FF8e3A1f9C59c5e298;

address public lzFrxEth = 0x43eDD7f3831b08FE70B7555ddD373C8bF65a9050;
address public lzFrxEthCurve = 0x50842664DfBD876249D0113671d72dB168FBE4d0;
address public frxEthOft = 0x43eDD7f3831b08FE70B7555ddD373C8bF65a9050;
address public frxEthCurve = 0x50842664DfBD876249D0113671d72dB168FBE4d0;

address public lzSFrxEth = 0x3Ec3849C33291a9eF4c5dB86De593EB4A37fDe45;
address public lzSFrxEthCurve = 0xe5F61df936d50302962d5B914537Ff3cB63b3526;
address public sFrxEthOft = 0x3Ec3849C33291a9eF4c5dB86De593EB4A37fDe45;
address public sFrxEthCurve = 0xe5F61df936d50302962d5B914537Ff3cB63b3526;

address public lzFxs = 0x64445f0aecC51E94aD52d8AC56b7190e764E561a;
address public lzFxsCurve = 0xBc383485068Ffd275D7262Bef65005eE7a5A1870;
address public fxsOft = 0x64445f0aecC51E94aD52d8AC56b7190e764E561a;
address public fxsCurve = 0xBc383485068Ffd275D7262Bef65005eE7a5A1870;

address public lzFpi = 0x90581eCa9469D8D7F5D3B60f4715027aDFCf7927;
address public lzFpiCurve = 0x7FaA69f8fEbe38bBfFbAE3252DE7D1491F0c6157;
address public fpiOft = 0x90581eCa9469D8D7F5D3B60f4715027aDFCf7927;
address public fpiCurve = 0x7FaA69f8fEbe38bBfFbAE3252DE7D1491F0c6157;

/// @dev Initializes the contract.
/// @param _endpoint LayerZero Endpoint address
Expand All @@ -50,6 +54,37 @@ contract MockReceiver is IOAppComposer {

receive() external payable {}

/// @dev Using the _oApp address (as provided by the endpoint), return the respective tokens
/// @dev ie. a send of FRAX would have the _oApp address of the FRAX OFT
/// @return nToken "Native token" (pre-compiled proxy address)
/// @return curve (Address of curve.fi pool for nToken/lzToken)
function _getRespectiveTokens(address _oApp) internal view returns (
address nToken,
address curve
) {
if (_oApp == fraxOft) {
nToken = FraxtalL2.FRAX;
curve = fraxCurve;
} else if (_oApp == sFraxOft) {
nToken = FraxtalL2.SFRAX;
curve = sFraxCurve;
} else if (_oApp == frxEthOft) {
nToken = FraxtalL2.WFRXETH;
curve = frxEthCurve;
} else if (_oApp == sFrxEthOft) {
nToken = FraxtalL2.SFRXETH;
curve = sFrxEthCurve;
} else if (_oApp == fxsOft) {
nToken = FraxtalL2.FXS;
curve = fxsCurve;
} else if (_oApp == fpiOft) {
nToken = FraxtalL2.FPI;
curve = fpiCurve;
} else {
revert InvalidOApp();
}
}

/// @notice Handles incoming composed messages from LayerZero.
/// @dev Decodes the message payload to perform a token swap.
/// This method expects the encoded compose message to contain the swap amount and recipient address.
Expand All @@ -67,36 +102,7 @@ contract MockReceiver is IOAppComposer {
) external payable override {
require(msg.sender == endpoint, "!endpoint");

address nToken; // "native" token
address lzToken; // "LayerZero" token
address curve; // curve.fi pool
if (_oApp == lzFrax) {
nToken = FraxtalL2.FRAX;
lzToken = lzFrax;
curve = lzFraxCurve;
} else if (_oApp == lzSFrax) {
nToken = FraxtalL2.SFRAX;
lzToken = lzSFrax;
curve = lzSFraxCurve;
} else if (_oApp == lzFrxEth) {
nToken = FraxtalL2.WFRXETH;
lzToken = lzFrxEth;
curve = lzFrxEthCurve;
} else if (_oApp == lzSFrxEth) {
nToken = FraxtalL2.SFRXETH;
lzToken = lzSFrxEth;
curve = lzSFrxEthCurve;
} else if (_oApp == lzFxs) {
nToken = FraxtalL2.FXS;
lzToken = lzFxs;
curve = lzFxsCurve;
} else if (_oApp == lzFpi) {
nToken = FraxtalL2.FPI;
lzToken = lzFpi;
curve = lzFpiCurve;
} else {
revert InvalidOApp();
}
(address nToken, address curve) = _getRespectiveTokens(_oApp);

// Extract the composed message from the delivered message using the MsgCodec
(address recipient, uint256 amountOutMin) = abi.decode(
Expand All @@ -105,10 +111,10 @@ contract MockReceiver is IOAppComposer {
);
uint256 amount = OFTComposeMsgCodec.amountLD(_message);

IERC20(lzToken).approve(curve, amount);
try ICurve(curve).exchange({ i: int128(1), j: int128(0), _dx: amount, _min_dy: amountOutMin }) returns (
uint256 amountOut
) {
// try swap
IERC20(_oApp).approve(curve, amount);
try ICurve(curve).exchange({i: int128(1), j: int128(0), _dx: amount, _min_dy: amountOutMin }) returns (uint256 amountOut)
{
if (nToken == FraxtalL2.WFRXETH) {
// unwrap then send
IWETH(nToken).withdraw(amountOut);
Expand All @@ -120,8 +126,83 @@ contract MockReceiver is IOAppComposer {
}
} catch {
// reset approval - swap failed
IERC20(lzToken).approve(curve, 0);
IERC20(lzToken).transfer(recipient, amount);
IERC20(_oApp).approve(curve, 0);

// send non-converted OFT to recipient
IERC20(_oApp).transfer(recipient, amount);
}
}

/// @notice swap native token on curve and send OFT to another chain
/// @param _oApp Address of the upgradeable OFT
/// @param _dstEid Destination EID
/// @param _to Bytes32 representation of recipient ( ie. for EVM: bytes32(uint256(uint160(addr))) )
/// @param _amount Amount of OFT to send
/// @param _amountOutMin Minimum amount allowed to receive from the Curve.fi swap
/// @param _amountLDMin Minimum amount allowed to receive from the LZ send
function swapAndSend(
address _oApp,
uint32 _dstEid,
bytes32 _to,
uint256 _amount,
uint256 _amountOutMin,
uint256 _amountLDMin
) external {
(
address nToken,
address curve
) = _getRespectiveTokens(_oApp);

// transfer from sender to here
IERC20(nToken).transferFrom(msg.sender, address(this), _amount);

// Swap
IERC20(nToken).approve(curve, _amount);
uint256 amountOut = ICurve(curve).exchange({i: int128(0), j: int128(1), _dx: _amount, _min_dy: _amountOutMin });

// Send OFT to destination chain
_send({
_oApp: _oApp,
_dstEid: _dstEid,
_to: _to,
_amountLD: amountOut,
_amountLDMin: _amountLDMin
});
}

function _swap(
bool _isNToken,
address _token,
address _curve,
uint256 _amount,
uint256 _amountOutMin
) internal returns (uint256) {
// approval first
IERC20(_token).approve(_curve, _amount);

// swap
(int128 i, int128 j) = _isNToken ? (int128(0), int128(1)) : (int128(1), int128(0));
return ICurve(_curve).exchange({ i: i, j: j, _dx: _amount, _min_dy: _amountOutMin});
}

function _send(
address _oApp,
uint32 _dstEid,
bytes32 _to,
uint256 _amountLD,
uint256 _amountLDMin
) internal {
bytes memory options = OptionsBuilder.newOptions();
SendParam memory sendParam = SendParam({
dstEid: _dstEid,
to: _to,
amountLD: _amountLD,
minAmountLD: _amountLDMin,
extraOptions: options,
composeMsg: "",
oftCmd: ""
});
MessagingFee memory fee = IOFT(_oApp).quoteSend(sendParam, false);
IOFT(_oApp).send{ value: fee.nativeFee }(sendParam, fee, payable(msg.sender));
}
}

0 comments on commit 23dc21e

Please sign in to comment.