Skip to content

Commit

Permalink
finalize uni v3 caller
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolev-igor committed Sep 5, 2024
1 parent 3bed6e7 commit 5a1fe92
Show file tree
Hide file tree
Showing 35 changed files with 1,228 additions and 803 deletions.
11 changes: 9 additions & 2 deletions .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"mark-callable-contracts": ["off"],
"reason-string": ["error", { "maxLength": 50 }],
"function-max-lines": ["error", 99],
"max-line-length": ["error", 99],
"max-line-length": ["off"],
"compiler-version": ["error", "0.8.12"],
"private-vars-leading-underscore": ["off"],
"const-name-snakecase": ["off"],
Expand All @@ -16,6 +16,13 @@
}
],
"prettier/prettier": "warn",
"comprehensive-interface": ["off"]
"comprehensive-interface": ["off"],
"foundry-test-functions": ["off"],
"gas-small-strings": ["off"],
"gas-increment-by-one": ["off"],
"func-named-parameters": ["off"],
"gas-custom-errors": ["off"],
"named-parameters-mapping": ["off"],
"gas-indexed-events": ["off"]
}
}
4 changes: 1 addition & 3 deletions contracts/callers/SimpleCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol";

import { ICaller } from "../interfaces/ICaller.sol";
import { Base } from "../shared/Base.sol";
import { ActionType } from "../shared/Enums.sol";
import { HighInputBalanceChange, ZeroTarget } from "../shared/Errors.sol";
import { AbsoluteTokenAmount } from "../shared/Structs.sol";
import { ZeroTarget } from "../shared/Errors.sol";
import { TokensHandler } from "../shared/TokensHandler.sol";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,14 @@ import { IUniswapV2Pair } from "../interfaces/IUniswapV2Pair.sol";
import { IWETH9 } from "../interfaces/IWETH9.sol";
import { Base } from "../shared/Base.sol";
import { SwapType } from "../shared/Enums.sol";
import {
BadToken,
InconsistentPairsAndDirectionsLengths,
InputSlippage,
LowReserve,
ZeroAmountIn,
ZeroAmountOut,
ZeroLength
} from "../shared/Errors.sol";
import { InconsistentPairsAndDirectionsLengths, InputSlippage, LowReserve, ZeroAmountIn, ZeroAmountOut, ZeroLength } from "../shared/Errors.sol";
import { TokensHandler } from "../shared/TokensHandler.sol";
import { Weth } from "../shared/Weth.sol";

/**
* @title Uniswap caller that executes swaps on UniswapV2-like pools
*/
contract UniswapCaller is ICaller, TokensHandler, Weth {
contract UniswapV2Caller is ICaller, TokensHandler, Weth {
address internal constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

/**
Expand Down Expand Up @@ -76,8 +68,9 @@ contract UniswapCaller is ICaller, TokensHandler, Weth {

uint256 length = pairs.length;
if (length == uint256(0)) revert ZeroLength();
if (directions.length != length)
if (directions.length != length) {
revert InconsistentPairsAndDirectionsLengths(length, directions.length);
}

uint256[] memory amounts = (swapType == SwapType.FixedInputs)
? getAmountsOut(fixedSideAmount, pairs, directions)
Expand Down Expand Up @@ -252,11 +245,10 @@ contract UniswapCaller is ICaller, TokensHandler, Weth {
* @return reserveIn Pool reserve for input token
* @return reserveOut Pool reserve for output token
*/
function getReserves(address pair, bool direction)
internal
view
returns (uint256 reserveIn, uint256 reserveOut)
{
function getReserves(
address pair,
bool direction
) internal view returns (uint256 reserveIn, uint256 reserveOut) {
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pair).getReserves();
(reserveIn, reserveOut) = direction ? (reserve0, reserve1) : (reserve1, reserve0);

Expand Down
110 changes: 49 additions & 61 deletions contracts/callers/UniswapV3Caller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,41 @@
pragma solidity 0.8.12;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { IWETH9 } from "../interfaces/IWETH9.sol";
import { Base } from "../shared/Base.sol";
import { TokensHandler } from "../shared/TokensHandler.sol";
import { Weth } from "../shared/Weth.sol";
// solhint-disable-next-line
import { CallbackValidation } from "./helpers/uniswap/CallbackValidation.sol";
import { TickMath } from "./helpers/uniswap/TickMath.sol";
// solhint-disable-next-line
import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";
import { Path } from "./helpers/uniswap/Path.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";

contract UniswapV3Caller is TokensHandler, Weth {
using Path for bytes;
import { IWETH9 } from "./../interfaces/IWETH9.sol";
import { Base } from "./../shared/Base.sol";
import { TokensHandler } from "./../shared/TokensHandler.sol";
import { Weth } from "./../shared/Weth.sol";

contract UniswapV3Caller is TokensHandler, Weth, IUniswapV3SwapCallback {
address internal constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

struct SwapCallbackData {
bytes path;
address payer;
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

/**
* @notice Sets Wrapped Ether address for the current chain
* @param weth Wrapped Ether address
*/
constructor(address weth) Weth(weth) {
// solhint-disable-previous-line no-empty-blocks
}

constructor(address _weth) Weth(_weth) {}

function callBytes(bytes calldata callerCallData) external {
(
address inputToken,
address outputToken,
address pool,
bool direction,
uint256 fixedSideAmount,
bool isExactInput
) = abi.decode(callerCallData, (address, address, address, uint256, bool));

if (inputToken == ETH) {
depositEth(fixedSideAmount);
}
) = abi.decode(callerCallData, (address, address, address, bool, uint256, bool));

if (isExactInput) {
exactInputSwap(inputToken, outputToken, pool, fixedSideAmount);
exactInputSwap(inputToken, pool, direction, fixedSideAmount);
} else {
exactOutputSwap(inputToken, outputToken, pool, fixedSideAmount);
exactOutputSwap(inputToken, pool, direction, fixedSideAmount);
}

// Unwrap weth if necessary
Expand All @@ -56,61 +49,56 @@ contract UniswapV3Caller is TokensHandler, Weth {
Base.transfer(outputToken, msg.sender, Base.getBalance(outputToken));
}

function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external {
address inputToken = abi.decode(data, (address));

if (amount0Delta > 0) {
if (inputToken == ETH) {
depositEth(uint256(amount0Delta));
}
Base.transfer(IUniswapV3Pool(msg.sender).token0(), msg.sender, uint256(amount0Delta));
} else {
if (inputToken == ETH) {
depositEth(uint256(amount1Delta));
}
Base.transfer(IUniswapV3Pool(msg.sender).token1(), msg.sender, uint256(amount1Delta));
}
}

function exactInputSwap(
address inputToken,
address outputToken,
address pool,
bool direction,
uint256 amountIn
) internal {
IUniswapV3Pool v3Pool = IUniswapV3Pool(pool);

SafeERC20.safeApprove(IERC20(inputToken), pool, amountIn);

(int256 amount0, int256 amount1) = v3Pool.swap(
IUniswapV3Pool(pool).swap(
address(this),
inputToken < outputToken,
direction,
int256(amountIn),
inputToken < outputToken ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(inputToken, outputToken)
direction ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,
abi.encode(inputToken)
);
}

function exactOutputSwap(
address inputToken,
address outputToken,
address pool,
bool direction,
uint256 amountOut
) internal {
IUniswapV3Pool v3Pool = IUniswapV3Pool(pool);

(int256 amount0, int256 amount1) = v3Pool.swap(
IUniswapV3Pool(pool).swap(
address(this),
inputToken < outputToken,
direction,
-int256(amountOut),
inputToken < outputToken ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(inputToken, outputToken)
direction ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,
abi.encode(inputToken)
);
}

function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external {
SwapCallbackData memory callbackData = abi.decode(data, (SwapCallbackData));
(address tokenIn, address tokenOut, ) = callbackData.path.decodeFirstPool();

(bool isExactInput, uint256 amountToPay) = amount0Delta > 0
? (tokenIn < tokenOut, uint256(amount0Delta))
: (tokenOut < tokenIn, uint256(amount1Delta));

if (isExactInput) {
Base.transfer(tokenIn, msg.sender, amountToPay);
} else {
Base.transfer(tokenOut, msg.sender, amountToPay);
}
}

function depositEth(uint256 amount) internal {
IWETH9(getWeth()).deposit{ value: amount }();
}
Expand Down
104 changes: 0 additions & 104 deletions contracts/callers/helpers/uniswap/BytesLib.sol

This file was deleted.

35 changes: 0 additions & 35 deletions contracts/callers/helpers/uniswap/CallbackValidation.sol

This file was deleted.

Loading

0 comments on commit 5a1fe92

Please sign in to comment.