diff --git a/.solhint.json b/.solhint.json index 23c48a2c..d49667ba 100755 --- a/.solhint.json +++ b/.solhint.json @@ -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"], @@ -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"] } } diff --git a/contracts/callers/SimpleCaller.sol b/contracts/callers/SimpleCaller.sol index 7c4c9568..b9a5362f 100644 --- a/contracts/callers/SimpleCaller.sol +++ b/contracts/callers/SimpleCaller.sol @@ -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"; /** diff --git a/contracts/callers/UniswapCaller.sol b/contracts/callers/UniswapV2Caller.sol similarity index 95% rename from contracts/callers/UniswapCaller.sol rename to contracts/callers/UniswapV2Caller.sol index 2db66d8b..bb60de2c 100644 --- a/contracts/callers/UniswapCaller.sol +++ b/contracts/callers/UniswapV2Caller.sol @@ -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; /** @@ -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) @@ -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); diff --git a/contracts/callers/UniswapV3Caller.sol b/contracts/callers/UniswapV3Caller.sol index 1ef34ccf..28afc90f 100644 --- a/contracts/callers/UniswapV3Caller.sol +++ b/contracts/callers/UniswapV3Caller.sol @@ -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 @@ -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 }(); } diff --git a/contracts/callers/helpers/uniswap/BytesLib.sol b/contracts/callers/helpers/uniswap/BytesLib.sol deleted file mode 100644 index bb163e76..00000000 --- a/contracts/callers/helpers/uniswap/BytesLib.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * @title Solidity Bytes Arrays Utils - * @author Gonçalo Sá - * - * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. - * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. - */ -pragma solidity 0.8.12; - -library BytesLib { - function slice( - bytes memory _bytes, - uint256 _start, - uint256 _length - ) internal pure returns (bytes memory) { - require(_length + 31 >= _length, "slice_overflow"); - require(_start + _length >= _start, "slice_overflow"); - require(_bytes.length >= _start + _length, "slice_outOfBounds"); - - bytes memory tempBytes; - - assembly { - switch iszero(_length) - case 0 { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // The first word of the slice result is potentially a partial - // word read from the original array. To read it, we calculate - // the length of that partial word and start copying that many - // bytes into the array. The first word we copy will start with - // data we don't care about, but the last `lengthmod` bytes will - // land at the beginning of the contents of the new array. When - // we're done copying, we overwrite the full first word with - // the actual length of the slice. - let lengthmod := and(_length, 31) - - // The multiplication in the next line is necessary - // because when slicing multiples of 32 bytes (lengthmod == 0) - // the following copy loop was copying the origin's length - // and then ending prematurely not copying everything it should. - let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) - let end := add(mc, _length) - - for { - // The multiplication in the next line has the same exact purpose - // as the one above. - let cc := add( - add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), - _start - ) - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - mstore(mc, mload(cc)) - } - - mstore(tempBytes, _length) - - //update free-memory pointer - //allocating the array padded to 32 bytes like the compiler does now - mstore(0x40, and(add(mc, 31), not(31))) - } - //if we want a zero-length slice let's just return a zero-length array - default { - tempBytes := mload(0x40) - //zero out the 32 bytes slice we are about to return - //we need to do it because Solidity does not garbage collect - mstore(tempBytes, 0) - - mstore(0x40, add(tempBytes, 0x20)) - } - } - - return tempBytes; - } - - function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { - require(_start + 20 >= _start, "toAddress_overflow"); - require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); - address tempAddress; - - assembly { - tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) - } - - return tempAddress; - } - - function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { - require(_start + 3 >= _start, "toUint24_overflow"); - require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); - uint24 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x3), _start)) - } - - return tempUint; - } -} diff --git a/contracts/callers/helpers/uniswap/CallbackValidation.sol b/contracts/callers/helpers/uniswap/CallbackValidation.sol deleted file mode 100644 index 9b1d8b8e..00000000 --- a/contracts/callers/helpers/uniswap/CallbackValidation.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.12; - -import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import "./PoolAddress.sol"; - -/// @notice Provides validation for callbacks from Uniswap V3 Pools -library CallbackValidation { - /// @notice Returns the address of a valid Uniswap V3 Pool - /// @param factory The contract address of the Uniswap V3 factory - /// @param tokenA The contract address of either token0 or token1 - /// @param tokenB The contract address of the other token - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @return pool The V3 pool contract address - function verifyCallback( - address factory, - address tokenA, - address tokenB, - uint24 fee - ) internal view returns (IUniswapV3Pool pool) { - return verifyCallback(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee)); - } - - /// @notice Returns the address of a valid Uniswap V3 Pool - /// @param factory The contract address of the Uniswap V3 factory - /// @param poolKey The identifying key of the V3 pool - /// @return pool The V3 pool contract address - function verifyCallback( - address factory, - PoolAddress.PoolKey memory poolKey - ) internal view returns (IUniswapV3Pool pool) { - pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); - require(msg.sender == address(pool)); - } -} diff --git a/contracts/callers/helpers/uniswap/Path.sol b/contracts/callers/helpers/uniswap/Path.sol deleted file mode 100644 index c058c437..00000000 --- a/contracts/callers/helpers/uniswap/Path.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.12; - -import "./BytesLib.sol"; - -/// @title Functions for manipulating path data for multihop swaps -library Path { - using BytesLib for bytes; - - /// @dev The length of the bytes encoded address - uint256 private constant ADDR_SIZE = 20; - /// @dev The length of the bytes encoded fee - uint256 private constant FEE_SIZE = 3; - - /// @dev The offset of a single token address and pool fee - uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; - - /// @notice Decodes the first pool in path - /// @param path The bytes encoded swap path - /// @return tokenA The first token of the given pool - /// @return tokenB The second token of the given pool - /// @return fee The fee level of the pool - function decodeFirstPool( - bytes memory path - ) internal pure returns (address tokenA, address tokenB, uint24 fee) { - tokenA = path.toAddress(0); - fee = path.toUint24(ADDR_SIZE); - tokenB = path.toAddress(NEXT_OFFSET); - } -} diff --git a/contracts/callers/helpers/uniswap/PoolAddress.sol b/contracts/callers/helpers/uniswap/PoolAddress.sol deleted file mode 100644 index ff542b0f..00000000 --- a/contracts/callers/helpers/uniswap/PoolAddress.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.12; - -/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee -library PoolAddress { - bytes32 internal constant POOL_INIT_CODE_HASH = - 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; - - /// @notice The identifying key of the pool - struct PoolKey { - address token0; - address token1; - uint24 fee; - } - - /// @notice Returns PoolKey: the ordered tokens with the matched fee levels - /// @param tokenA The first token of a pool, unsorted - /// @param tokenB The second token of a pool, unsorted - /// @param fee The fee level of the pool - /// @return Poolkey The pool details with ordered token0 and token1 assignments - function getPoolKey( - address tokenA, - address tokenB, - uint24 fee - ) internal pure returns (PoolKey memory) { - if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); - return PoolKey({ token0: tokenA, token1: tokenB, fee: fee }); - } - - /// @notice Deterministically computes the pool address given the factory and PoolKey - /// @param factory The Uniswap V3 factory contract address - /// @param key The PoolKey - /// @return pool The contract address of the V3 pool - function computeAddress( - address factory, - PoolKey memory key - ) internal pure returns (address pool) { - require(key.token0 < key.token1); - pool = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - hex"ff", - factory, - keccak256(abi.encode(key.token0, key.token1, key.fee)), - POOL_INIT_CODE_HASH - ) - ) - ) - ) - ); - } -} diff --git a/contracts/callers/helpers/uniswap/TickMath.sol b/contracts/callers/helpers/uniswap/TickMath.sol deleted file mode 100644 index 9d0d5f5b..00000000 --- a/contracts/callers/helpers/uniswap/TickMath.sol +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.12; - -/// @title Math library for computing sqrt prices from ticks and vice versa -/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports -/// prices between 2**-128 and 2**128 -library TickMath { - /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 - int24 internal constant MIN_TICK = -887272; - /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 - int24 internal constant MAX_TICK = -MIN_TICK; - - /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) - uint160 internal constant MIN_SQRT_RATIO = 4295128739; - /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) - uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; - - /// @notice Calculates sqrt(1.0001^tick) * 2^96 - /// @dev Throws if |tick| > max tick - /// @param tick The input tick for the above formula - /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) - /// at the given tick - function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { - uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); - require(absTick <= uint256(uint24(MAX_TICK)), "T"); - - uint256 ratio = absTick & 0x1 != 0 - ? 0xfffcb933bd6fad37aa2d162d1a594001 - : 0x100000000000000000000000000000000; - if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; - if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; - if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; - if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; - if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; - if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; - if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; - if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; - if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; - if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; - if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; - if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; - if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; - if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; - if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; - if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; - if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; - if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; - if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; - - if (tick > 0) ratio = type(uint256).max / ratio; - - // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. - // we then downcast because we know the result always fits within 160 bits due to our tick input constraint - // we round up in the division so getTickAtSqrtRatio of the output price is always consistent - sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); - } - - /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio - /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may - /// ever return. - /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 - /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio - function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { - // second inequality must be < because the price can never reach the price at the max tick - require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R"); - uint256 ratio = uint256(sqrtPriceX96) << 32; - - uint256 r = ratio; - uint256 msb = 0; - - assembly { - let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(5, gt(r, 0xFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(4, gt(r, 0xFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(3, gt(r, 0xFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(2, gt(r, 0xF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(1, gt(r, 0x3)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := gt(r, 0x1) - msb := or(msb, f) - } - - if (msb >= 128) r = ratio >> (msb - 127); - else r = ratio << (127 - msb); - - int256 log_2 = (int256(msb) - 128) << 64; - - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(63, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(62, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(61, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(60, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(59, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(58, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(57, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(56, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(55, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(54, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(53, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(52, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(51, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(50, f)) - } - - int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number - - int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); - int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); - - tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 - ? tickHi - : tickLow; - } -} diff --git a/contracts/interfaces/ICaller.sol b/contracts/interfaces/ICaller.sol index 39e6afc1..414e3e28 100644 --- a/contracts/interfaces/ICaller.sol +++ b/contracts/interfaces/ICaller.sol @@ -17,8 +17,6 @@ pragma solidity 0.8.12; -import { AbsoluteTokenAmount } from "../shared/Structs.sol"; - import { ITokensHandler } from "./ITokensHandler.sol"; interface ICaller is ITokensHandler { diff --git a/contracts/interfaces/IDAIPermit.sol b/contracts/interfaces/IDAIPermit.sol index f146f886..2ab20a8c 100644 --- a/contracts/interfaces/IDAIPermit.sol +++ b/contracts/interfaces/IDAIPermit.sol @@ -31,5 +31,5 @@ interface IDAIPermit is IERC20 { bytes32 s ) external; - function nonces(address holder) external view returns (uint256); + function nonces(address holder) external view returns (uint256 nonce); } diff --git a/contracts/interfaces/IEIP2612.sol b/contracts/interfaces/IEIP2612.sol index b74e17f2..35b0729d 100644 --- a/contracts/interfaces/IEIP2612.sol +++ b/contracts/interfaces/IEIP2612.sol @@ -30,5 +30,5 @@ interface IEIP2612 is IERC20 { bytes32 s ) external; - function nonces(address holder) external view returns (uint256); + function nonces(address holder) external view returns (uint256 nonce); } diff --git a/contracts/interfaces/IRouter.sol b/contracts/interfaces/IRouter.sol index 513e81bb..d9d33a57 100644 --- a/contracts/interfaces/IRouter.sol +++ b/contracts/interfaces/IRouter.sol @@ -17,17 +17,9 @@ pragma solidity 0.8.12; -import { - AbsoluteTokenAmount, - Input, - SwapDescription, - AccountSignature, - ProtocolFeeSignature, - Fee -} from "../shared/Structs.sol"; - -import { ITokensHandler } from "./ITokensHandler.sol"; +import { AbsoluteTokenAmount, Input, SwapDescription, AccountSignature, ProtocolFeeSignature } from "../shared/Structs.sol"; import { ISignatureVerifier } from "./ISignatureVerifier.sol"; +import { ITokensHandler } from "./ITokensHandler.sol"; interface IRouter is ITokensHandler, ISignatureVerifier { /** @@ -89,7 +81,9 @@ interface IRouter is ITokensHandler, ISignatureVerifier { SwapDescription calldata swapDescription, AccountSignature calldata accountSignature, ProtocolFeeSignature calldata protocolFeeSignature - ) external payable + ) + external + payable returns ( uint256 inputBalanceChange, uint256 actualOutputAmount, diff --git a/contracts/interfaces/ISignatureVerifier.sol b/contracts/interfaces/ISignatureVerifier.sol index e5c0e7ed..a379bb54 100644 --- a/contracts/interfaces/ISignatureVerifier.sol +++ b/contracts/interfaces/ISignatureVerifier.sol @@ -17,9 +17,6 @@ pragma solidity 0.8.12; -import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - import { AbsoluteTokenAmount, Input, SwapDescription } from "../shared/Structs.sol"; interface ISignatureVerifier { diff --git a/contracts/interfaces/IUniswapV2Pair.sol b/contracts/interfaces/IUniswapV2Pair.sol index 00496ef5..3b8fb277 100644 --- a/contracts/interfaces/IUniswapV2Pair.sol +++ b/contracts/interfaces/IUniswapV2Pair.sol @@ -23,27 +23,23 @@ pragma solidity 0.8.12; * github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol. */ interface IUniswapV2Pair { - function mint(address) external returns (uint256); + function mint(address to) external returns (uint256 liquidity); - function burn(address) external returns (uint256, uint256); + function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( - uint256, - uint256, - address, - bytes calldata + uint256 amount0Out, + uint256 amount1Out, + address to, + bytes calldata data ) external; function getReserves() external view - returns ( - uint112, - uint112, - uint32 - ); + returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); - function token0() external view returns (address); + function token0() external view returns (address token0); - function token1() external view returns (address); + function token1() external view returns (address token1); } diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol index 0796174f..1b18cf07 100644 --- a/contracts/interfaces/IUniswapV2Router02.sol +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -24,9 +24,9 @@ pragma solidity 0.8.12; */ interface IUniswapV2Router02 { function swapExactETHForTokens( - uint256, - address[] calldata, - address, - uint256 + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline ) external payable; } diff --git a/contracts/interfaces/IWETH9.sol b/contracts/interfaces/IWETH9.sol index b4761ddf..75eb5a38 100644 --- a/contracts/interfaces/IWETH9.sol +++ b/contracts/interfaces/IWETH9.sol @@ -26,7 +26,7 @@ pragma solidity 0.8.12; interface IWETH9 { function deposit() external payable; - function withdraw(uint256) external; + function withdraw(uint256 amount) external; - function balanceOf(address) external view returns (uint256); + function balanceOf(address account) external view returns (uint256 balance); } diff --git a/contracts/interfaces/IYearnPermit.sol b/contracts/interfaces/IYearnPermit.sol index 3bfcebbd..3afc6c31 100644 --- a/contracts/interfaces/IYearnPermit.sol +++ b/contracts/interfaces/IYearnPermit.sol @@ -20,11 +20,5 @@ pragma solidity 0.8.12; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IYearnPermit is IERC20 { - function permit( - address, - address, - uint256, - uint256, - bytes calldata - ) external; + function permit(address, address, uint256, uint256, bytes calldata) external; } diff --git a/contracts/router/Router.sol b/contracts/router/Router.sol index 6164c08d..71afed55 100644 --- a/contracts/router/Router.sol +++ b/contracts/router/Router.sol @@ -18,8 +18,8 @@ pragma solidity 0.8.12; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; @@ -30,39 +30,10 @@ import { IRouter } from "../interfaces/IRouter.sol"; import { IYearnPermit } from "../interfaces/IYearnPermit.sol"; import { Base } from "../shared/Base.sol"; import { AmountType, PermitType, SwapType } from "../shared/Enums.sol"; -import { - BadAmount, - BadAccount, - BadAccountSignature, - BadAmountType, - BadFeeAmount, - BadFeeBeneficiary, - BadFeeShare, - BadFeeSignature, - ExceedingDelimiterAmount, - ExceedingLimitFee, - HighInputBalanceChange, - InsufficientAllowance, - InsufficientMsgValue, - LowActualOutputAmount, - NoneAmountType, - NonePermitType, - NoneSwapType, - PassedDeadline -} from "../shared/Errors.sol"; +import { BadAmount, BadAccount, BadAccountSignature, BadAmountType, BadFeeAmount, BadFeeBeneficiary, BadFeeShare, BadFeeSignature, ExceedingDelimiterAmount, ExceedingLimitFee, HighInputBalanceChange, InsufficientAllowance, InsufficientMsgValue, LowActualOutputAmount, NoneAmountType, NonePermitType, NoneSwapType, PassedDeadline } from "../shared/Errors.sol"; import { Ownable } from "../shared/Ownable.sol"; -import { - AbsoluteTokenAmount, - AccountSignature, - Fee, - ProtocolFeeSignature, - Input, - Permit, - SwapDescription, - TokenAmount -} from "../shared/Structs.sol"; +import { AbsoluteTokenAmount, AccountSignature, Fee, ProtocolFeeSignature, Input, Permit, SwapDescription, TokenAmount } from "../shared/Structs.sol"; import { TokensHandler } from "../shared/TokensHandler.sol"; - import { ProtocolFee } from "./ProtocolFee.sol"; import { SignatureVerifier } from "./SignatureVerifier.sol"; @@ -86,8 +57,9 @@ contract Router is SwapDescription calldata swapDescription, AccountSignature calldata accountSignature ) external override nonReentrant { - if (msg.sender != swapDescription.account) + if (msg.sender != swapDescription.account) { revert BadAccount(msg.sender, swapDescription.account); + } validateAndExpireAccountSignature(input, output, swapDescription, accountSignature); } @@ -102,7 +74,10 @@ contract Router is AccountSignature calldata accountSignature, ProtocolFeeSignature calldata protocolFeeSignature ) - external payable override nonReentrant + external + payable + override + nonReentrant returns ( uint256 inputBalanceChange, uint256 actualOutputAmount, @@ -164,8 +139,9 @@ contract Router is uint256 outputBalanceChange = Base.getBalance(output.token) - initialOutputBalance; // Check input requirements, prevent the underflow - if (inputBalanceChange > absoluteInputAmount) + if (inputBalanceChange > absoluteInputAmount) { revert HighInputBalanceChange(inputBalanceChange, absoluteInputAmount); + } // Calculate the refund amount uint256 refundAmount = absoluteInputAmount - inputBalanceChange; @@ -180,8 +156,9 @@ contract Router is ); // Check output requirements, prevent revert on transfers - if (actualOutputAmount < output.absoluteAmount) + if (actualOutputAmount < output.absoluteAmount) { revert LowActualOutputAmount(actualOutputAmount, output.absoluteAmount); + } // Transfer the refund back to the user, // do nothing in zero input token case as `refundAmount` is zero @@ -267,8 +244,9 @@ contract Router is ) internal { uint256 allowance = IERC20(token).allowance(account, address(this)); if (allowance < amount) { - if (permit.permitCallData.length == uint256(0)) + if (permit.permitCallData.length == uint256(0)) { revert InsufficientAllowance(allowance, amount); + } Address.functionCall( token, @@ -327,8 +305,9 @@ contract Router is AccountSignature calldata accountSignature ) internal { if (accountSignature.signature.length == uint256(0)) { - if (msg.sender != swapDescription.account) + if (msg.sender != swapDescription.account) { revert BadAccount(msg.sender, swapDescription.account); + } return; } bytes32 hashedAccountSignatureData = hashAccountSignatureData( @@ -366,15 +345,18 @@ contract Router is Fee memory protocolFee = swapDescription.protocolFee; if (protocolFeeSignature.signature.length == uint256(0)) { - if (protocolFee.share != baseProtocolFee.share) + if (protocolFee.share != baseProtocolFee.share) { revert BadFeeShare(protocolFee.share, baseProtocolFee.share); - if (protocolFee.beneficiary != baseProtocolFee.beneficiary) + } + if (protocolFee.beneficiary != baseProtocolFee.beneficiary) { revert BadFeeBeneficiary(protocolFee.beneficiary, baseProtocolFee.beneficiary); + } return; } - if (protocolFee.share > baseProtocolFee.share) + if (protocolFee.share > baseProtocolFee.share) { revert ExceedingLimitFee(protocolFee.share, baseProtocolFee.share); + } bytes32 hashedProtocolFeeSignatureData = hashProtocolFeeSignatureData( input, @@ -392,8 +374,9 @@ contract Router is ) revert BadFeeSignature(); // solhint-disable not-rely-on-time - if (block.timestamp > protocolFeeSignature.deadline) + if (block.timestamp > protocolFeeSignature.deadline) { revert PassedDeadline(block.timestamp, protocolFeeSignature.deadline); + } // solhint-enable not-rely-on-time } @@ -405,11 +388,10 @@ contract Router is * @param account Address of the account to transfer token from * @return absoluteTokenAmount Absolute token amount */ - function getAbsoluteInputAmount(TokenAmount calldata tokenAmount, address account) - internal - view - returns (uint256 absoluteTokenAmount) - { + function getAbsoluteInputAmount( + TokenAmount calldata tokenAmount, + address account + ) internal view returns (uint256 absoluteTokenAmount) { AmountType amountType = tokenAmount.amountType; address token = tokenAmount.token; uint256 amount = tokenAmount.amount; @@ -420,8 +402,9 @@ contract Router is if (amountType == AmountType.Absolute) return amount; - if (token == ETH || token == address(0)) + if (token == ETH || token == address(0)) { revert BadAmountType(amountType, AmountType.Absolute); + } if (amount > DELIMITER) revert ExceedingDelimiterAmount(amount); @@ -456,18 +439,15 @@ contract Router is ) internal pure - returns ( - uint256 returnedAmount, - uint256 protocolFeeAmount, - uint256 marketplaceFeeAmount - ) + returns (uint256 returnedAmount, uint256 protocolFeeAmount, uint256 marketplaceFeeAmount) { if (swapType == SwapType.None) revert NoneSwapType(); uint256 outputAbsoluteAmount = output.absoluteAmount; if (output.token == address(0)) { - if (outputAbsoluteAmount > uint256(0)) + if (outputAbsoluteAmount > uint256(0)) { revert BadAmount(outputAbsoluteAmount, uint256(0)); + } return (uint256(0), uint256(0), uint256(0)); } @@ -490,8 +470,9 @@ contract Router is uint256 totalFeeAmount = outputBalanceChange - returnedAmount; // This check is important in fixed outputs case as we never actually check that // total fee amount is not too large and should always just pass in fixed inputs case - if (totalFeeAmount * DELIMITER > totalFeeShare * returnedAmount) + if (totalFeeAmount * DELIMITER > totalFeeShare * returnedAmount) { revert BadFeeAmount(totalFeeAmount, (returnedAmount * totalFeeShare) / DELIMITER); + } protocolFeeAmount = (totalFeeAmount * protocolFee.share) / totalFeeShare; marketplaceFeeAmount = totalFeeAmount - protocolFeeAmount; diff --git a/contracts/router/SignatureVerifier.sol b/contracts/router/SignatureVerifier.sol index 6d1695dc..dbdc01cf 100644 --- a/contracts/router/SignatureVerifier.sol +++ b/contracts/router/SignatureVerifier.sol @@ -21,16 +21,7 @@ import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/draft-EIP712. import { ISignatureVerifier } from "../interfaces/ISignatureVerifier.sol"; import { UsedHash } from "../shared/Errors.sol"; -import { - AbsoluteTokenAmount, - AccountSignature, - ProtocolFeeSignature, - Fee, - Input, - Permit, - SwapDescription, - TokenAmount -} from "../shared/Structs.sol"; +import { AbsoluteTokenAmount, Fee, Input, Permit, SwapDescription, TokenAmount } from "../shared/Structs.sol"; contract SignatureVerifier is ISignatureVerifier, EIP712 { mapping(bytes32 => bool) private isHashUsed_; @@ -175,7 +166,7 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { * @param output Outut described in `hashDada()` function * @param swapDescription Swap parameters described in `hashDada()` function * @param saltOrDeadline Salt/deadline parameter preventing double-spending - * @return `execute()` function data hashed + * @return hashedData `execute()` function data hashed */ function hash( bytes32 typehash, @@ -183,7 +174,7 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { AbsoluteTokenAmount memory output, SwapDescription memory swapDescription, uint256 saltOrDeadline - ) internal pure returns (bytes32) { + ) internal pure returns (bytes32 hashedData) { return keccak256( abi.encode( @@ -198,17 +189,19 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { /** * @param input Input struct to be hashed - * @return Hashed Input structs array + * @return hashedInput Hashed Input structs array */ - function hash(Input memory input) internal pure returns (bytes32) { + function hash(Input memory input) internal pure returns (bytes32 hashedInput) { return keccak256(abi.encode(INPUT_TYPEHASH, hash(input.tokenAmount), hash(input.permit))); } /** * @param tokenAmount TokenAmount struct to be hashed - * @return Hashed TokenAmount struct + * @return hashedTokenAmount Hashed TokenAmount struct */ - function hash(TokenAmount memory tokenAmount) internal pure returns (bytes32) { + function hash( + TokenAmount memory tokenAmount + ) internal pure returns (bytes32 hashedTokenAmount) { return keccak256( abi.encode( @@ -222,9 +215,9 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { /** * @param permit Permit struct to be hashed - * @return Hashed Permit struct + * @return hashedPermit Hashed Permit struct */ - function hash(Permit memory permit) internal pure returns (bytes32) { + function hash(Permit memory permit) internal pure returns (bytes32 hashedPermit) { return keccak256( abi.encode( @@ -237,9 +230,11 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { /** * @param absoluteTokenAmount AbsoluteTokenAmount struct to be hashed - * @return Hashed AbsoluteTokenAmount struct + * @return hashedAbsoluteTokenAmount Hashed AbsoluteTokenAmount struct */ - function hash(AbsoluteTokenAmount memory absoluteTokenAmount) internal pure returns (bytes32) { + function hash( + AbsoluteTokenAmount memory absoluteTokenAmount + ) internal pure returns (bytes32 hashedAbsoluteTokenAmount) { return keccak256( abi.encode( @@ -252,9 +247,11 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { /** * @param swapDescription SwapDescription struct to be hashed - * @return Hashed SwapDescription struct + * @return hashedSwapDescription Hashed SwapDescription struct */ - function hash(SwapDescription memory swapDescription) internal pure returns (bytes32) { + function hash( + SwapDescription memory swapDescription + ) internal pure returns (bytes32 hashedSwapDescription) { return keccak256( abi.encode( @@ -271,9 +268,9 @@ contract SignatureVerifier is ISignatureVerifier, EIP712 { /** * @param fee Fee struct to be hashed - * @return Hashed Fee struct + * @return hashedFee Hashed Fee struct */ - function hash(Fee memory fee) internal pure returns (bytes32) { + function hash(Fee memory fee) internal pure returns (bytes32 hashedFee) { return keccak256(abi.encode(FEE_TYPEHASH, fee.share, fee.beneficiary)); } } diff --git a/contracts/shared/Base.sol b/contracts/shared/Base.sol index ef0049cb..306e0aa9 100644 --- a/contracts/shared/Base.sol +++ b/contracts/shared/Base.sol @@ -17,11 +17,11 @@ pragma solidity 0.8.12; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { FailedEtherTransfer, ZeroReceiver } from "./Errors.sol"; +import { ZeroReceiver } from "./Errors.sol"; /** * @title Library unifying transfer, approval, and getting balance for ERC20 tokens and Ether @@ -38,11 +38,7 @@ library Base { * @dev Reverts on zero `receiver`, does nothing for zero amount * @dev Should not be used with zero token address */ - function transfer( - address token, - address receiver, - uint256 amount - ) internal { + function transfer(address token, address receiver, uint256 amount) internal { if (amount == uint256(0)) return; if (receiver == address(0)) revert ZeroReceiver(); @@ -60,11 +56,7 @@ library Base { * @param amount Tokens amount to be approved * @dev Should not be used with zero or `ETH` token address */ - function safeApproveMax( - address token, - address spender, - uint256 amount - ) internal { + function safeApproveMax(address token, address spender, uint256 amount) internal { uint256 allowance = IERC20(token).allowance(address(this), spender); if (allowance < amount) { if (allowance > uint256(0)) { @@ -80,7 +72,7 @@ library Base { * @param account Address of the account * @dev Should not be used with zero token address */ - function getBalance(address token, address account) internal view returns (uint256) { + function getBalance(address token, address account) internal view returns (uint256 balance) { if (token == ETH) return account.balance; return IERC20(token).balanceOf(account); @@ -91,7 +83,7 @@ library Base { * @param token Address of the token * @dev Returns `0` for zero token address in order to handle empty token case */ - function getBalance(address token) internal view returns (uint256) { + function getBalance(address token) internal view returns (uint256 balance) { if (token == address(0)) return uint256(0); return Base.getBalance(token, address(this)); diff --git a/contracts/shared/Errors.sol b/contracts/shared/Errors.sol index a347d7ab..b9731b00 100644 --- a/contracts/shared/Errors.sol +++ b/contracts/shared/Errors.sol @@ -17,7 +17,7 @@ pragma solidity 0.8.12; -import { ActionType, AmountType, PermitType, SwapType } from "./Enums.sol"; +import { AmountType } from "./Enums.sol"; import { Fee } from "./Structs.sol"; error BadAccount(address account, address expectedAccount); diff --git a/contracts/shared/Weth.sol b/contracts/shared/Weth.sol index eddfc63d..dc170715 100644 --- a/contracts/shared/Weth.sol +++ b/contracts/shared/Weth.sol @@ -21,14 +21,14 @@ pragma solidity 0.8.12; * @title Abstract contract storing Wrapped Ether address for the current chain */ abstract contract Weth { - address private immutable weth_; + address private immutable WETH; /** * @notice Sets Wrapped Ether address for the current chain * @param weth Wrapped Ether address */ constructor(address weth) { - weth_ = weth; + WETH = weth; } /** @@ -36,6 +36,6 @@ abstract contract Weth { * @return weth Wrapped Ether address */ function getWeth() public view returns (address weth) { - return weth_; + return WETH; } } diff --git a/hardhat.config.ts b/hardhat.config.ts index aec6e004..c7bc2a34 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -103,7 +103,8 @@ const config: HardhatUserConfig = { blast: process.env.BLAST_API_KEY ? process.env.BLAST_API_KEY.toString() : '', bsc: process.env.BSC_API_KEY ? process.env.BSC_API_KEY.toString() : '', celo: process.env.CELO_API_KEY ? process.env.CELO_API_KEY.toString() : '', - gnosis: process.env.GNOSIS_API_KEY ? process.env.GNOSIS_API_KEY.toString() : '', + xdai: process.env.GNOSIS_API_KEY ? process.env.GNOSIS_API_KEY.toString() : '', + linea: process.env.LINEA_API_KEY ? process.env.LINEA_API_KEY.toString() : '', mainnet: process.env.ETHEREUM_API_KEY ? process.env.ETHEREUM_API_KEY.toString() : '', opera: process.env.FANTOM_API_KEY ? process.env.FANTOM_API_KEY.toString() : '', optimisticEthereum: process.env.OPTIMISM_API_KEY ? process.env.OPTIMISM_API_KEY.toString() : '', diff --git a/package-lock.json b/package-lock.json index ccd2dc24..f5d6887a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,11 +45,11 @@ "lint-staged": "11.0.0", "mrm": "3.0.10", "pinst": "2.1.6", - "prettier": "2.8.8", + "prettier": "3.3.3", "prettier-plugin-solidity": "1.1.3", "regenerator-runtime": "0.13.11", - "solhint": "3.4.1", - "solhint-plugin-prettier": "0.0.5", + "solhint": "5.0.3", + "solhint-plugin-prettier": "0.1.0", "solidity-coverage": "0.8.4", "truffle": "5.11.1", "ts-node": "10.9.1", @@ -4721,6 +4721,53 @@ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.0.tgz", "integrity": "sha512-dlKiZmDvJnGRLHojrDoFZJmsQVeltVeoiRN7RK+cf2FmkhASDEblE0RiaYdxPNsUZa6mRG8393b9bfyp+V5IAw==" }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@prettier/sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.3.0.tgz", + "integrity": "sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==", + "dev": true, + "funding": { + "url": "https://github.com/prettier/prettier-synchronized?sponsor=1" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -5809,9 +5856,9 @@ "dev": true }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "node_modules/@types/http-errors": { @@ -6114,6 +6161,22 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/@vue/component-compiler-utils/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/@vue/component-compiler-utils/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6705,9 +6768,9 @@ "dev": true }, "node_modules/antlr4": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.0.tgz", - "integrity": "sha512-zooUbt+UscjnWyOrsuY/tVFL4rwrAGwOivpQmvmUDE22hy/lUA467Rc1rcixyRwcRUIXFYBwv7+dClDSHdmmew==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.2.tgz", + "integrity": "sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==", "dev": true, "engines": { "node": ">=16" @@ -8897,6 +8960,16 @@ "dev": true, "optional": true }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "node_modules/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -23161,9 +23234,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/graphemer": { @@ -23918,9 +23991,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-errors": { @@ -29233,15 +29306,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -29373,6 +29446,12 @@ "extend": "^3.0.0" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -30950,14 +31029,14 @@ } }, "node_modules/solhint": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.4.1.tgz", - "integrity": "sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-5.0.3.tgz", + "integrity": "sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ==", "dev": true, "dependencies": { - "@solidity-parser/parser": "^0.16.0", + "@solidity-parser/parser": "^0.18.0", "ajv": "^6.12.6", - "antlr4": "^4.11.0", + "antlr4": "^4.13.1-patch-1", "ast-parents": "^0.0.1", "chalk": "^4.1.2", "commander": "^10.0.0", @@ -30966,9 +31045,10 @@ "glob": "^8.0.3", "ignore": "^5.2.4", "js-yaml": "^4.1.0", + "latest-version": "^7.0.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", - "semver": "^6.3.0", + "semver": "^7.5.2", "strip-ansi": "^6.0.1", "table": "^6.8.1", "text-table": "^0.2.0" @@ -30981,25 +31061,47 @@ } }, "node_modules/solhint-plugin-prettier": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz", - "integrity": "sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.1.0.tgz", + "integrity": "sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==", "dev": true, "dependencies": { + "@prettier/sync": "^0.3.0", "prettier-linter-helpers": "^1.0.0" }, "peerDependencies": { - "prettier": "^1.15.0 || ^2.0.0", - "prettier-plugin-solidity": "^1.0.0-alpha.14" + "prettier": "^3.0.0", + "prettier-plugin-solidity": "^1.0.0" + } + }, + "node_modules/solhint/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/solhint/node_modules/@solidity-parser/parser": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.1.tgz", - "integrity": "sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true + }, + "node_modules/solhint/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" } }, "node_modules/solhint/node_modules/argparse": { @@ -31017,6 +31119,33 @@ "balanced-match": "^1.0.0" } }, + "node_modules/solhint/node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/solhint/node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/solhint/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -31044,6 +31173,63 @@ "url": "https://github.com/sponsors/d-fischer" } }, + "node_modules/solhint/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/solhint/node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/solhint/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/solhint/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -31063,6 +31249,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/solhint/node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/solhint/node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/solhint/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -31075,6 +31299,60 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/solhint/node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/solhint/node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/solhint/node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dev": true, + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/solhint/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -31087,6 +31365,115 @@ "node": ">=10" } }, + "node_modules/solhint/node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/solhint/node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dev": true, + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/solhint/node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/solhint/node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/solhint/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/solidity-comments-extractor": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", @@ -33699,6 +34086,22 @@ "node": ">=4" } }, + "node_modules/ts-generator/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/ts-generator/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -39295,6 +39698,39 @@ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.0.tgz", "integrity": "sha512-dlKiZmDvJnGRLHojrDoFZJmsQVeltVeoiRN7RK+cf2FmkhASDEblE0RiaYdxPNsUZa6mRG8393b9bfyp+V5IAw==" }, + "@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true + }, + "@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "requires": { + "graceful-fs": "4.2.10" + } + }, + "@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "requires": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + } + }, + "@prettier/sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@prettier/sync/-/sync-0.3.0.tgz", + "integrity": "sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==", + "dev": true, + "requires": {} + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -40316,9 +40752,9 @@ "dev": true }, "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "@types/http-errors": { @@ -40602,6 +41038,13 @@ "source-map": "^0.6.1" } }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -41081,9 +41524,9 @@ "dev": true }, "antlr4": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.0.tgz", - "integrity": "sha512-zooUbt+UscjnWyOrsuY/tVFL4rwrAGwOivpQmvmUDE22hy/lUA467Rc1rcixyRwcRUIXFYBwv7+dClDSHdmmew==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.2.tgz", + "integrity": "sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==", "dev": true }, "antlr4ts": { @@ -42829,6 +43272,16 @@ } } }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -54118,9 +54571,9 @@ } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "graphemer": { @@ -54692,9 +55145,9 @@ } }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "http-errors": { @@ -58891,9 +59344,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true }, "prettier-linter-helpers": { @@ -58997,6 +59450,12 @@ "extend": "^3.0.0" } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -60243,14 +60702,14 @@ } }, "solhint": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.4.1.tgz", - "integrity": "sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-5.0.3.tgz", + "integrity": "sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ==", "dev": true, "requires": { - "@solidity-parser/parser": "^0.16.0", + "@solidity-parser/parser": "^0.18.0", "ajv": "^6.12.6", - "antlr4": "^4.11.0", + "antlr4": "^4.13.1-patch-1", "ast-parents": "^0.0.1", "chalk": "^4.1.2", "commander": "^10.0.0", @@ -60259,22 +60718,35 @@ "glob": "^8.0.3", "ignore": "^5.2.4", "js-yaml": "^4.1.0", + "latest-version": "^7.0.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", "prettier": "^2.8.3", - "semver": "^6.3.0", + "semver": "^7.5.2", "strip-ansi": "^6.0.1", "table": "^6.8.1", "text-table": "^0.2.0" }, "dependencies": { + "@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true + }, "@solidity-parser/parser": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.1.tgz", - "integrity": "sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, "requires": { - "antlr4ts": "^0.5.0-alpha.4" + "defer-to-connect": "^2.0.1" } }, "argparse": { @@ -60292,6 +60764,27 @@ "balanced-match": "^1.0.0" } }, + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true + }, + "cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + } + }, "commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -60310,6 +60803,41 @@ "path-type": "^4.0.0" } }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -60323,6 +60851,35 @@ "once": "^1.3.0" } }, + "got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + } + }, + "http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -60332,6 +60889,42 @@ "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dev": true, + "requires": { + "package-json": "^8.1.0" + } + }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true + }, + "mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true + }, "minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -60340,15 +60933,80 @@ "requires": { "brace-expansion": "^2.0.1" } + }, + "normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true + }, + "package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dev": true, + "requires": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + } + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true + }, + "registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "requires": { + "@pnpm/npm-conf": "^2.1.0" + } + }, + "registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "requires": { + "rc": "1.2.8" + } + }, + "responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "requires": { + "lowercase-keys": "^3.0.0" + } + }, + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true } } }, "solhint-plugin-prettier": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz", - "integrity": "sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.1.0.tgz", + "integrity": "sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==", "dev": true, "requires": { + "@prettier/sync": "^0.3.0", "prettier-linter-helpers": "^1.0.0" } }, @@ -62335,6 +62993,13 @@ "dev": true, "peer": true }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "peer": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 7e7a24ca..7492d55b 100755 --- a/package.json +++ b/package.json @@ -65,11 +65,11 @@ "lint-staged": "11.0.0", "mrm": "3.0.10", "pinst": "2.1.6", - "prettier": "2.8.8", + "prettier": "3.3.3", "prettier-plugin-solidity": "1.1.3", "regenerator-runtime": "0.13.11", - "solhint": "3.4.1", - "solhint-plugin-prettier": "0.0.5", + "solhint": "5.0.3", + "solhint-plugin-prettier": "0.1.0", "solidity-coverage": "0.8.4", "truffle": "5.11.1", "ts-node": "10.9.1", diff --git a/scripts/4_deploy_uniswap_v2_caller copy.js b/scripts/4_deploy_uniswap_v2_caller copy.js new file mode 100644 index 00000000..520cb6ef --- /dev/null +++ b/scripts/4_deploy_uniswap_v2_caller copy.js @@ -0,0 +1,23 @@ +import deploymentAddresses from './deployment'; + +try { + (async () => { + console.log('Make sure 0x161b29D1919D4E06b53eE449376181B5082b30B9 is used and nonce is 3-6'); + + const chainIdHex = await hre.network.provider.request({ method: 'eth_chainId' }); + const chainId = parseInt(chainIdHex.toString(), 16).toString(); + + console.log(`Working with chainId ${chainId}`); + + // We get the contract to deploy + const Contract = await ethers.getContractFactory('UniswapV2Caller'); + const contract = await Contract.deploy(deploymentAddresses.weth[chainId]); + + console.log(`${'UniswapV2Caller'} deployed to: ${contract.address}`); + + return contract.address; + })(); +} catch (error) { + console.error(error); + process.exit(1); +} diff --git a/scripts/5_deploy_uniswap_v2_caller.js b/scripts/5_deploy_uniswap_v2_caller.js new file mode 100644 index 00000000..29561842 --- /dev/null +++ b/scripts/5_deploy_uniswap_v2_caller.js @@ -0,0 +1,23 @@ +import deploymentAddresses from './deployment'; + +try { + (async () => { + console.log('Make sure 0x161b29D1919D4E06b53eE449376181B5082b30B9 is used and nonce is 7'); + + const chainIdHex = await hre.network.provider.request({ method: 'eth_chainId' }); + const chainId = parseInt(chainIdHex.toString(), 16).toString(); + + console.log(`Working with chainId ${chainId}`); + + // We get the contract to deploy + const Contract = await ethers.getContractFactory('UniswapV3Caller'); + const contract = await Contract.deploy(deploymentAddresses.weth[chainId]); + + console.log(`${'UniswapV3Caller'} deployed to: ${contract.address}`); + + return contract.address; + })(); +} catch (error) { + console.error(error); + process.exit(1); +} diff --git a/scripts/4_verify.js b/scripts/6_verify.js similarity index 56% rename from scripts/4_verify.js rename to scripts/6_verify.js index 3fb3f879..e3872d19 100644 --- a/scripts/4_verify.js +++ b/scripts/6_verify.js @@ -10,6 +10,18 @@ try { await hre.run('verify:verify', { address: deploymentAddresses.simpleCaller[chainId], }); + await hre.run('verify:verify', { + address: deploymentAddresses.uniswapV2Caller[chainId], + constructorArguments: [ + deploymentAddresses.weth[chainId], + ], + }); + await hre.run('verify:verify', { + address: deploymentAddresses.uniswapV3Caller[chainId], + constructorArguments: [ + deploymentAddresses.weth[chainId], + ], + }); })(); } catch (error) { console.error(error); diff --git a/scripts/deployment.js b/scripts/deployment.js index 92cf6659..badefacb 100644 --- a/scripts/deployment.js +++ b/scripts/deployment.js @@ -27,6 +27,36 @@ const deploymentAddresses = { 81457: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', 1313161554: '0xC629Bf86f02ef13E8F1f5F75adE8a8165587998F', }, + uniswapV2Caller: { + 56: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 100: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 137: '0x53F5D89914327C3900FC2C9D297B9B30129F463e', + 250: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 1101: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', + 8453: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 42161: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 43114: '0xE81B24bcD0a706ae3Ece36C42CF96c010EEeF37a', + 59144: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', + 81457: '0xC0AC45d01a64660629506b5889722C6dA25F4084', + 534352: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', + 7777777: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', + 666666666: '0xe76BA87E04555e1a5afcCb0c8c5AC4d0b29e3dBE', + }, + weth: { + 56: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', + 100: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', + 137: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + 250: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', + 1101: '0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9', + 8453: '0x4200000000000000000000000000000000000006', + 42161: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + 43114: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', + 59144: '0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f', + 81457: '0x4300000000000000000000000000000000000004', + 534352: '0x5300000000000000000000000000000000000004', + 7777777: '0x4200000000000000000000000000000000000006', + 666666666: '0xEb54dACB4C2ccb64F8074eceEa33b5eBb38E5387', + }, feeBeneficiaries: { 10: '0x7d20Ab6D8aF50d87A5E8DeF46e48F4d7dC2Ea5c7', 56: '0x4a183b7ED67B9E14b3f45Abfb2Cf44ed22c29E54', diff --git a/test/callers/UniswapCaller.js b/test/callers/UniswapV2Caller.js similarity index 84% rename from test/callers/UniswapCaller.js rename to test/callers/UniswapV2Caller.js index c7304969..197da5e5 100644 --- a/test/callers/UniswapCaller.js +++ b/test/callers/UniswapV2Caller.js @@ -1,4 +1,5 @@ import buyTokenOnUniswap from '../helpers/buyTokenOnUniswap'; +import logChange from '../helpers/logger'; import { wethAddress, ethAddress, daiAddress } from '../helpers/tokens'; const { expect } = require('chai'); @@ -15,7 +16,7 @@ const uniDaiWethAddress = '0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11'; const zeroPermit = ['0', EMPTY_BYTES]; const zeroSignature = ['0', EMPTY_BYTES]; -describe('UniswapCaller', () => { +describe('UniswapV2Caller', () => { let owner; let notOwner; let caller; @@ -28,6 +29,21 @@ describe('UniswapCaller', () => { const logger = new ethers.utils.Logger('1'); const abiCoder = new ethers.utils.AbiCoder(); + async function execute(i, out, sp, as, fs, opt = {}) { + const ethBefore = await owner.getBalance(); + const daiBefore = await dai.balanceOf(owner.address); + const wethBefore = await weth.balanceOf(owner.address); + + const tx = await router.functions.execute(i, out, sp, as, fs, opt); + const receipt = await tx.wait(); + + logger.info(`Called router for ${receipt.gasUsed} gas`); + + logChange(logger, 'eth', ethBefore, (await owner.getBalance()).add(receipt.gasUsed.mul(receipt.effectiveGasPrice))); + logChange(logger, 'dai', daiBefore, await dai.balanceOf(owner.address)); + logChange(logger, 'weth', wethBefore, await weth.balanceOf(owner.address)); + } + before(async () => { // await network.provider.request({ // method: "hardhat_reset", @@ -40,7 +56,7 @@ describe('UniswapCaller', () => { // ], // }); - Caller = await ethers.getContractFactory('UniswapCaller'); + Caller = await ethers.getContractFactory('UniswapV2Caller'); Router = await ethers.getContractFactory('Router'); [owner, notOwner] = await ethers.getSigners(); @@ -70,11 +86,11 @@ describe('UniswapCaller', () => { beforeEach(async () => { router = await Router.deploy(); + await router.setProtocolFeeDefault(protocolFeeDefault); }); it('should not do weth -> dai trade with high slippage', async () => { await weth.approve(router.address, ethers.utils.parseUnits('1', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -111,15 +127,8 @@ describe('UniswapCaller', () => { it('should do weth -> dai trade', async () => { await weth.approve(router.address, ethers.utils.parseUnits('1', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - logger.info( - `weth balance is ${ethers.utils.formatUnits(await weth.balanceOf(owner.address), 18)}`, - ); - const tx = await router.functions.execute( + await execute( // input [[weth.address, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], // output @@ -148,22 +157,10 @@ describe('UniswapCaller', () => { // fee signature zeroSignature, ); - logger.info(`Called router for ${(await tx.wait()).gasUsed} gas`); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - logger.info( - `weth balance is ${ethers.utils.formatUnits(await weth.balanceOf(owner.address), 18)}`, - ); }); it('should do eth -> dai trade', async () => { - await router.setProtocolFeeDefault(protocolFeeDefault); - - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - const tx = await router.functions.execute( + await execute( // input [[ethAddress, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], // output @@ -195,15 +192,9 @@ describe('UniswapCaller', () => { value: ethers.utils.parseEther('1'), }, ); - logger.info(`Called router for ${(await tx.wait()).gasUsed} gas`); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); }); it('should not do eth -> dai trade with too large marketplace fee', async () => { - await router.setProtocolFeeDefault(protocolFeeDefault); - await expect( router.functions.execute( // input @@ -241,8 +232,6 @@ describe('UniswapCaller', () => { }); it('should not do eth -> dai trade with high slippage', async () => { - await router.setProtocolFeeDefault(protocolFeeDefault); - await expect( router.functions.execute( // input @@ -279,9 +268,8 @@ describe('UniswapCaller', () => { ).to.be.reverted; }); - it('should do dai -> eth trade with 0 input', async () => { + it('should not do dai -> eth trade with 0 input', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -318,12 +306,8 @@ describe('UniswapCaller', () => { it('should do dai -> eth trade', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - const tx = await router.functions.execute( + await execute( // input [[daiAddress, ethers.utils.parseUnits('500', 18), AMOUNT_ABSOLUTE], zeroPermit], // output @@ -352,15 +336,10 @@ describe('UniswapCaller', () => { // fee signature zeroSignature, ); - logger.info(`Called router for ${(await tx.wait()).gasUsed} gas`); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); }); - it('should do dai -> weth trade with 0 output', async () => { + it('should not do dai -> weth trade with 0 output', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -397,7 +376,6 @@ describe('UniswapCaller', () => { it('should not do dai -> weth trade with empty path', async () => { await dai.approve(router.address, ethers.utils.parseUnits('1', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -434,7 +412,6 @@ describe('UniswapCaller', () => { it('should not do dai -> weth trade with bad directions length', async () => { await dai.approve(router.address, ethers.utils.parseUnits('5000', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -471,7 +448,6 @@ describe('UniswapCaller', () => { it('should not do dai -> weth trade with huge fee', async () => { await dai.approve(router.address, ethers.utils.parseUnits('5000', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -508,15 +484,8 @@ describe('UniswapCaller', () => { it('should do dai -> weth trade', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - logger.info( - `weth balance is ${ethers.utils.formatUnits(await weth.balanceOf(owner.address), 18)}`, - ); - const tx = await router.functions.execute( + await execute( // input [[daiAddress, ethers.utils.parseUnits('500', 18), AMOUNT_ABSOLUTE], zeroPermit], // output @@ -545,18 +514,10 @@ describe('UniswapCaller', () => { // fee signature zeroSignature, ); - logger.info(`Called router for ${(await tx.wait()).gasUsed} gas`); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - logger.info( - `weth balance is ${ethers.utils.formatUnits(await weth.balanceOf(owner.address), 18)}`, - ); }); it('should not do dai -> weth trade with high token slippage', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( @@ -593,7 +554,6 @@ describe('UniswapCaller', () => { it('should not do dai -> weth trade with not enough liquidity', async () => { await dai.approve(router.address, ethers.utils.parseUnits('500', 18)); - await router.setProtocolFeeDefault(protocolFeeDefault); await expect( router.functions.execute( diff --git a/test/callers/UniswapV3Caller.js b/test/callers/UniswapV3Caller.js index 9a862683..6ede88bc 100644 --- a/test/callers/UniswapV3Caller.js +++ b/test/callers/UniswapV3Caller.js @@ -1,10 +1,12 @@ import buyTokenOnUniswap from '../helpers/buyTokenOnUniswap'; +import logChange from '../helpers/logger'; import { wethAddress, ethAddress, daiAddress } from '../helpers/tokens'; const { ethers } = require('hardhat'); const AMOUNT_ABSOLUTE = 2; const SWAP_FIXED_INPUTS = 1; +const SWAP_FIXED_OUTPUTS = 2; const EMPTY_BYTES = '0x'; const uniDaiWethAddress = '0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8'; @@ -19,12 +21,27 @@ describe('UniswapV3Caller', () => { let Router; let Caller; let router; - // let weth; + let weth; let dai; let protocolFeeDefault; const logger = new ethers.utils.Logger('1'); const abiCoder = new ethers.utils.AbiCoder(); + async function execute(i, out, sp, as, fs, opt = {}) { + const ethBefore = await owner.getBalance(); + const daiBefore = await dai.balanceOf(owner.address); + const wethBefore = await weth.balanceOf(owner.address); + + const tx = await router.functions.execute(i, out, sp, as, fs, opt); + const receipt = await tx.wait(); + + logger.info(`Called router for ${receipt.gasUsed} gas`); + + logChange(logger, 'eth', ethBefore, (await owner.getBalance()).add(receipt.gasUsed.mul(receipt.effectiveGasPrice))); + logChange(logger, 'dai', daiBefore, await dai.balanceOf(owner.address)); + logChange(logger, 'weth', wethBefore, await weth.balanceOf(owner.address)); + } + before(async () => { // await network.provider.request({ // method: "hardhat_reset", @@ -51,24 +68,20 @@ describe('UniswapV3Caller', () => { caller = await Caller.deploy(wethAddress); - // weth = await ethers.getContractAt('IERC20', wethAddress, owner); + weth = await ethers.getContractAt('IERC20', wethAddress, owner); dai = await ethers.getContractAt('IERC20', daiAddress, owner); await buyTokenOnUniswap(owner, daiAddress); - protocolFeeDefault = [ethers.utils.parseUnits('0.01', 18), notOwner.address]; + protocolFeeDefault = [ethers.utils.parseUnits('0', 18), notOwner.address]; }); beforeEach(async () => { router = await Router.deploy(); - }); - - it.only('should do eth -> dai trade', async () => { await router.setProtocolFeeDefault(protocolFeeDefault); + }); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, - ); - const tx = await router.functions.execute( + it('should do eth -> dai trade fixed inputs', async () => { + await execute( // input [[ethAddress, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], // output @@ -81,12 +94,116 @@ describe('UniswapV3Caller', () => { owner.address, caller.address, abiCoder.encode( - ['address', 'address', 'address', 'uint256', 'bool'], + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], + [ + ethAddress, + daiAddress, + uniDaiWethAddress, + false, + ethers.utils.parseUnits('1', 18), + true, + ], + ), + ], + // account signature + zeroSignature, + // fee signature + zeroSignature, + { + value: ethers.utils.parseEther('1'), + }, + ); + }); + + it('should do dai -> eth trade fixed inputs', async () => { + await dai.approve(router.address, ethers.utils.parseUnits('1000', 18)); + + await execute( + // input + [[daiAddress, ethers.utils.parseUnits('1000', 18), AMOUNT_ABSOLUTE], zeroPermit], + // output + [ethAddress, ethers.utils.parseUnits('0.1', 18)], + // swap description + [ + SWAP_FIXED_INPUTS, + protocolFeeDefault, + protocolFeeDefault, + owner.address, + caller.address, + abiCoder.encode( + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], [ + daiAddress, ethAddress, + uniDaiWethAddress, + true, + ethers.utils.parseUnits('1000', 18), + true, + ], + ), + ], + // account signature + zeroSignature, + // fee signature + zeroSignature, + ); + }); + + it('should do weth -> dai trade fixed inputs', async () => { + await weth.approve(router.address, ethers.utils.parseUnits('1', 18)); + + await execute( + // input + [[wethAddress, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], + // output + [daiAddress, ethers.utils.parseUnits('1000', 18)], + // swap description + [ + SWAP_FIXED_INPUTS, + protocolFeeDefault, + protocolFeeDefault, + owner.address, + caller.address, + abiCoder.encode( + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], + [ + wethAddress, daiAddress, uniDaiWethAddress, + false, ethers.utils.parseUnits('1', 18), + true, + ], + ), + ], + // account signature + zeroSignature, + // fee signature + zeroSignature, + ); + }); + + it('should do eth -> dai trade fixed outputs', async () => { + await execute( + // input + [[ethAddress, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], + // output + [daiAddress, ethers.utils.parseUnits('1000', 18)], + // swap description + [ + SWAP_FIXED_OUTPUTS, + protocolFeeDefault, + protocolFeeDefault, + owner.address, + caller.address, + abiCoder.encode( + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], + [ + ethAddress, + daiAddress, + uniDaiWethAddress, + false, + ethers.utils.parseUnits('1000', 18), false, ], ), @@ -99,9 +216,73 @@ describe('UniswapV3Caller', () => { value: ethers.utils.parseEther('1'), }, ); - logger.info(`Called router for ${(await tx.wait()).gasUsed} gas`); - logger.info( - `dai balance is ${ethers.utils.formatUnits(await dai.balanceOf(owner.address), 18)}`, + }); + + it('should do dai -> eth trade fixed outputs', async () => { + await dai.approve(router.address, ethers.utils.parseUnits('1000', 18)); + + await execute( + // input + [[daiAddress, ethers.utils.parseUnits('1000', 18), AMOUNT_ABSOLUTE], zeroPermit], + // output + [ethAddress, ethers.utils.parseUnits('0.1', 18)], + // swap description + [ + SWAP_FIXED_OUTPUTS, + protocolFeeDefault, + protocolFeeDefault, + owner.address, + caller.address, + abiCoder.encode( + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], + [ + daiAddress, + ethAddress, + uniDaiWethAddress, + true, + ethers.utils.parseUnits('0.1', 18), + false, + ], + ), + ], + // account signature + zeroSignature, + // fee signature + zeroSignature, + ); + }); + + it('should do weth -> dai trade fixed outputs', async () => { + await weth.approve(router.address, ethers.utils.parseUnits('1', 18)); + + await execute( + // input + [[wethAddress, ethers.utils.parseUnits('1', 18), AMOUNT_ABSOLUTE], zeroPermit], + // output + [daiAddress, ethers.utils.parseUnits('1000', 18)], + // swap description + [ + SWAP_FIXED_OUTPUTS, + protocolFeeDefault, + protocolFeeDefault, + owner.address, + caller.address, + abiCoder.encode( + ['address', 'address', 'address', 'bool', 'uint256', 'bool'], + [ + wethAddress, + daiAddress, + uniDaiWethAddress, + false, + ethers.utils.parseUnits('1000', 18), + false, + ], + ), + ], + // account signature + zeroSignature, + // fee signature + zeroSignature, ); }); }); diff --git a/test/helpers/latestTime.js b/test/helpers/latestTime.js index 2f6e4cd1..ed312b26 100755 --- a/test/helpers/latestTime.js +++ b/test/helpers/latestTime.js @@ -1,7 +1,7 @@ // Returns the time of the last mined block in seconds -async function latestTime() { +const latestTime = async () => { const block = await web3.eth.getBlock('latest'); return block.timestamp; -} +}; export default latestTime; diff --git a/test/helpers/logger.js b/test/helpers/logger.js new file mode 100644 index 00000000..05e724f3 --- /dev/null +++ b/test/helpers/logger.js @@ -0,0 +1,11 @@ +const { ethers } = require('hardhat'); + +const logChange = (logger, name, before, after) => { + if (!before.eq(after)) { + logger.info( + `${name} balance change is ${before.gt(after) ? '-' : '+'}${ethers.utils.formatUnits(before.gt(after) ? before.sub(after) : after.sub(before), 18)}`, + ); + } +}; + +export default logChange; diff --git a/test/router/Router.js b/test/router/Router.js index 1f2bb983..af6d3abc 100644 --- a/test/router/Router.js +++ b/test/router/Router.js @@ -270,6 +270,23 @@ describe('Router', () => { ).to.be.reverted; }); + it('should not execute with bad amount type', async () => { + await expect( + router.functions.execute( + // input + [[AddressZero, ethers.utils.parseUnits('0', 18), '0'], zeroPermit], + // output + [ethAddress, '0'], + // swap description + [SWAP_FIXED_INPUTS, zeroFee, zeroFee, owner.address, mockCaller.address, EMPTY_BYTES], + // account signature + zeroSignature, + // fee signature + zeroSignature, + ), + ).to.be.reverted; + }); + it('should not execute with relative amount for ETH', async () => { await expect( router.functions.execute(