Skip to content

Commit

Permalink
refactor: interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
pegahcarter committed Feb 17, 2024
1 parent d33ee37 commit e5e34f1
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 182 deletions.
202 changes: 24 additions & 178 deletions src/contracts/periphery/FraxswapRouterMultihop.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.0;
pragma solidity ^0.8.0;
pragma abicoder v2;

// ====================================================================
Expand All @@ -13,6 +13,7 @@ pragma abicoder v2;
// ===================== Fraxswap Router Multihop =====================
// ====================================================================

import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeCast } from "@uniswap/v3-core/contracts/libraries/SafeCast.sol";
Expand All @@ -22,10 +23,13 @@ import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/ca
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { IUniswapV2Pair } from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

import { IPoolInterface } from "./interfaces/IPoolInterface.sol";
import { IFraxswapRouterMultihop } from "./interfaces/IFraxswapRouterMultihop.sol";

/// @title Fraxswap Router Multihop
/// @dev Router for swapping across the majority of the FRAX liquidity
/// @author Frax Finance (https://github.com/FraxFinance)
contract FraxswapRouterMultihop is ReentrancyGuard {
contract FraxswapRouterMultihop is ReentrancyGuard, IFraxswapRouterMultihop {
using SafeCast for uint256;
using SafeCast for int256;

Expand Down Expand Up @@ -96,8 +100,9 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
}
bool outputETH = params.tokenOut == address(0); // save gas
uint256 initialBalance;
if (!CHECK_AMOUNTOUT_IN_ROUTER)
if (!CHECK_AMOUNTOUT_IN_ROUTER) {
initialBalance = outputETH ? params.recipient.balance : IERC20(params.tokenOut).balanceOf(params.recipient);
}
for (uint256 i; i < route.nextHops.length; ++i) {
FraxswapRoute memory nextRoute = i == 0 ? firstRoute : abi.decode(route.nextHops[i], (FraxswapRoute));
executeAllHops(route, nextRoute, params.recipient);
Expand All @@ -119,10 +124,11 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
TransferHelper.safeTransfer(params.tokenOut, params.recipient, amountOut);
}
}
if (!CHECK_AMOUNTOUT_IN_ROUTER)
if (!CHECK_AMOUNTOUT_IN_ROUTER) {
amountOut = outputETH
? params.recipient.balance
: IERC20(params.tokenOut).balanceOf(params.recipient) - initialBalance;
}

// Check output amounts (IMPORTANT CHECK)
require(amountOut >= params.amountOutMinimum, "FSR:IO"); // Insufficient output
Expand Down Expand Up @@ -165,11 +171,11 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
bool zeroForOne = (step.extraParam2 == 0 && prevRoute.tokenOut < step.tokenOut) || step.extraParam2 == 1;
if (step.swapType == 0) {
// Execute virtual orders for Fraxswap
PoolInterface(step.pool).executeVirtualOrders(block.timestamp);
IPoolInterface(step.pool).executeVirtualOrders(block.timestamp);
}
if (step.extraParam1 == 1) {
// Fraxswap V2 has getAmountOut in the pair (different fees)
amountOut = PoolInterface(step.pool).getAmountOut(amountIn, prevRoute.tokenOut);
amountOut = IPoolInterface(step.pool).getAmountOut(amountIn, prevRoute.tokenOut);
} else {
// use the reserves and helper function for Uniswap V2 and Fraxswap V1
(uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(step.pool).getReserves();
Expand Down Expand Up @@ -202,12 +208,12 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
} else if (step.swapType == 3) {
// Curve exchange V2
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
PoolInterface(step.pool).exchange(step.extraParam1, step.extraParam2, amountIn, 0);
IPoolInterface(step.pool).exchange(step.extraParam1, step.extraParam2, amountIn, 0);
amountOut = IERC20(step.tokenOut).balanceOf(address(this));
} else if (step.swapType == 4) {
// Curve exchange, with returns (no ETH)
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
amountOut = PoolInterface(step.pool).exchange(
amountOut = IPoolInterface(step.pool).exchange(
int128(int256(step.extraParam1)),
int128(int256(step.extraParam2)),
amountIn,
Expand All @@ -216,7 +222,7 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
} else if (step.swapType == 5) {
// Curve exchange_underlying
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
amountOut = PoolInterface(step.pool).exchange_underlying(
amountOut = IPoolInterface(step.pool).exchange_underlying(
int128(int256(step.extraParam1)),
int128(int256(step.extraParam2)),
amountIn,
Expand All @@ -225,7 +231,7 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
} else if (step.swapType == 6) {
// Saddle
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
amountOut = PoolInterface(step.pool).swap(
amountOut = IPoolInterface(step.pool).swap(
uint8(step.extraParam1),
uint8(step.extraParam2),
amountIn,
Expand All @@ -236,14 +242,14 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
// FPIController
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
if (prevRoute.tokenOut == FRAX) {
amountOut = PoolInterface(step.pool).mintFPI(amountIn, 0);
amountOut = IPoolInterface(step.pool).mintFPI(amountIn, 0);
} else {
amountOut = PoolInterface(step.pool).redeemFPI(amountIn, 0);
amountOut = IPoolInterface(step.pool).redeemFPI(amountIn, 0);
}
} else if (step.swapType == 8) {
// ERC4626/Fraxlend deposit
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
amountOut = PoolInterface(step.pool).deposit(
amountOut = IPoolInterface(step.pool).deposit(
amountIn,
step.directFundNextPool == 1 ? getNextDirectFundingPool(route, recipient) : address(this)
);
Expand All @@ -253,7 +259,7 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
// Unwrap WETH
WETH9.withdraw(amountIn);
}
PoolInterface(step.pool).submitAndGive{ value: amountIn }(
IPoolInterface(step.pool).submitAndGive{ value: amountIn }(
step.directFundNextPool == 1 ? getNextDirectFundingPool(route, recipient) : address(this)
);
amountOut = amountIn; // exchange 1 for 1
Expand All @@ -279,7 +285,7 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
} else {
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
}
amountOut = PoolInterface(step.pool).exchange{ value: value }(
amountOut = IPoolInterface(step.pool).exchange{ value: value }(
int128(int256(step.extraParam1)),
int128(int256(step.extraParam2)),
amountIn,
Expand All @@ -295,7 +301,7 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
// this pool is funded by router
TransferHelper.safeTransfer(prevRoute.tokenOut, step.pool, amountIn);
}
amountOut = PoolInterface(step.pool).exchange_received(
amountOut = IPoolInterface(step.pool).exchange_received(
int128(int256(step.extraParam1)),
int128(int256(step.extraParam2)),
amountIn,
Expand All @@ -304,15 +310,15 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
);
} else if (step.swapType == 13) {
// Redeem ERC4626
amountOut = PoolInterface(step.pool).redeem(
amountOut = IPoolInterface(step.pool).redeem(
amountIn,
step.directFundNextPool == 1 ? getNextDirectFundingPool(route, recipient) : address(this),
address(this)
);
} else if (step.swapType == 14) {
// Curve exchange, with returns + receiver (no ETH)
TransferHelper.safeApprove(prevRoute.tokenOut, step.pool, amountIn);
amountOut = PoolInterface(step.pool).exchange(
amountOut = IPoolInterface(step.pool).exchange(
int128(int256(step.extraParam1)),
int128(int256(step.extraParam2)),
amountIn,
Expand Down Expand Up @@ -466,164 +472,4 @@ contract FraxswapRouterMultihop is ReentrancyGuard {
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}

/// ---------------------------
/// --------- Structs ---------
/// ---------------------------

/// @notice Input to the swap function
/// @dev contains the top level info for the swap
/// @param tokenIn input token address
/// @param amountIn input token amount
/// @param tokenOut output token address
/// @param amountOutMinimum output token minimum amount (reverts if lower)
/// @param recipient recipient of the output token
/// @param deadline deadline for this swap transaction (reverts if exceeded)
/// @param v v value for permit signature if supported
/// @param r r value for permit signature if supported
/// @param s s value for permit signature if supported
/// @param route byte encoded
struct FraxswapParams {
address tokenIn;
uint256 amountIn;
address tokenOut;
uint256 amountOutMinimum;
address recipient;
uint256 deadline;
bool approveMax;
uint8 v;
bytes32 r;
bytes32 s;
bytes route;
}

/// @notice A hop along the swap route
/// @dev a series of swaps (steps) containing the same input token and output token
/// @param tokenOut output token address
/// @param amountOut output token amount
/// @param percentOfHop amount of output tokens from the previous hop going into this hop
/// @param steps list of swaps from the same input token to the same output token
/// @param nextHops next hops from this one, the next hops' input token is the tokenOut
struct FraxswapRoute {
address tokenOut;
uint256 amountOut;
uint256 percentOfHop;
bytes[] steps;
bytes[] nextHops;
}

/// @notice A swap step in a specific route. routes can have multiple swap steps
/// @dev a single swap to a token from the token of the previous hop in the route
/// @param swapType type of the swap performed (Uniswap V2, Fraxswap,Curve, etc)
/// @param directFundNextPool 0 = funds go via router, 1 = fund are sent directly to pool
/// @param directFundThisPool 0 = funds go via router, 1 = fund are sent directly to pool
/// @param tokenOut the target token of the step. output token address
/// @param pool address of the pool to use in the step
/// @param extraParam1 extra data used in the step
/// @param extraParam2 extra data used in the step
/// @param percentOfHop percentage of all of the steps in this route (hop)
struct FraxswapStepData {
uint8 swapType;
uint8 directFundNextPool;
uint8 directFundThisPool;
address tokenOut;
address pool;
uint256 extraParam1;
uint256 extraParam2;
uint256 percentOfHop;
}

/// @notice struct for Uniswap V3 callback
/// @param tokenIn address of input token
/// @param directFundThisPool this pool already been funded
struct SwapCallbackData {
address tokenIn;
bool directFundThisPool;
}

/// ---------------------------
/// --------- Events ----------
/// ---------------------------

/// @notice Routing event emitted every swap
/// @param tokenIn address of the original input token
/// @param tokenOut address of the final output token
/// @param amountIn input amount for original input token
/// @param amountOut output amount for the final output token
event Routed(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut);
}

// Interface for ERC20 Permit
interface IERC20Permit is IERC20 {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}

// Interface used to call pool specific functions
interface PoolInterface {
// Fraxswap
function executeVirtualOrders(uint256 blockNumber) external;

function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256);

// Fraxlend
function deposit(uint256 userId, address userAddress) external returns (uint256 _sharesReceived);

// FrxETHMinter
function submitAndGive(address recipient) external payable;

// Curve
function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external;

function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256);

function exchange(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy,
address receiver
) external payable returns (uint256);

function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256);

function exchange_received(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy,
address receiver
) external returns (uint256);

// Saddle
function swap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy,
uint256 deadline
) external returns (uint256);

//FPI Mint/Redeem
function mintFPI(uint256 frax_in, uint256 min_fpi_out) external returns (uint256 fpi_out);

function redeemFPI(uint256 fpi_in, uint256 min_frax_out) external returns (uint256 frax_out);

// ERC4626 redeem
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);

// generic swap wrapper
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn,
address target
) external returns (uint256 amountOut);
}
18 changes: 18 additions & 0 deletions src/contracts/periphery/interfaces/IFraxswapRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

interface IFraxswapRouter is IUniswapV2Router02 {
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB);
function getAmountsOut(uint256 amountIn, address[] memory path) external view returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] memory path) external view returns (uint256[] memory amounts);
function getAmountsOutWithTwamm(
uint256 amountIn,
address[] memory path
) external returns (uint256[] memory amounts);
function getAmountsInWithTwamm(
uint256 amountOut,
address[] memory path
) external returns (uint256[] memory amounts);
}
Loading

0 comments on commit e5e34f1

Please sign in to comment.