Skip to content

Commit ae2aca2

Browse files
authored
Merge pull request #34 from skip-mev/jw/weth
[API-4248] Wrap and unwrap ETH
2 parents 3b05ab0 + 1467845 commit ae2aca2

File tree

7 files changed

+322
-11
lines changed

7 files changed

+322
-11
lines changed

SwapRouter/script/Deploy.s.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ contract SwapRouterDeploy is Script {
1111
function run() external {
1212
vm.startBroadcast();
1313

14-
SkipGoSwapRouter router = SkipGoSwapRouter(0xa11CC0eFb1B3AcD95a2B8cd316E8c132E16048b5);
14+
address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
15+
16+
SkipGoSwapRouter router = new SkipGoSwapRouter(weth);
1517

1618
UniswapV2Adapter uniswapV2Adapter = new UniswapV2Adapter();
1719
UniswapV3Adapter uniswapV3Adapter = new UniswapV3Adapter();

SwapRouter/src/SkipGoSwapRouter.sol

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
55
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
66

77
import {IAdapter} from "./interfaces/IAdapter.sol";
8+
import {IWETH} from "./interfaces/IWETH.sol";
89

910
contract SkipGoSwapRouter is Ownable {
1011
enum ExchangeType {
@@ -14,6 +15,8 @@ contract SkipGoSwapRouter is Ownable {
1415

1516
mapping(ExchangeType => address) public adapters;
1617

18+
address public weth;
19+
1720
struct Hop {
1821
ExchangeType exchangeType;
1922
bytes data;
@@ -24,7 +27,11 @@ contract SkipGoSwapRouter is Ownable {
2427
uint256 feeBPS;
2528
}
2629

27-
constructor() Ownable(msg.sender) {}
30+
constructor(address _weth) Ownable(msg.sender) {
31+
weth = _weth;
32+
}
33+
34+
receive() external payable {}
2835

2936
function swapExactIn(
3037
uint256 amountIn,
@@ -34,7 +41,15 @@ contract SkipGoSwapRouter is Ownable {
3441
Hop[] calldata hops,
3542
Affiliate[] calldata affiliates
3643
) external payable returns (uint256 amountOut) {
37-
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
44+
// if token in is ETH, msg.value must be equal to amountIn
45+
// if token in is not ETH, msg.value must be 0
46+
require(msg.value == (tokenIn == address(0) ? amountIn : 0), "invalid msg.value");
47+
48+
if (tokenIn == address(0)) {
49+
IWETH(weth).deposit{value: amountIn}();
50+
} else {
51+
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
52+
}
3853

3954
amountOut = amountIn;
4055

@@ -61,9 +76,14 @@ contract SkipGoSwapRouter is Ownable {
6176

6277
uint256 amountPaid = _payAffiliateFees(tokenOut, amountOut, affiliates);
6378

64-
IERC20(tokenOut).transfer(msg.sender, amountOut - amountPaid);
65-
6679
amountOut = amountOut - amountPaid;
80+
81+
if (tokenOut == address(0)) {
82+
IWETH(weth).withdraw(amountOut);
83+
payable(msg.sender).transfer(amountOut);
84+
} else {
85+
IERC20(tokenOut).transfer(msg.sender, amountOut);
86+
}
6787
}
6888

6989
function swapExactOut(
@@ -78,7 +98,13 @@ contract SkipGoSwapRouter is Ownable {
7898

7999
require(amountIn <= amountInMax, "amount in is greater than amount in max");
80100

81-
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
101+
if (tokenIn == address(0)) {
102+
require(msg.value >= amountIn, "msg.value is less than amount in");
103+
IWETH(weth).deposit{value: amountIn}();
104+
} else {
105+
require(msg.value == 0, "msg.value must be 0");
106+
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
107+
}
82108

83109
amountOut = amountIn;
84110

@@ -103,7 +129,19 @@ contract SkipGoSwapRouter is Ownable {
103129

104130
uint256 amountPaid = _payAffiliateFees(tokenOut, amountOut, affiliates);
105131

106-
IERC20(tokenOut).transfer(msg.sender, amountOut - amountPaid);
132+
uint256 amountOutAfterFees = amountOut - amountPaid;
133+
134+
if (tokenOut == address(0)) {
135+
IWETH(weth).withdraw(amountOutAfterFees);
136+
payable(msg.sender).transfer(amountOutAfterFees);
137+
} else {
138+
IERC20(tokenOut).transfer(msg.sender, amountOutAfterFees);
139+
}
140+
141+
// refund unused ETH
142+
if (tokenIn == address(0) && msg.value > amountIn) {
143+
payable(msg.sender).transfer(msg.value - amountIn);
144+
}
107145
}
108146

109147
function getAmountOut(uint256 amountIn, Hop[] calldata hops) public view returns (uint256 amountOut) {

SwapRouter/src/adapters/UniswapV2Adapter.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ contract UniswapV2Adapter {
1313
uint256 fee;
1414
}
1515

16-
function swapExactIn(uint256 amountIn, bytes calldata data) external returns (uint256 amountOut) {
16+
function swapExactIn(uint256 amountIn, bytes calldata data) external payable returns (uint256 amountOut) {
1717
UniswapV2Data memory uniswapV2Data = abi.decode(data, (UniswapV2Data));
1818

1919
(uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(uniswapV2Data.pool).getReserves();

SwapRouter/src/adapters/UniswapV3Adapter.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ contract UniswapV3Adapter {
1515
address swapRouter;
1616
}
1717

18-
function swapExactIn(uint256 amountIn, bytes calldata data) external returns (uint256 amountOut) {
18+
function swapExactIn(uint256 amountIn, bytes calldata data) external payable returns (uint256 amountOut) {
1919
UniswapV3Data memory uniswapV3Data = abi.decode(data, (UniswapV3Data));
2020

2121
IERC20(uniswapV3Data.tokenIn).approve(uniswapV3Data.swapRouter, amountIn);

SwapRouter/src/interfaces/IWETH.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
// Copyright (C) 2015, 2016, 2017 Dapphub
3+
// Adapted by Ethereum Community 2021
4+
pragma solidity ^0.8.0;
5+
6+
/// @dev Wrapped Ether v10 (WETH10) is an Ether (ETH) ERC-20 wrapper. You can `deposit` ETH and obtain a WETH10 balance which can then be operated as an ERC-20 token. You can
7+
/// `withdraw` ETH from WETH10, which will then burn WETH10 token in your wallet. The amount of WETH10 token in any wallet is always identical to the
8+
/// balance of ETH deposited minus the ETH withdrawn with that specific wallet.
9+
interface IWETH {
10+
/// @dev `msg.value` of ETH sent to this contract grants caller account a matching increase in WETH10 token balance.
11+
/// Emits {Transfer} event to reflect WETH10 token mint of `msg.value` from `address(0)` to caller account.
12+
function deposit() external payable;
13+
14+
/// @dev Burn `value` WETH10 token from caller account and withdraw matching ETH to the same.
15+
/// Emits {Transfer} event to reflect WETH10 token burn of `value` to `address(0)` from caller account.
16+
/// Requirements:
17+
/// - caller account must have at least `value` balance of WETH10 token.
18+
function withdraw(uint256 value) external;
19+
}

0 commit comments

Comments
 (0)