generated from FraxFinance/frax-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: EthereumNativeBridgeComposer, LZCurveAMO
- Loading branch information
1 parent
ace1d40
commit 669e9ce
Showing
7 changed files
with
300 additions
and
196 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
134
src/contracts/composers/EthereumNativeBridgeComposer.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.