Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion SwapRouter/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ contract SwapRouterDeploy is Script {
function run() external {
vm.startBroadcast();

SkipGoSwapRouter router = SkipGoSwapRouter(0xa11CC0eFb1B3AcD95a2B8cd316E8c132E16048b5);
address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is weth address the same on all chains?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I'm going to have to update this script for more proper deployments, I just wrote this to deploy a contract I could test with


SkipGoSwapRouter router = new SkipGoSwapRouter(weth);

UniswapV2Adapter uniswapV2Adapter = new UniswapV2Adapter();
UniswapV3Adapter uniswapV3Adapter = new UniswapV3Adapter();
Expand Down
50 changes: 44 additions & 6 deletions SwapRouter/src/SkipGoSwapRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {IAdapter} from "./interfaces/IAdapter.sol";
import {IWETH} from "./interfaces/IWETH.sol";

contract SkipGoSwapRouter is Ownable {
enum ExchangeType {
Expand All @@ -14,6 +15,8 @@ contract SkipGoSwapRouter is Ownable {

mapping(ExchangeType => address) public adapters;

address public weth;

struct Hop {
ExchangeType exchangeType;
bytes data;
Expand All @@ -24,7 +27,11 @@ contract SkipGoSwapRouter is Ownable {
uint256 feeBPS;
}

constructor() Ownable(msg.sender) {}
constructor(address _weth) Ownable(msg.sender) {
weth = _weth;
}

receive() external payable {}

function swapExactIn(
uint256 amountIn,
Expand All @@ -34,7 +41,15 @@ contract SkipGoSwapRouter is Ownable {
Hop[] calldata hops,
Affiliate[] calldata affiliates
) external payable returns (uint256 amountOut) {
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
// if token in is ETH, msg.value must be equal to amountIn
// if token in is not ETH, msg.value must be 0
require(msg.value == (tokenIn == address(0) ? amountIn : 0), "invalid msg.value");

if (tokenIn == address(0)) {
IWETH(weth).deposit{value: amountIn}();
} else {
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
}

amountOut = amountIn;

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

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

IERC20(tokenOut).transfer(msg.sender, amountOut - amountPaid);

amountOut = amountOut - amountPaid;

if (tokenOut == address(0)) {
IWETH(weth).withdraw(amountOut);
payable(msg.sender).transfer(amountOut);
} else {
IERC20(tokenOut).transfer(msg.sender, amountOut);
}
}

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

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

IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
if (tokenIn == address(0)) {
require(msg.value >= amountIn, "msg.value is less than amount in");
IWETH(weth).deposit{value: amountIn}();
} else {
require(msg.value == 0, "msg.value must be 0");
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
}

amountOut = amountIn;

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

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

IERC20(tokenOut).transfer(msg.sender, amountOut - amountPaid);
uint256 amountOutAfterFees = amountOut - amountPaid;

if (tokenOut == address(0)) {
IWETH(weth).withdraw(amountOutAfterFees);
payable(msg.sender).transfer(amountOutAfterFees);
} else {
IERC20(tokenOut).transfer(msg.sender, amountOutAfterFees);
}

// refund unused ETH
if (tokenIn == address(0) && msg.value > amountIn) {
payable(msg.sender).transfer(msg.value - amountIn);
}
}

function getAmountOut(uint256 amountIn, Hop[] calldata hops) public view returns (uint256 amountOut) {
Expand Down
2 changes: 1 addition & 1 deletion SwapRouter/src/adapters/UniswapV2Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ contract UniswapV2Adapter {
uint256 fee;
}

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

(uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(uniswapV2Data.pool).getReserves();
Expand Down
2 changes: 1 addition & 1 deletion SwapRouter/src/adapters/UniswapV3Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract UniswapV3Adapter {
address swapRouter;
}

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

IERC20(uniswapV3Data.tokenIn).approve(uniswapV3Data.swapRouter, amountIn);
Expand Down
19 changes: 19 additions & 0 deletions SwapRouter/src/interfaces/IWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2015, 2016, 2017 Dapphub
// Adapted by Ethereum Community 2021
pragma solidity ^0.8.0;

/// @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
/// `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
/// balance of ETH deposited minus the ETH withdrawn with that specific wallet.
interface IWETH {
/// @dev `msg.value` of ETH sent to this contract grants caller account a matching increase in WETH10 token balance.
/// Emits {Transfer} event to reflect WETH10 token mint of `msg.value` from `address(0)` to caller account.
function deposit() external payable;

/// @dev Burn `value` WETH10 token from caller account and withdraw matching ETH to the same.
/// Emits {Transfer} event to reflect WETH10 token burn of `value` to `address(0)` from caller account.
/// Requirements:
/// - caller account must have at least `value` balance of WETH10 token.
function withdraw(uint256 value) external;
}
Loading
Loading