Skip to content

Commit

Permalink
feat: EthereumNativeBridgeComposer, LZCurveAMO
Browse files Browse the repository at this point in the history
  • Loading branch information
pegahcarter committed Nov 6, 2024
1 parent ace1d40 commit 669e9ce
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 196 deletions.
51 changes: 51 additions & 0 deletions src/contracts/FraxtalStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract FraxtalStorage {
error InvalidOApp();

// Note: each curve pool is "native" token / "layerzero" token with "a" factor of 1400
// All OFTs can be referenced at https://github.com/FraxFinance/frax-oft-upgradeable?tab=readme-ov-file#proxy-upgradeable-ofts
address public constant fraxOft = 0x80Eede496655FB9047dd39d9f418d5483ED600df;
address public constant sFraxOft = 0x5Bff88cA1442c2496f7E475E9e7786383Bc070c0;
address public constant frxEthOft = 0x43eDD7f3831b08FE70B7555ddD373C8bF65a9050;
address public constant sFrxEthOft = 0x3Ec3849C33291a9eF4c5dB86De593EB4A37fDe45;
address public constant fxsOft = 0x64445f0aecC51E94aD52d8AC56b7190e764E561a;
address public constant fpiOft = 0x90581eCa9469D8D7F5D3B60f4715027aDFCf7927;
address public constant fraxCurve = 0x53f8F4e154F68C2D29a0D06BD50f82bCf1bd95dB;
address public constant sFraxCurve = 0xd2866eF5A94E741Ec8EDE5FF8e3A1f9C59c5e298;
address public constant frxEthCurve = 0x50842664DfBD876249D0113671d72dB168FBE4d0;
address public constant sFrxEthCurve = 0xe5F61df936d50302962d5B914537Ff3cB63b3526;
address public constant fxsCurve = 0xBc383485068Ffd275D7262Bef65005eE7a5A1870;
address public constant fpiCurve = 0x7FaA69f8fEbe38bBfFbAE3252DE7D1491F0c6157;

/// @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 pure 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();
}
}
}
87 changes: 87 additions & 0 deletions src/contracts/LZCurveAMO.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

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

import { FraxtalStorage } from "src/contracts/FraxtalStorage.sol";

interface ICurve {
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
function get_balances() external view returns (uint256[] memory);
}

contract LZCurveAMO is AccessControlUpgradeable, FraxtalStorage {
using OptionsBuilder for bytes;

// keccak256(abi.encode(uint256(keccak256("frax.storage.LZCurveAmoStorage")) - 1));
bytes32 private constant LZCurveAmoStorageLocation =
0x34cfa87765bced8684ef975fad48f7c370ba6aca6fca817512efcf044977addf;
struct LZCurveAmoStorage {
address ethereumComposer;
}
function _getLZCurveAmoStorage() private pure returns (LZCurveAmoStorage storage $) {
assembly {
$.slot := LZCurveAmoStorageLocation
}
}

constructor() {
_disableInitializers();
}

function initialize(address _owner) external initializer {
_grantRole(DEFAULT_ADMIN_ROLE, _owner);
}

function setEthereumComposer(address _ethereumComposer) external onlyRole(DEFAULT_ADMIN_ROLE) {
LZCurveAmoStorage storage $ = _getLZCurveAmoStorage();
$.ethereumComposer = _ethereumComposer;
}

// todo: AC
function exchange(address _oApp, bool _sell, uint256 _amountIn, uint256 _amountOutMin) external {
(address nToken, address curve) = _getRespectiveTokens(_oApp);

uint256 amountOut;
if (_sell) {
// _sell oft for nToken
IERC20(_oApp).approve(curve, _amountIn);
amountOut = ICurve(curve).exchange({ i: int128(1), j: int128(0), _dx: _amountIn, _min_dy: _amountOutMin });
} else {
// sell nToken for oft
IERC20(nToken).approve(curve, _amountIn);
amountOut = ICurve(curve).exchange({ i: int128(0), j: int128(1), _dx: _amountIn, _min_dy: _amountOutMin });
}

// TODO: now what
}

// TODO: AC
function sendToAdapterAndBridgeBackNatively(address _oApp, uint256 _amount) external {
bytes memory options = OptionsBuilder.newOptions().addExecutorLzComposeOption(0, 100_000, 0);
bytes memory composeMsg = abi.encode(uint256(0));
SendParam memory sendParam = SendParam({
dstEid: uint32(30101), // Ethereum
to: bytes32(uint256(uint160(ethereumComposer()))),
amountLD: _amount,
minAmountLD: 0,
extraOptions: options,
composeMsg: composeMsg,
oftCmd: ""
});
MessagingFee memory fee = IOFT(_oApp).quoteSend(sendParam, false);
IOFT(_oApp).send{ value: fee.nativeFee }(sendParam, fee, payable(address(this)));
}

// TODO: AC
function sendToFerry(address _oApp, uint256 _amount) external {}

function ethereumComposer() public view returns (address) {
LZCurveAmoStorage storage $ = _getLZCurveAmoStorage();
return $.ethereumComposer;
}
}
134 changes: 134 additions & 0 deletions src/contracts/composers/EthereumNativeBridgeComposer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

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

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

import { FraxtalStorage } from "src/contracts/FraxtalStorage.sol";

interface IL1Bridge {
function depositERC20To(
address _l1Token,
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
) external;
}

contract EthereumNativeBridgeComposer is IOAppComposer, Initializable, FraxtalStorage {
using OptionsBuilder for bytes;

uint256 public counterA;
uint256 public counterB;

// keccak256(abi.encode(uint256(keccak256("frax.storage.EthereumNativeBridgeComposer")) - 1));
bytes32 private constant EthereumNativeBridgeComposerStorageLocation =
0x0bb7e48c2e948c6814cc15299d1ccfc0eafc6cbe8616be42cbb8c40105c27f52;

struct EthereumNativeBridgeComposerStorage {
address endpoint;
address l1Bridge;
address lzCurveAmo;
}

function _getEthereumNativeBridgeComposerStorage()
private
pure
returns (EthereumNativeBridgeComposerStorage storage $)
{
assembly {
$.slot := EthereumNativeBridgeComposerStorageLocation
}
}

constructor() {
_disableInitializers();
// approve bridge spending all six tokens
}

/// @dev Initializes the contract.
function initialize(address _endpoint, address _l1Bridge, address _lzCurveAmo) external initializer {
EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage();
$.endpoint = _endpoint;
$.l1Bridge = _l1Bridge;
$.lzCurveAmo = _lzCurveAmo;
}

/// @notice Handles incoming composed messages from LayerZero.
/// @dev Decodes the message payload to perform a native bridge back to L2.
/// This method expects the encoded compose message to contain the recipient address as the AMO.
/// @dev source: https://docs.layerzero.network/v2/developers/evm/protocol-gas-settings/options#lzcompose-option
/// @param _oApp The address of the originating OApp.
/// @param /*_guid*/ The globally unique identifier of the message (unused).
/// @param _message The encoded message content in the format of the OFTComposeMsgCodec.
/// @param /*Executor*/ Executor address (unused).
/// @param /*Executor Data*/ Additional data for checking for a specific executor (unused).
function lzCompose(
address _oApp,
bytes32 /*_guid*/,
bytes calldata _message,
address /*Executor*/,
bytes calldata /*Executor Data*/
) external payable override {
EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage();
require(msg.sender == $.endpoint, "!endpoint");

address l1Token = IOFT(_oApp).token();
(address l2Token, ) = _getRespectiveTokens(_oApp);
uint256 amount = OFTComposeMsgCodec.amountLD(_message);

try
IL1Bridge($.l1Bridge).depositERC20To({
_l1Token: l1Token,
_l2Token: l2Token,
_to: $.lzCurveAmo,
_amount: amount,
_minGasLimit: uint32(200_000),
_extraData: ""
})
{
counterA += 1;
} catch {
counterB += 1;
}
}

// TODO: ac
function sendToFraxtalViaLayerZero(address _oApp, uint256 _amount) external {
bytes memory options = OptionsBuilder.newOptions();
SendParam memory sendParam = SendParam({
dstEid: uint32(30255), // fraxtal
to: bytes32(uint256(uint160(lzCurveAmo()))),
amountLD: _amount,
minAmountLD: 0,
extraOptions: options,
composeMsg: "",
oftCmd: ""
});
MessagingFee memory fee = IOFT(_oApp).quoteSend(sendParam, false);
IOFT(_oApp).send{ value: fee.nativeFee }(sendParam, fee, payable(address(this)));
}

function endpoint() external view returns (address) {
EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage();
return $.endpoint;
}

function l1Bridge() external view returns (address) {
EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage();
return $.l1Bridge;
}

function lzCurveAmo() public view returns (address) {
EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage();
return $.lzCurveAmo;
}
}
Loading

0 comments on commit 669e9ce

Please sign in to comment.