diff --git a/src/contracts/amos/EthereumLZSenderAMO.sol b/src/contracts/amos/EthereumLZCurveAMO.sol similarity index 53% rename from src/contracts/amos/EthereumLZSenderAMO.sol rename to src/contracts/amos/EthereumLZCurveAMO.sol index a405341..979eefa 100644 --- a/src/contracts/amos/EthereumLZSenderAMO.sol +++ b/src/contracts/amos/EthereumLZCurveAMO.sol @@ -11,6 +11,7 @@ import { SendParam, OFTReceipt, MessagingFee, IOFT } from "@fraxfinance/layerzer import { OptionsBuilder } from "@fraxfinance/layerzero-v2-upgradeable/oapp/contracts/oapp/libs/OptionsBuilder.sol"; import { FraxtalConstants } from "src/contracts/FraxtalConstants.sol"; +import { IL1Bridge } from "src/contracts/shared/IL1Bridge.sol"; // ==================================================================== // | ______ _______ | @@ -20,20 +21,21 @@ import { FraxtalConstants } from "src/contracts/FraxtalConstants.sol"; // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | // | | // ==================================================================== -// ======================= EthereumLZSenderAMO ======================== +// ======================= EthereumLZCurveAMO ======================== // ==================================================================== /// @author Frax Finance: https://github.com/FraxFinance -contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { +contract EthereumLZCurveAMO is OwnableUpgradeable, FraxtalConstants { using OptionsBuilder for bytes; error FailedEthTransfer(); - // keccak256(abi.encode(uint256(keccak256("frax.storage.EthereumLZSenderAMO")) - 1)); - bytes32 private constant EthereumLZSenderAmoStorageLocation = - 0xae71d745ae90af64f9f5e208d9e8dce64cca865b5246e2309de5b63cca6b882a; + // keccak256(abi.encode(uint256(keccak256("frax.storage.EthereumLZCurveAMO")) - 1)); + bytes32 private constant EthereumLZCurveAmoStorageLocation = + 0x9457c09651b1abd0043ec778e295270b24c7b02014c9a6e064fec36091d1f6ce; - struct EthereumLZSenderAmoStorage { + struct EthereumLZCurveAmoStorage { + address l1Bridge; address fraxtalLzCurveAmo; address fraxOft; address sFraxOft; @@ -42,9 +44,9 @@ contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { address fpiOft; } - function _getEthereumLZSenderAmoStorage() private pure returns (EthereumLZSenderAmoStorage storage $) { + function _getEthereumLZCurveAmoStorage() private pure returns (EthereumLZCurveAmoStorage storage $) { assembly { - $.slot := EthereumLZSenderAmoStorageLocation + $.slot := EthereumLZCurveAmoStorageLocation } } @@ -54,6 +56,7 @@ contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { function initialize( address _owner, + address _l1Bridge, address _fraxtalLzCurveAmo, address _fraxOft, address _sFraxOft, @@ -61,7 +64,8 @@ contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { address _fxsOft, address _fpiOft ) external initializer { - EthereumLZSenderAmoStorage storage $ = _getEthereumLZSenderAmoStorage(); + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); + $.l1Bridge = _l1Bridge; $.fraxtalLzCurveAmo = _fraxtalLzCurveAmo; $.fraxOft = _fraxOft; $.sFraxOft = _sFraxOft; @@ -72,23 +76,44 @@ contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { _transferOwnership(_owner); } - function sendAllToFraxtal() external { - EthereumLZSenderAmoStorage storage $ = _getEthereumLZSenderAmoStorage(); + function _validateOft(address _oApp) internal view { + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); + require( + _oApp == $.fraxOft || + _oApp == $.sFraxOft || + _oApp == $.sFrxEthOft || + _oApp == $.fxsOft || + _oApp == $.fpiOft, + "Invalid OFT" + ); + } - sendToFraxtal($.fraxOft); - sendToFraxtal($.sFraxOft); - sendToFraxtal($.sFrxEthOft); - sendToFraxtal($.fxsOft); - sendToFraxtal($.fpiOft); + function _getL2Token(address _oApp) internal view returns (address) { + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); + if (_oApp == $.fraxOft) { + return FraxtalConstants.frax; + } else if (_oApp == $.sFraxOft) { + return FraxtalConstants.sFrax; + } else if (_oApp == $.sFrxEthOft) { + return FraxtalConstants.sFrxEth; + } else if (_oApp == $.fxsOft) { + return FraxtalConstants.fxs; + } else { + /// @dev assume _oApp is one of these tokens as _validateOft is called prior and would revert + return FraxtalConstants.fpi; + } } - function sendToFraxtal(address _oApp) internal { + function sendViaLz(address _oApp, uint256 _amount) external { + // reverts if invalid OFT + _validateOft(_oApp); + address token = IOFT(_oApp).token(); - uint256 amount = IERC20(token).balanceOf(address(this)); - if (amount == 0) return; // craft tx bytes memory options = OptionsBuilder.newOptions(); + // round amount to prevent dust lost + uint256 amount = (_amount / 1e13) * 1e13; SendParam memory sendParam = SendParam({ dstEid: uint32(30255), // fraxtal to: bytes32(uint256(uint160(fraxtalLzCurveAmo()))), @@ -105,13 +130,37 @@ contract EthereumLZSenderAMO is OwnableUpgradeable, FraxtalConstants { IOFT(_oApp).send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); } + function sendViaNativeBridge(address _oApp, uint256 _amount) external { + // reverts if invalid OFT + _validateOft(_oApp); + + address token = IOFT(_oApp).token(); + + // approve and send + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); + IERC20(token).approve($.l1Bridge, _amount); + IL1Bridge($.l1Bridge).depositERC20To({ + _l1Token: token, + _l2Token: _getL2Token(_oApp), + _to: $.fraxtalLzCurveAmo, + _amount: _amount, + _minGasLimit: uint32(200_000), + _extraData: "" + }); + } + function rescueEth(address to, uint256 amount) external payable onlyOwner { (bool success, ) = to.call{ value: amount }(""); if (!success) revert FailedEthTransfer(); } function fraxtalLzCurveAmo() public view returns (address) { - EthereumLZSenderAmoStorage storage $ = _getEthereumLZSenderAmoStorage(); + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); return $.fraxtalLzCurveAmo; } + + function l1Bridge() public view returns (address) { + EthereumLZCurveAmoStorage storage $ = _getEthereumLZCurveAmoStorage(); + return $.l1Bridge; + } } diff --git a/src/contracts/amos/FraxtalLZCurveAMO.sol b/src/contracts/amos/FraxtalLZCurveAMO.sol index d4f8105..f55e8b5 100644 --- a/src/contracts/amos/FraxtalLZCurveAMO.sol +++ b/src/contracts/amos/FraxtalLZCurveAMO.sol @@ -81,23 +81,22 @@ contract FraxtalLZCurveAMO is AccessControlUpgradeable, FraxtalConstants { // TODO: now what } - function sendToAdapterAndBridgeBackNatively(address _oApp, uint256 _amount) external onlyRole(SEND_ROLE) { + function sendViaLz(address _oApp, uint256 _amount) external onlyRole(SEND_ROLE) { 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, + composeMsg: "", oftCmd: "" }); MessagingFee memory fee = IOFT(_oApp).quoteSend(sendParam, false); IOFT(_oApp).send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); } - function sendToFerry(address _oApp, uint256 _amount) external onlyRole(SEND_ROLE) { + function sendViaFerry(address _oApp, uint256 _amount) external onlyRole(SEND_ROLE) { (address nToken, ) = _getRespectiveTokens(_oApp); address ferry; if (nToken == FraxtalL2.FRAX) { diff --git a/src/contracts/composers/EthereumNativeBridgeComposer.sol b/src/contracts/composers/EthereumNativeBridgeComposer.sol deleted file mode 100644 index bb4c57c..0000000 --- a/src/contracts/composers/EthereumNativeBridgeComposer.sol +++ /dev/null @@ -1,123 +0,0 @@ -// 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 { FraxtalConstants } from "src/contracts/FraxtalConstants.sol"; -import { IL1Bridge } from "src/contracts/shared/IL1Bridge.sol"; - -// ==================================================================== -// | ______ _______ | -// | / _____________ __ __ / ____(_____ ____ _____ ________ | -// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | -// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | -// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | -// | | -// ==================================================================== -// ==================== EthereumNativeBridgeComposer ================== -// ==================================================================== - -/// @author Frax Finance: https://github.com/FraxFinance -contract EthereumNativeBridgeComposer is IOAppComposer, Initializable, FraxtalConstants { - 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 fraxtalLzCurveAmo; - } - - 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 _fraxtalLzCurveAmo) external initializer { - EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage(); - $.endpoint = _endpoint; - $.l1Bridge = _l1Bridge; - $.fraxtalLzCurveAmo = _fraxtalLzCurveAmo; - } - - /// @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); - - // approve and send - IERC20(l1Token).approve($.l1Bridge, amount); - try - IL1Bridge($.l1Bridge).depositERC20To({ - _l1Token: l1Token, - _l2Token: l2Token, - _to: $.fraxtalLzCurveAmo, - _amount: amount, - _minGasLimit: uint32(200_000), - _extraData: "" - }) - { - counterA += 1; - } catch { - IERC20(l1Token).approve($.l1Bridge, 0); - counterB += 1; - } - } - - function endpoint() external view returns (address) { - EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage(); - return $.endpoint; - } - - function l1Bridge() external view returns (address) { - EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage(); - return $.l1Bridge; - } - - function fraxtalLzCurveAmo() public view returns (address) { - EthereumNativeBridgeComposerStorage storage $ = _getEthereumNativeBridgeComposerStorage(); - return $.fraxtalLzCurveAmo; - } -} diff --git a/src/script/2DeployEthereum.s.sol b/src/script/2DeployEthereum.s.sol index bde9d53..30d0bc8 100644 --- a/src/script/2DeployEthereum.s.sol +++ b/src/script/2DeployEthereum.s.sol @@ -3,8 +3,7 @@ pragma solidity ^0.8.19; import { BaseScript } from "frax-std/BaseScript.sol"; import { console } from "frax-std/FraxTest.sol"; -import { EthereumNativeBridgeComposer } from "../contracts/composers/EthereumNativeBridgeComposer.sol"; -import { EthereumLZSenderAMO } from "../contracts/amos/EthereumLZSenderAMO.sol"; +import { EthereumLZCurveAMO } from "../contracts/amos/EthereumLZCurveAMO.sol"; import { FraxProxy } from "../contracts/FraxProxy.sol"; // Run this with source .env && forge script --broadcast --rpc-url $MAINNET_URL DeployFraxtalL.s.sol @@ -22,25 +21,16 @@ contract DeployFraxtalL is BaseScript { address fpiOft = 0x6Eca253b102D41B6B69AC815B9CC6bD47eF1979d; function run() public broadcaster { - // deploy EthereumNativeBridgeComposer - address implementation = address(new EthereumNativeBridgeComposer()); + // Deploy EthereumLZCurveAMO + address implementation = address(new EthereumLZCurveAMO()); FraxProxy proxy = new FraxProxy( - implementation, - multisig, - abi.encodeCall(EthereumNativeBridgeComposer.initialize, (endpoint, l1Bridge, fraxtalLzCurveAmo)) - ); - console.log("EthereumNativeBridgeComposer @ ", address(proxy)); - - // Deploy EthereumLZSenderAMO - implementation = address(new EthereumLZSenderAMO()); - proxy = new FraxProxy( implementation, multisig, abi.encodeCall( - EthereumLZSenderAMO.initialize, - (deployer, fraxtalLzCurveAmo, fraxOft, sFraxOft, sFrxEthOft, fxsOft, fpiOft) + EthereumLZCurveAMO.initialize, + (deployer, l1Bridge, fraxtalLzCurveAmo, fraxOft, sFraxOft, sFrxEthOft, fxsOft, fpiOft) ) ); - console.log("EthereumLZSenderAMO @ ", address(proxy)); + console.log("EthereumLZCurveAMO @ ", address(proxy)); } }