feat: Wrap and unwrap ETH
--- don't change below this line --- ENG-4041 Took 2 hours 28 minutes Took 14 seconds Took 11 seconds Took 2 minutes Took 1 minute Took 7 minutes
This commit is contained in:
9
foundry/lib/IWETH.sol
Normal file
9
foundry/lib/IWETH.sol
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.28;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
|
interface IWETH is IERC20 {
|
||||||
|
function deposit() external payable;
|
||||||
|
function withdraw(uint256) external;
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.28;
|
pragma solidity ^0.8.28;
|
||||||
|
|
||||||
|
import "../lib/IWETH.sol";
|
||||||
|
import "../lib/bytes/LibPrefixLengthEncodedByteArray.sol";
|
||||||
|
import "./CallbackVerificationDispatcher.sol";
|
||||||
|
import "./SwapExecutionDispatcher.sol";
|
||||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
@@ -8,9 +12,12 @@ import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
|||||||
import "./ExecutionDispatcher.sol";
|
import "./ExecutionDispatcher.sol";
|
||||||
import "./CallbackVerificationDispatcher.sol";
|
import "./CallbackVerificationDispatcher.sol";
|
||||||
import "@openzeppelin/contracts/utils/Pausable.sol";
|
import "@openzeppelin/contracts/utils/Pausable.sol";
|
||||||
|
import {Swap} from "./Swap.sol";
|
||||||
|
|
||||||
error TychoRouter__WithdrawalFailed();
|
error TychoRouter__WithdrawalFailed();
|
||||||
error TychoRouter__AddressZero();
|
error TychoRouter__AddressZero();
|
||||||
|
error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount);
|
||||||
|
error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount);
|
||||||
|
|
||||||
contract TychoRouter is
|
contract TychoRouter is
|
||||||
AccessControl,
|
AccessControl,
|
||||||
@@ -19,8 +26,11 @@ contract TychoRouter is
|
|||||||
Pausable
|
Pausable
|
||||||
{
|
{
|
||||||
IAllowanceTransfer public immutable permit2;
|
IAllowanceTransfer public immutable permit2;
|
||||||
|
IWETH private immutable _weth;
|
||||||
|
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
using LibPrefixLengthEncodedByteArray for bytes;
|
||||||
|
using Swap for bytes;
|
||||||
|
|
||||||
//keccak256("NAME_OF_ROLE") : save gas on deployment
|
//keccak256("NAME_OF_ROLE") : save gas on deployment
|
||||||
bytes32 public constant EXECUTOR_SETTER_ROLE =
|
bytes32 public constant EXECUTOR_SETTER_ROLE =
|
||||||
@@ -48,9 +58,10 @@ contract TychoRouter is
|
|||||||
);
|
);
|
||||||
event FeeSet(uint256 indexed oldFee, uint256 indexed newFee);
|
event FeeSet(uint256 indexed oldFee, uint256 indexed newFee);
|
||||||
|
|
||||||
constructor(address _permit2) {
|
constructor(address _permit2, address weth) {
|
||||||
permit2 = IAllowanceTransfer(_permit2);
|
permit2 = IAllowanceTransfer(_permit2);
|
||||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||||
|
_weth = IWETH(weth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,6 +220,27 @@ contract TychoRouter is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Wraps a defined amount of ETH.
|
||||||
|
* @param amount of native ETH to wrap.
|
||||||
|
*/
|
||||||
|
function _wrapETH(uint256 amount) internal {
|
||||||
|
if (msg.value > 0 && msg.value != amount) {
|
||||||
|
revert TychoRouter__MessageValueMismatch(msg.value, amount);
|
||||||
|
}
|
||||||
|
_weth.deposit{value: amount}();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Unwraps a defined amount of WETH.
|
||||||
|
* @param amount of WETH to unwrap.
|
||||||
|
*/
|
||||||
|
function _unwrapETH(uint256 amount) internal {
|
||||||
|
uint256 unwrapAmount =
|
||||||
|
amount == 0 ? _weth.balanceOf(address(this)) : amount;
|
||||||
|
_weth.withdraw(unwrapAmount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Allows this contract to receive native token
|
* @dev Allows this contract to receive native token
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -189,4 +189,28 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
tychoRouter.pause();
|
tychoRouter.pause();
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testWrapETH() public {
|
||||||
|
uint256 amount = 1 ether;
|
||||||
|
vm.deal(BOB, amount);
|
||||||
|
|
||||||
|
vm.startPrank(BOB);
|
||||||
|
tychoRouter.wrapETH{value: amount}(amount);
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
assertEq(address(tychoRouter).balance, 0);
|
||||||
|
assertEq(IERC20(WETH_ADDR).balanceOf(address(tychoRouter)), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUnwrapETH() public {
|
||||||
|
uint256 amount = 1 ether;
|
||||||
|
deal(WETH_ADDR, address(tychoRouter), amount);
|
||||||
|
|
||||||
|
vm.startPrank(BOB);
|
||||||
|
tychoRouter.unwrapETH(amount);
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
assertEq(address(tychoRouter).balance, amount);
|
||||||
|
assertEq(IERC20(WETH_ADDR).balanceOf(address(tychoRouter)), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,32 @@ pragma solidity ^0.8.13;
|
|||||||
import "@src/TychoRouter.sol";
|
import "@src/TychoRouter.sol";
|
||||||
import "./Constants.sol";
|
import "./Constants.sol";
|
||||||
import "./mock/MockERC20.sol";
|
import "./mock/MockERC20.sol";
|
||||||
|
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
|
||||||
|
|
||||||
|
contract TychoRouterExposed is TychoRouter {
|
||||||
|
constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {}
|
||||||
|
|
||||||
|
function wrapETH(uint256 amount) external payable {
|
||||||
|
return _wrapETH(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapETH(uint256 amount) external {
|
||||||
|
return _unwrapETH(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contract TychoRouterTestSetup is Test, Constants {
|
contract TychoRouterTestSetup is Test, Constants {
|
||||||
TychoRouter tychoRouter;
|
TychoRouterExposed tychoRouter;
|
||||||
address executorSetter;
|
address executorSetter;
|
||||||
address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
||||||
MockERC20[] tokens;
|
MockERC20[] tokens;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
|
uint256 forkBlock = 21000000;
|
||||||
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
|
|
||||||
vm.startPrank(ADMIN);
|
vm.startPrank(ADMIN);
|
||||||
tychoRouter = new TychoRouter(permit2Address);
|
tychoRouter = new TychoRouterExposed(permit2Address, WETH_ADDR);
|
||||||
tychoRouter.grantRole(keccak256("EXECUTOR_SETTER_ROLE"), BOB);
|
tychoRouter.grantRole(keccak256("EXECUTOR_SETTER_ROLE"), BOB);
|
||||||
tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER);
|
tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER);
|
||||||
tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);
|
tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);
|
||||||
|
|||||||
Reference in New Issue
Block a user