diff --git a/script/DeployBETHToETH.s.sol b/script/DeployBETHToETH.s.sol new file mode 100644 index 0000000..8377910 --- /dev/null +++ b/script/DeployBETHToETH.s.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "src/hooks/cypher-eth/BETHToETH.sol"; +import {IWNativeToken} from "src/hooks/cypher-eth/IWNativeToken.sol"; + +contract DeployBETHToETH is Script { + // mainnet addresses + address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address constant BETH = 0x5624344235607940d4d4EE76Bf8817d403EB9Cf8; + address constant swapRouter = 0x20C5893f69F635f55b0367C519F3f95e59c0b0Ab; + + function run() external { + vm.startBroadcast(); + + BETHToETH bethToEth = new BETHToETH(IERC20(BETH), IWNativeToken(WETH), ISwapRouter(swapRouter)); + + console.log("BETHToETH deployed to:", address(bethToEth)); + console.log("BETH address:", BETH); + console.log("WETH address:", WETH); + + vm.stopBroadcast(); + } +} diff --git a/src/hooks/cypher-eth/BETHToETH.sol b/src/hooks/cypher-eth/BETHToETH.sol new file mode 100644 index 0000000..0951a0a --- /dev/null +++ b/src/hooks/cypher-eth/BETHToETH.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IWNativeToken} from "src/hooks/cypher-eth/IWNativeToken.sol"; +import {ISwapRouter} from "src/hooks/cypher-eth/ISwapRouter.sol"; + +contract BETHToETH { + IERC20 public immutable bethContract; + IWNativeToken public immutable wethContract; + ISwapRouter public immutable swapRouterContract; + + constructor(IERC20 _bethContract, IWNativeToken _wethContract, ISwapRouter _swapRouterContract) { + require(address(_bethContract) != address(0), "Invalid BETH address"); + require(address(_wethContract) != address(0), "Invalid WETH address"); + require(address(_swapRouterContract) != address(0), "Invalid WETH address"); + bethContract = _bethContract; + wethContract = _wethContract; + swapRouterContract = _swapRouterContract; + } + + function swapBethWithEth(uint256 _swapAmount, address _recipient) public returns (uint256 amountOut) { + require(_swapAmount > 0, "Amount must be greater than 0"); + require(_recipient != address(0), "Invalid recipient"); + + require( + bethContract.transferFrom(msg.sender, address(this), _swapAmount), "error while transferFrom beth to this" + ); + + bethContract.approve(address(swapRouterContract), _swapAmount); + + amountOut = swapRouterContract.exactInputSingle( + ISwapRouter.ExactInputSingleParams({ + tokenIn: address(bethContract), + tokenOut: address(wethContract), + deployer: address(0), + recipient: address(this), + deadline: block.timestamp + 15 minutes, + amountIn: _swapAmount, + amountOutMinimum: 0, + limitSqrtPrice: 0 + }) + ); + + bethContract.approve(address(swapRouterContract), 0); // extra safety + wethContract.withdraw(amountOut); + (bool success,) = _recipient.call{value: amountOut}(""); + require(success, "ETH transfer failed"); + } + + fallback() external payable {} +} diff --git a/src/hooks/cypher-eth/ISwapRouter.sol b/src/hooks/cypher-eth/ISwapRouter.sol new file mode 100644 index 0000000..fa82b97 --- /dev/null +++ b/src/hooks/cypher-eth/ISwapRouter.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +interface ISwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + address deployer; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 limitSqrtPrice; + } + + function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); +} diff --git a/src/hooks/cypher-eth/IWNativeToken.sol b/src/hooks/cypher-eth/IWNativeToken.sol new file mode 100644 index 0000000..7225a73 --- /dev/null +++ b/src/hooks/cypher-eth/IWNativeToken.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/// @title Interface for WNativeToken +interface IWNativeToken is IERC20 { + /// @notice Deposit ether to get wrapped ether + function deposit() external payable; + + /// @notice Withdraw wrapped ether to get ether + function withdraw(uint256) external; +}