mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
Merge pull request #114 from Instadapp/uniswap-arbitrum
feat: simple uniswap connector on arbitrum
This commit is contained in:
commit
b2e1060e85
10
contracts/arbitrum/connectors/uniswap-sell-beta/events.sol
Normal file
10
contracts/arbitrum/connectors/uniswap-sell-beta/events.sol
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
contract Events {
|
||||||
|
event LogSell(
|
||||||
|
uint24 fee,
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 amountOut,
|
||||||
|
uint256 amountOutMinimum
|
||||||
|
);
|
||||||
|
}
|
123
contracts/arbitrum/connectors/uniswap-sell-beta/helpers.sol
Normal file
123
contracts/arbitrum/connectors/uniswap-sell-beta/helpers.sol
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
pragma solidity ^0.7.6;
|
||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
import {UniswapV3Pool, ISwapRouter} from "./interface.sol";
|
||||||
|
import {SqrtPriceMath} from "./libraries/SqrtPriceMath.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
|
||||||
|
contract Helpers {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
ISwapRouter internal constant router =
|
||||||
|
ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
|
||||||
|
|
||||||
|
bytes32 internal constant POOL_INIT_CODE_HASH =
|
||||||
|
0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
|
||||||
|
|
||||||
|
struct PoolKey {
|
||||||
|
address token0;
|
||||||
|
address token1;
|
||||||
|
uint24 fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPoolAddress(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint24 fee
|
||||||
|
) internal pure returns (address pool) {
|
||||||
|
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
|
||||||
|
return
|
||||||
|
computeAddress(
|
||||||
|
0x1F98431c8aD98523631AE4a59f267346ea31F984,
|
||||||
|
PoolKey({token0: tokenA, token1: tokenB, fee: fee})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPriceLimit(
|
||||||
|
uint256 amountIn,
|
||||||
|
bool zeroForOne,
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint24 fee
|
||||||
|
) internal view returns (uint160) {
|
||||||
|
UniswapV3Pool state = UniswapV3Pool(
|
||||||
|
getPoolAddress(tokenA, tokenB, fee)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
SqrtPriceMath.getNextSqrtPriceFromInput(
|
||||||
|
state.slot0().sqrtPriceX96,
|
||||||
|
state.liquidity(),
|
||||||
|
amountIn,
|
||||||
|
zeroForOne
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParams(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
address recipient,
|
||||||
|
uint24 fee,
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 amountOutMinimum
|
||||||
|
) internal view returns (ISwapRouter.ExactInputSingleParams memory params) {
|
||||||
|
params = ISwapRouter.ExactInputSingleParams({
|
||||||
|
tokenIn: tokenIn,
|
||||||
|
tokenOut: tokenOut,
|
||||||
|
fee: fee,
|
||||||
|
recipient: recipient,
|
||||||
|
deadline: block.timestamp + 1,
|
||||||
|
amountIn: amountIn,
|
||||||
|
amountOutMinimum: amountOutMinimum,
|
||||||
|
sqrtPriceLimitX96: getPriceLimit(
|
||||||
|
amountIn,
|
||||||
|
tokenIn < tokenOut,
|
||||||
|
tokenIn,
|
||||||
|
tokenOut,
|
||||||
|
fee
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function SwapTokens(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
bool zeroForOne
|
||||||
|
) internal pure returns (address, address) {
|
||||||
|
if (!zeroForOne) return (tokenOut, tokenIn);
|
||||||
|
else return (tokenIn, tokenOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapSingleInput(ISwapRouter.ExactInputSingleParams memory params)
|
||||||
|
internal
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return (uint256(router.exactInputSingle(params)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
pragma solidity ^0.7.6;
|
||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
interface UniswapV3Pool {
|
||||||
|
struct Slot0 {
|
||||||
|
uint160 sqrtPriceX96;
|
||||||
|
int24 tick;
|
||||||
|
uint16 observationIndex;
|
||||||
|
uint16 observationCardinality;
|
||||||
|
uint16 observationCardinalityNext;
|
||||||
|
uint8 feeProtocol;
|
||||||
|
bool unlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
function liquidity() external view returns (uint128);
|
||||||
|
|
||||||
|
function slot0() external view returns (Slot0 memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISwapRouter {
|
||||||
|
struct ExactInputSingleParams {
|
||||||
|
address tokenIn;
|
||||||
|
address tokenOut;
|
||||||
|
uint24 fee;
|
||||||
|
address recipient;
|
||||||
|
uint256 deadline;
|
||||||
|
uint256 amountIn;
|
||||||
|
uint256 amountOutMinimum;
|
||||||
|
uint160 sqrtPriceLimitX96;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactInputSingle(ExactInputSingleParams calldata params)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint256);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
pragma solidity >=0.4.0;
|
||||||
|
|
||||||
|
/// @title FixedPoint96
|
||||||
|
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
|
||||||
|
/// @dev Used in SqrtPriceMath.sol
|
||||||
|
library FixedPoint96 {
|
||||||
|
uint8 internal constant RESOLUTION = 96;
|
||||||
|
uint256 internal constant Q96 = 0x1000000000000000000000000;
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity >=0.4.0;
|
||||||
|
|
||||||
|
/// @title Contains 512-bit math functions
|
||||||
|
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
|
||||||
|
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
|
||||||
|
library FullMath {
|
||||||
|
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
||||||
|
/// @param a The multiplicand
|
||||||
|
/// @param b The multiplier
|
||||||
|
/// @param denominator The divisor
|
||||||
|
/// @return result The 256-bit result
|
||||||
|
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
|
||||||
|
function mulDiv(
|
||||||
|
uint256 a,
|
||||||
|
uint256 b,
|
||||||
|
uint256 denominator
|
||||||
|
) internal pure returns (uint256 result) {
|
||||||
|
// 512-bit multiply [prod1 prod0] = a * b
|
||||||
|
// Compute the product mod 2**256 and mod 2**256 - 1
|
||||||
|
// then use the Chinese Remainder Theorem to reconstruct
|
||||||
|
// the 512 bit result. The result is stored in two 256
|
||||||
|
// variables such that product = prod1 * 2**256 + prod0
|
||||||
|
uint256 prod0; // Least significant 256 bits of the product
|
||||||
|
uint256 prod1; // Most significant 256 bits of the product
|
||||||
|
assembly {
|
||||||
|
let mm := mulmod(a, b, not(0))
|
||||||
|
prod0 := mul(a, b)
|
||||||
|
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle non-overflow cases, 256 by 256 division
|
||||||
|
if (prod1 == 0) {
|
||||||
|
require(denominator > 0);
|
||||||
|
assembly {
|
||||||
|
result := div(prod0, denominator)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the result is less than 2**256.
|
||||||
|
// Also prevents denominator == 0
|
||||||
|
require(denominator > prod1);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
// 512 by 256 division.
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Make division exact by subtracting the remainder from [prod1 prod0]
|
||||||
|
// Compute remainder using mulmod
|
||||||
|
uint256 remainder;
|
||||||
|
assembly {
|
||||||
|
remainder := mulmod(a, b, denominator)
|
||||||
|
}
|
||||||
|
// Subtract 256 bit number from 512 bit number
|
||||||
|
assembly {
|
||||||
|
prod1 := sub(prod1, gt(remainder, prod0))
|
||||||
|
prod0 := sub(prod0, remainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factor powers of two out of denominator
|
||||||
|
// Compute largest power of two divisor of denominator.
|
||||||
|
// Always >= 1.
|
||||||
|
uint256 twos = -denominator & denominator;
|
||||||
|
// Divide denominator by power of two
|
||||||
|
assembly {
|
||||||
|
denominator := div(denominator, twos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide [prod1 prod0] by the factors of two
|
||||||
|
assembly {
|
||||||
|
prod0 := div(prod0, twos)
|
||||||
|
}
|
||||||
|
// Shift in bits from prod1 into prod0. For this we need
|
||||||
|
// to flip `twos` such that it is 2**256 / twos.
|
||||||
|
// If twos is zero, then it becomes one
|
||||||
|
assembly {
|
||||||
|
twos := add(div(sub(0, twos), twos), 1)
|
||||||
|
}
|
||||||
|
prod0 |= prod1 * twos;
|
||||||
|
|
||||||
|
// Invert denominator mod 2**256
|
||||||
|
// Now that denominator is an odd number, it has an inverse
|
||||||
|
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
|
||||||
|
// Compute the inverse by starting with a seed that is correct
|
||||||
|
// correct for four bits. That is, denominator * inv = 1 mod 2**4
|
||||||
|
uint256 inv = (3 * denominator) ^ 2;
|
||||||
|
// Now use Newton-Raphson iteration to improve the precision.
|
||||||
|
// Thanks to Hensel's lifting lemma, this also works in modular
|
||||||
|
// arithmetic, doubling the correct bits in each step.
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**8
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**16
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**32
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**64
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**128
|
||||||
|
inv *= 2 - denominator * inv; // inverse mod 2**256
|
||||||
|
|
||||||
|
// Because the division is now exact we can divide by multiplying
|
||||||
|
// with the modular inverse of denominator. This will give us the
|
||||||
|
// correct result modulo 2**256. Since the precoditions guarantee
|
||||||
|
// that the outcome is less than 2**256, this is the final result.
|
||||||
|
// We don't need to compute the high bits of the result and prod1
|
||||||
|
// is no longer required.
|
||||||
|
result = prod0 * inv;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
||||||
|
/// @param a The multiplicand
|
||||||
|
/// @param b The multiplier
|
||||||
|
/// @param denominator The divisor
|
||||||
|
/// @return result The 256-bit result
|
||||||
|
function mulDivRoundingUp(
|
||||||
|
uint256 a,
|
||||||
|
uint256 b,
|
||||||
|
uint256 denominator
|
||||||
|
) internal pure returns (uint256 result) {
|
||||||
|
result = mulDiv(a, b, denominator);
|
||||||
|
if (mulmod(a, b, denominator) > 0) {
|
||||||
|
require(result < type(uint256).max);
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
pragma solidity >=0.7.0;
|
||||||
|
|
||||||
|
/// @title Optimized overflow and underflow safe math operations
|
||||||
|
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
|
||||||
|
library LowGasSafeMath {
|
||||||
|
/// @notice Returns x + y, reverts if sum overflows uint256
|
||||||
|
/// @param x The augend
|
||||||
|
/// @param y The addend
|
||||||
|
/// @return z The sum of x and y
|
||||||
|
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||||
|
require((z = x + y) >= x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Returns x - y, reverts if underflows
|
||||||
|
/// @param x The minuend
|
||||||
|
/// @param y The subtrahend
|
||||||
|
/// @return z The difference of x and y
|
||||||
|
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||||
|
require((z = x - y) <= x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Returns x * y, reverts if overflows
|
||||||
|
/// @param x The multiplicand
|
||||||
|
/// @param y The multiplier
|
||||||
|
/// @return z The product of x and y
|
||||||
|
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||||
|
require(x == 0 || (z = x * y) / x == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Returns x + y, reverts if overflows or underflows
|
||||||
|
/// @param x The augend
|
||||||
|
/// @param y The addend
|
||||||
|
/// @return z The sum of x and y
|
||||||
|
function add(int256 x, int256 y) internal pure returns (int256 z) {
|
||||||
|
require((z = x + y) >= x == (y >= 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Returns x - y, reverts if overflows or underflows
|
||||||
|
/// @param x The minuend
|
||||||
|
/// @param y The subtrahend
|
||||||
|
/// @return z The difference of x and y
|
||||||
|
function sub(int256 x, int256 y) internal pure returns (int256 z) {
|
||||||
|
require((z = x - y) <= x == (y >= 0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
pragma solidity >=0.5.0;
|
||||||
|
|
||||||
|
/// @title Safe casting methods
|
||||||
|
/// @notice Contains methods for safely casting between types
|
||||||
|
library SafeCast {
|
||||||
|
/// @notice Cast a uint256 to a uint160, revert on overflow
|
||||||
|
/// @param y The uint256 to be downcasted
|
||||||
|
/// @return z The downcasted integer, now type uint160
|
||||||
|
function toUint160(uint256 y) internal pure returns (uint160 z) {
|
||||||
|
require((z = uint160(y)) == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Cast a int256 to a int128, revert on overflow or underflow
|
||||||
|
/// @param y The int256 to be downcasted
|
||||||
|
/// @return z The downcasted integer, now type int128
|
||||||
|
function toInt128(int256 y) internal pure returns (int128 z) {
|
||||||
|
require((z = int128(y)) == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Cast a uint256 to a int256, revert on overflow
|
||||||
|
/// @param y The uint256 to be casted
|
||||||
|
/// @return z The casted integer, now type int256
|
||||||
|
function toInt256(uint256 y) internal pure returns (int256 z) {
|
||||||
|
require(y < 2**255);
|
||||||
|
z = int256(y);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
pragma solidity >=0.5.0;
|
||||||
|
|
||||||
|
import './LowGasSafeMath.sol';
|
||||||
|
import './SafeCast.sol';
|
||||||
|
|
||||||
|
import './FullMath.sol';
|
||||||
|
import './UnsafeMath.sol';
|
||||||
|
import './FixedPoint96.sol';
|
||||||
|
|
||||||
|
/// @title Functions based on Q64.96 sqrt price and liquidity
|
||||||
|
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
|
||||||
|
library SqrtPriceMath {
|
||||||
|
using LowGasSafeMath for uint256;
|
||||||
|
using SafeCast for uint256;
|
||||||
|
|
||||||
|
/// @notice Gets the next sqrt price given a delta of token0
|
||||||
|
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
|
||||||
|
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
|
||||||
|
/// price less in order to not send too much output.
|
||||||
|
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
|
||||||
|
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
|
||||||
|
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param amount How much of token0 to add or remove from virtual reserves
|
||||||
|
/// @param add Whether to add or remove the amount of token0
|
||||||
|
/// @return The price after adding or removing amount, depending on add
|
||||||
|
function getNextSqrtPriceFromAmount0RoundingUp(
|
||||||
|
uint160 sqrtPX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
uint256 amount,
|
||||||
|
bool add
|
||||||
|
) internal pure returns (uint160) {
|
||||||
|
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
|
||||||
|
if (amount == 0) return sqrtPX96;
|
||||||
|
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
|
||||||
|
|
||||||
|
if (add) {
|
||||||
|
uint256 product;
|
||||||
|
if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
|
||||||
|
uint256 denominator = numerator1 + product;
|
||||||
|
if (denominator >= numerator1)
|
||||||
|
// always fits in 160 bits
|
||||||
|
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
|
||||||
|
} else {
|
||||||
|
uint256 product;
|
||||||
|
// if the product overflows, we know the denominator underflows
|
||||||
|
// in addition, we must check that the denominator does not underflow
|
||||||
|
require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
|
||||||
|
uint256 denominator = numerator1 - product;
|
||||||
|
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Gets the next sqrt price given a delta of token1
|
||||||
|
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
|
||||||
|
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
|
||||||
|
/// price less in order to not send too much output.
|
||||||
|
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
|
||||||
|
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param amount How much of token1 to add, or remove, from virtual reserves
|
||||||
|
/// @param add Whether to add, or remove, the amount of token1
|
||||||
|
/// @return The price after adding or removing `amount`
|
||||||
|
function getNextSqrtPriceFromAmount1RoundingDown(
|
||||||
|
uint160 sqrtPX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
uint256 amount,
|
||||||
|
bool add
|
||||||
|
) internal pure returns (uint160) {
|
||||||
|
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
|
||||||
|
// in both cases, avoid a mulDiv for most inputs
|
||||||
|
if (add) {
|
||||||
|
uint256 quotient =
|
||||||
|
(
|
||||||
|
amount <= type(uint160).max
|
||||||
|
? (amount << FixedPoint96.RESOLUTION) / liquidity
|
||||||
|
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
|
||||||
|
);
|
||||||
|
|
||||||
|
return uint256(sqrtPX96).add(quotient).toUint160();
|
||||||
|
} else {
|
||||||
|
uint256 quotient =
|
||||||
|
(
|
||||||
|
amount <= type(uint160).max
|
||||||
|
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
|
||||||
|
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
|
||||||
|
);
|
||||||
|
|
||||||
|
require(sqrtPX96 > quotient);
|
||||||
|
// always fits 160 bits
|
||||||
|
return uint160(sqrtPX96 - quotient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Gets the next sqrt price given an input amount of token0 or token1
|
||||||
|
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
|
||||||
|
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param amountIn How much of token0, or token1, is being swapped in
|
||||||
|
/// @param zeroForOne Whether the amount in is token0 or token1
|
||||||
|
/// @return sqrtQX96 The price after adding the input amount to token0 or token1
|
||||||
|
function getNextSqrtPriceFromInput(
|
||||||
|
uint160 sqrtPX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
uint256 amountIn,
|
||||||
|
bool zeroForOne
|
||||||
|
) internal pure returns (uint160 sqrtQX96) {
|
||||||
|
require(sqrtPX96 > 0);
|
||||||
|
require(liquidity > 0);
|
||||||
|
|
||||||
|
// round to make sure that we don't pass the target price
|
||||||
|
return
|
||||||
|
zeroForOne
|
||||||
|
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
|
||||||
|
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Gets the next sqrt price given an output amount of token0 or token1
|
||||||
|
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
|
||||||
|
/// @param sqrtPX96 The starting price before accounting for the output amount
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param amountOut How much of token0, or token1, is being swapped out
|
||||||
|
/// @param zeroForOne Whether the amount out is token0 or token1
|
||||||
|
/// @return sqrtQX96 The price after removing the output amount of token0 or token1
|
||||||
|
function getNextSqrtPriceFromOutput(
|
||||||
|
uint160 sqrtPX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
uint256 amountOut,
|
||||||
|
bool zeroForOne
|
||||||
|
) internal pure returns (uint160 sqrtQX96) {
|
||||||
|
require(sqrtPX96 > 0);
|
||||||
|
require(liquidity > 0);
|
||||||
|
|
||||||
|
// round to make sure that we pass the target price
|
||||||
|
return
|
||||||
|
zeroForOne
|
||||||
|
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
|
||||||
|
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Gets the amount0 delta between two prices
|
||||||
|
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
|
||||||
|
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
|
||||||
|
/// @param sqrtRatioAX96 A sqrt price
|
||||||
|
/// @param sqrtRatioBX96 Another sqrt price
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param roundUp Whether to round the amount up or down
|
||||||
|
/// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
|
||||||
|
function getAmount0Delta(
|
||||||
|
uint160 sqrtRatioAX96,
|
||||||
|
uint160 sqrtRatioBX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
bool roundUp
|
||||||
|
) internal pure returns (uint256 amount0) {
|
||||||
|
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
|
||||||
|
|
||||||
|
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
|
||||||
|
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
|
||||||
|
|
||||||
|
require(sqrtRatioAX96 > 0);
|
||||||
|
|
||||||
|
return
|
||||||
|
roundUp
|
||||||
|
? UnsafeMath.divRoundingUp(
|
||||||
|
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
|
||||||
|
sqrtRatioAX96
|
||||||
|
)
|
||||||
|
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Gets the amount1 delta between two prices
|
||||||
|
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
|
||||||
|
/// @param sqrtRatioAX96 A sqrt price
|
||||||
|
/// @param sqrtRatioBX96 Another sqrt price
|
||||||
|
/// @param liquidity The amount of usable liquidity
|
||||||
|
/// @param roundUp Whether to round the amount up, or down
|
||||||
|
/// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
|
||||||
|
function getAmount1Delta(
|
||||||
|
uint160 sqrtRatioAX96,
|
||||||
|
uint160 sqrtRatioBX96,
|
||||||
|
uint128 liquidity,
|
||||||
|
bool roundUp
|
||||||
|
) internal pure returns (uint256 amount1) {
|
||||||
|
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
|
||||||
|
|
||||||
|
return
|
||||||
|
roundUp
|
||||||
|
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
|
||||||
|
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Helper that gets signed token0 delta
|
||||||
|
/// @param sqrtRatioAX96 A sqrt price
|
||||||
|
/// @param sqrtRatioBX96 Another sqrt price
|
||||||
|
/// @param liquidity The change in liquidity for which to compute the amount0 delta
|
||||||
|
/// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
|
||||||
|
function getAmount0Delta(
|
||||||
|
uint160 sqrtRatioAX96,
|
||||||
|
uint160 sqrtRatioBX96,
|
||||||
|
int128 liquidity
|
||||||
|
) internal pure returns (int256 amount0) {
|
||||||
|
return
|
||||||
|
liquidity < 0
|
||||||
|
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
|
||||||
|
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Helper that gets signed token1 delta
|
||||||
|
/// @param sqrtRatioAX96 A sqrt price
|
||||||
|
/// @param sqrtRatioBX96 Another sqrt price
|
||||||
|
/// @param liquidity The change in liquidity for which to compute the amount1 delta
|
||||||
|
/// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
|
||||||
|
function getAmount1Delta(
|
||||||
|
uint160 sqrtRatioAX96,
|
||||||
|
uint160 sqrtRatioBX96,
|
||||||
|
int128 liquidity
|
||||||
|
) internal pure returns (int256 amount1) {
|
||||||
|
return
|
||||||
|
liquidity < 0
|
||||||
|
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
|
||||||
|
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
pragma solidity >=0.5.0;
|
||||||
|
|
||||||
|
/// @title Math functions that do not check inputs or outputs
|
||||||
|
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
|
||||||
|
library UnsafeMath {
|
||||||
|
/// @notice Returns ceil(x / y)
|
||||||
|
/// @dev division by 0 has unspecified behavior, and must be checked externally
|
||||||
|
/// @param x The dividend
|
||||||
|
/// @param y The divisor
|
||||||
|
/// @return z The quotient, ceil(x / y)
|
||||||
|
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||||
|
assembly {
|
||||||
|
z := add(div(x, y), gt(mod(x, y), 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
contracts/arbitrum/connectors/uniswap-sell-beta/main.sol
Normal file
41
contracts/arbitrum/connectors/uniswap-sell-beta/main.sol
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
pragma solidity ^0.7.6;
|
||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
import "./helpers.sol";
|
||||||
|
import {Events} from "./events.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
|
||||||
|
contract uniswapSellBeta is Helpers, Events {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
function sell(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint24 fee,
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 amountOutMinimum
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (string memory _eventName, bytes memory _eventParam)
|
||||||
|
{
|
||||||
|
IERC20(tokenIn).safeApprove(address(router), amountIn);
|
||||||
|
uint256 amountOut = swapSingleInput(
|
||||||
|
getParams(
|
||||||
|
tokenIn,
|
||||||
|
tokenOut,
|
||||||
|
address(this),
|
||||||
|
fee,
|
||||||
|
amountIn,
|
||||||
|
amountOutMinimum
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_eventName = "LogSell(uint24,uint256,uint256,uint256)";
|
||||||
|
_eventParam = abi.encode(fee, amountIn, amountOut, amountOutMinimum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ConnectV2UniswapSellBeta is uniswapSellBeta {
|
||||||
|
string public constant name = "Uniswap-Sell-Beta";
|
||||||
|
}
|
|
@ -80,12 +80,12 @@ module.exports = {
|
||||||
gasPrice: parseInt(utils.parseUnits("2", "gwei")),
|
gasPrice: parseInt(utils.parseUnits("2", "gwei")),
|
||||||
},
|
},
|
||||||
avax: {
|
avax: {
|
||||||
url: 'https://api.avax.network/ext/bc/C/rpc',
|
url: "https://api.avax.network/ext/bc/C/rpc",
|
||||||
chainId: 43114,
|
chainId: 43114,
|
||||||
accounts: [`0x${PRIVATE_KEY}`],
|
accounts: [`0x${PRIVATE_KEY}`],
|
||||||
timeout: 150000,
|
timeout: 150000,
|
||||||
gasPrice: parseInt(utils.parseUnits("225", "gwei"))
|
gasPrice: parseInt(utils.parseUnits("225", "gwei")),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
etherscan: {
|
etherscan: {
|
||||||
apiKey: ETHERSCAN_API_KEY,
|
apiKey: ETHERSCAN_API_KEY,
|
||||||
|
|
80
test/sample/uniswap.js
Normal file
80
test/sample/uniswap.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
const { expect } = require("chai");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
const { web3, deployments, waffle, ethers } = hre;
|
||||||
|
const { provider, deployContract } = waffle;
|
||||||
|
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
|
||||||
|
const buildDSAv2 = require("../../scripts/buildDSAv2");
|
||||||
|
const encodeSpells = require("../../scripts/encodeSpells.js");
|
||||||
|
|
||||||
|
const addresses = require("../../scripts/constant/addresses");
|
||||||
|
const abis = require("../../scripts/constant/abis");
|
||||||
|
|
||||||
|
const FeeAmount = {
|
||||||
|
LOW: 500,
|
||||||
|
MEDIUM: 3000,
|
||||||
|
HIGH: 10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TICK_SPACINGS = {
|
||||||
|
500: 10,
|
||||||
|
3000: 60,
|
||||||
|
10000: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7";
|
||||||
|
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f";
|
||||||
|
|
||||||
|
describe("UniswapV3", function() {
|
||||||
|
const connectorName = "UniswapV3-v1";
|
||||||
|
let dsaWallet0;
|
||||||
|
let masterSigner;
|
||||||
|
let instaConnectorsV2;
|
||||||
|
let connector;
|
||||||
|
let nftManager;
|
||||||
|
|
||||||
|
const wallets = provider.getWallets();
|
||||||
|
const [wallet0, wallet1, wallet2, wallet3] = wallets;
|
||||||
|
before(async () => {
|
||||||
|
masterSigner = await getMasterSigner(wallet3);
|
||||||
|
instaConnectorsV2 = await ethers.getContractAt(
|
||||||
|
abis.core.connectorsV2,
|
||||||
|
addresses.core.connectorsV2
|
||||||
|
);
|
||||||
|
connector = await deployAndEnableConnector({
|
||||||
|
connectorName,
|
||||||
|
contractArtifact: connectV2UniswapV3Artifacts,
|
||||||
|
signer: masterSigner,
|
||||||
|
connectors: instaConnectorsV2,
|
||||||
|
});
|
||||||
|
console.log("Connector address", connector.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should have contracts deployed.", async function() {
|
||||||
|
expect(!!instaConnectorsV2.address).to.be.true;
|
||||||
|
expect(!!connector.address).to.be.true;
|
||||||
|
expect(!!masterSigner.address).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("DSA wallet setup", function() {
|
||||||
|
it("Should build DSA v2", async function() {
|
||||||
|
dsaWallet0 = await buildDSAv2(wallet0.address);
|
||||||
|
expect(!!dsaWallet0.address).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Perfrom a swap", async function() {
|
||||||
|
await wallet0.sendTransaction({
|
||||||
|
to: dsaWallet0.address,
|
||||||
|
value: ethers.utils.parseEther("10"),
|
||||||
|
});
|
||||||
|
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
|
||||||
|
ethers.utils.parseEther("10")
|
||||||
|
);
|
||||||
|
|
||||||
|
await addLiquidity(
|
||||||
|
"usdt",
|
||||||
|
dsaWallet0.address,
|
||||||
|
ethers.utils.parseEther("100000")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
128
test/uniswap-sell-beta/uniswap-sell-beta.js
Normal file
128
test/uniswap-sell-beta/uniswap-sell-beta.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
const { expect } = require("chai");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
const { web3, deployments, waffle, ethers } = hre;
|
||||||
|
const { provider, deployContract } = waffle;
|
||||||
|
|
||||||
|
const USDC_ADDR = "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8";
|
||||||
|
const WETH_ADDR = "0x82af49447d8a07e3bd95bd0d56f35241523fbab1";
|
||||||
|
|
||||||
|
describe("Uniswap-sell-beta", function() {
|
||||||
|
let UniswapSellBeta, uniswapSellBeta;
|
||||||
|
|
||||||
|
async function setBalance(address) {
|
||||||
|
await network.provider.send("hardhat_setBalance", [
|
||||||
|
address,
|
||||||
|
ethers.utils.parseEther("10.0").toHexString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function impersonate(owner, account, token0, decimals) {
|
||||||
|
const tokenArtifact = await artifacts.readArtifact(
|
||||||
|
"@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20"
|
||||||
|
);
|
||||||
|
|
||||||
|
setBalance(owner);
|
||||||
|
setBalance(account);
|
||||||
|
|
||||||
|
await hre.network.provider.request({
|
||||||
|
method: "hardhat_impersonateAccount",
|
||||||
|
params: [account],
|
||||||
|
});
|
||||||
|
|
||||||
|
const signer = await ethers.getSigner(account);
|
||||||
|
|
||||||
|
const token = new ethers.Contract(
|
||||||
|
token0,
|
||||||
|
tokenArtifact.abi,
|
||||||
|
ethers.provider
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log((await token.balanceOf(account)).toString());
|
||||||
|
|
||||||
|
await token
|
||||||
|
.connect(signer)
|
||||||
|
.transfer(owner, ethers.utils.parseUnits("10", decimals));
|
||||||
|
|
||||||
|
await hre.network.provider.request({
|
||||||
|
method: "hardhat_stopImpersonatingAccount",
|
||||||
|
params: [account],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const account0 = "0x36cc7B13029B5DEe4034745FB4F24034f3F2ffc6";
|
||||||
|
const account1 = "0xce2cc46682e9c6d5f174af598fb4931a9c0be68e";
|
||||||
|
|
||||||
|
const [owner, add1, add2] = await ethers.getSigners();
|
||||||
|
await impersonate(owner.address, account1, USDC_ADDR, 6);
|
||||||
|
await impersonate(owner.address, account0, WETH_ADDR, 18);
|
||||||
|
|
||||||
|
UniswapSellBeta = await ethers.getContractFactory(
|
||||||
|
"ConnectV2UniswapSellBeta"
|
||||||
|
);
|
||||||
|
uniswapSellBeta = await UniswapSellBeta.deploy();
|
||||||
|
await uniswapSellBeta.deployed();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should have contracts deployed.", async function() {
|
||||||
|
expect(uniswapSellBeta.address).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should swap WETH with USDC", async () => {
|
||||||
|
const [owner, add1, add2] = await ethers.getSigners();
|
||||||
|
|
||||||
|
const tokenArtifact = await artifacts.readArtifact(
|
||||||
|
"@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20"
|
||||||
|
);
|
||||||
|
|
||||||
|
const token = new ethers.Contract(
|
||||||
|
WETH_ADDR,
|
||||||
|
tokenArtifact.abi,
|
||||||
|
ethers.provider
|
||||||
|
);
|
||||||
|
|
||||||
|
const signer = await ethers.getSigner(owner.address);
|
||||||
|
|
||||||
|
await token
|
||||||
|
.connect(signer)
|
||||||
|
.transfer(uniswapSellBeta.address, ethers.utils.parseUnits("10.0", 18));
|
||||||
|
|
||||||
|
const tx = await uniswapSellBeta.sell(
|
||||||
|
WETH_ADDR,
|
||||||
|
USDC_ADDR,
|
||||||
|
3000,
|
||||||
|
ethers.utils.parseUnits("10.0", 18),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
// console.log(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should swap USDC with WETH", async () => {
|
||||||
|
const [owner, add1, add2] = await ethers.getSigners();
|
||||||
|
|
||||||
|
const tokenArtifact = await artifacts.readArtifact(
|
||||||
|
"@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20"
|
||||||
|
);
|
||||||
|
|
||||||
|
const token = new ethers.Contract(
|
||||||
|
USDC_ADDR,
|
||||||
|
tokenArtifact.abi,
|
||||||
|
ethers.provider
|
||||||
|
);
|
||||||
|
|
||||||
|
const signer = await ethers.getSigner(owner.address);
|
||||||
|
|
||||||
|
await token
|
||||||
|
.connect(signer)
|
||||||
|
.transfer(uniswapSellBeta.address, ethers.utils.parseUnits("10.0", 6));
|
||||||
|
|
||||||
|
const tx = await uniswapSellBeta.sell(
|
||||||
|
USDC_ADDR,
|
||||||
|
WETH_ADDR,
|
||||||
|
3000,
|
||||||
|
ethers.utils.parseUnits("10.0", 6),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
// console.log(tx);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,20 +1,22 @@
|
||||||
const { expect } = require("chai");
|
const { expect } = require("chai");
|
||||||
const hre = require("hardhat");
|
const hre = require("hardhat");
|
||||||
const { web3, deployments, waffle, ethers } = hre;
|
const { web3, deployments, waffle, ethers } = hre;
|
||||||
const { provider, deployContract } = waffle
|
const { provider, deployContract } = waffle;
|
||||||
|
|
||||||
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
|
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
|
||||||
const buildDSAv2 = require("../../scripts/buildDSAv2")
|
const buildDSAv2 = require("../../scripts/buildDSAv2");
|
||||||
const encodeSpells = require("../../scripts/encodeSpells.js")
|
const encodeSpells = require("../../scripts/encodeSpells.js");
|
||||||
const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js")
|
const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js");
|
||||||
const getMasterSigner = require("../../scripts/getMasterSigner")
|
const getMasterSigner = require("../../scripts/getMasterSigner");
|
||||||
const addLiquidity = require("../../scripts/addLiquidity");
|
const addLiquidity = require("../../scripts/addLiquidity");
|
||||||
|
|
||||||
const addresses = require("../../scripts/constant/addresses");
|
const addresses = require("../../scripts/constant/addresses");
|
||||||
const abis = require("../../scripts/constant/abis");
|
const abis = require("../../scripts/constant/abis");
|
||||||
const constants = require("../../scripts/constant/constant");
|
const constants = require("../../scripts/constant/constant");
|
||||||
const tokens = require("../../scripts/constant/tokens");
|
const tokens = require("../../scripts/constant/tokens");
|
||||||
const { abi: nftManagerAbi } = require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json")
|
const {
|
||||||
|
abi: nftManagerAbi,
|
||||||
|
} = require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json");
|
||||||
|
|
||||||
const connectV2UniswapV3Artifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/v3/main.sol/ConnectV2UniswapV3.json");
|
const connectV2UniswapV3Artifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/v3/main.sol/ConnectV2UniswapV3.json");
|
||||||
const { eth } = require("../../scripts/constant/tokens");
|
const { eth } = require("../../scripts/constant/tokens");
|
||||||
|
@ -24,32 +26,32 @@ const FeeAmount = {
|
||||||
LOW: 500,
|
LOW: 500,
|
||||||
MEDIUM: 3000,
|
MEDIUM: 3000,
|
||||||
HIGH: 10000,
|
HIGH: 10000,
|
||||||
}
|
};
|
||||||
|
|
||||||
const TICK_SPACINGS = {
|
const TICK_SPACINGS = {
|
||||||
500: 10,
|
500: 10,
|
||||||
3000: 60,
|
3000: 60,
|
||||||
10000: 200
|
10000: 200,
|
||||||
}
|
};
|
||||||
|
|
||||||
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"
|
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7";
|
||||||
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"
|
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f";
|
||||||
|
|
||||||
let tokenIds = []
|
let tokenIds = [];
|
||||||
let liquidities = []
|
let liquidities = [];
|
||||||
const abiCoder = ethers.utils.defaultAbiCoder
|
const abiCoder = ethers.utils.defaultAbiCoder;
|
||||||
|
|
||||||
describe("UniswapV3", function () {
|
describe("UniswapV3", function() {
|
||||||
const connectorName = "UniswapV3-v1"
|
const connectorName = "UniswapV3-v1";
|
||||||
|
|
||||||
let dsaWallet0
|
let dsaWallet0;
|
||||||
let masterSigner;
|
let masterSigner;
|
||||||
let instaConnectorsV2;
|
let instaConnectorsV2;
|
||||||
let connector;
|
let connector;
|
||||||
let nftManager;
|
let nftManager;
|
||||||
|
|
||||||
const wallets = provider.getWallets()
|
const wallets = provider.getWallets();
|
||||||
const [wallet0, wallet1, wallet2, wallet3] = wallets
|
const [wallet0, wallet1, wallet2, wallet3] = wallets;
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await hre.network.provider.request({
|
await hre.network.provider.request({
|
||||||
method: "hardhat_reset",
|
method: "hardhat_reset",
|
||||||
|
@ -62,61 +64,78 @@ describe("UniswapV3", function () {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
masterSigner = await getMasterSigner(wallet3)
|
masterSigner = await getMasterSigner(wallet3);
|
||||||
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
|
instaConnectorsV2 = await ethers.getContractAt(
|
||||||
nftManager = await ethers.getContractAt(nftManagerAbi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88");
|
abis.core.connectorsV2,
|
||||||
|
addresses.core.connectorsV2
|
||||||
|
);
|
||||||
|
nftManager = await ethers.getContractAt(
|
||||||
|
nftManagerAbi,
|
||||||
|
"0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
|
||||||
|
);
|
||||||
connector = await deployAndEnableConnector({
|
connector = await deployAndEnableConnector({
|
||||||
connectorName,
|
connectorName,
|
||||||
contractArtifact: connectV2UniswapV3Artifacts,
|
contractArtifact: connectV2UniswapV3Artifacts,
|
||||||
signer: masterSigner,
|
signer: masterSigner,
|
||||||
connectors: instaConnectorsV2
|
connectors: instaConnectorsV2,
|
||||||
})
|
});
|
||||||
console.log("Connector address", connector.address)
|
console.log("Connector address", connector.address);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("Should have contracts deployed.", async function () {
|
it("Should have contracts deployed.", async function() {
|
||||||
expect(!!instaConnectorsV2.address).to.be.true;
|
expect(!!instaConnectorsV2.address).to.be.true;
|
||||||
expect(!!connector.address).to.be.true;
|
expect(!!connector.address).to.be.true;
|
||||||
expect(!!masterSigner.address).to.be.true;
|
expect(!!masterSigner.address).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("DSA wallet setup", function () {
|
describe("DSA wallet setup", function() {
|
||||||
it("Should build DSA v2", async function () {
|
it("Should build DSA v2", async function() {
|
||||||
dsaWallet0 = await buildDSAv2(wallet0.address)
|
dsaWallet0 = await buildDSAv2(wallet0.address);
|
||||||
expect(!!dsaWallet0.address).to.be.true;
|
expect(!!dsaWallet0.address).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Deposit ETH & DAI into DSA wallet", async function () {
|
it("Deposit ETH & DAI into DSA wallet", async function() {
|
||||||
await wallet0.sendTransaction({
|
await wallet0.sendTransaction({
|
||||||
to: dsaWallet0.address,
|
to: dsaWallet0.address,
|
||||||
value: ethers.utils.parseEther("10")
|
value: ethers.utils.parseEther("10"),
|
||||||
});
|
});
|
||||||
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
|
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
|
||||||
|
ethers.utils.parseEther("10")
|
||||||
|
);
|
||||||
|
|
||||||
await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000"));
|
await addLiquidity(
|
||||||
|
"dai",
|
||||||
|
dsaWallet0.address,
|
||||||
|
ethers.utils.parseEther("100000")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Deposit ETH & USDT into DSA wallet", async function () {
|
it("Deposit ETH & USDT into DSA wallet", async function() {
|
||||||
await wallet0.sendTransaction({
|
await wallet0.sendTransaction({
|
||||||
to: dsaWallet0.address,
|
to: dsaWallet0.address,
|
||||||
value: ethers.utils.parseEther("10")
|
value: ethers.utils.parseEther("10"),
|
||||||
});
|
});
|
||||||
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
|
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
|
||||||
|
ethers.utils.parseEther("10")
|
||||||
|
);
|
||||||
|
|
||||||
await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000"));
|
await addLiquidity(
|
||||||
|
"usdt",
|
||||||
|
dsaWallet0.address,
|
||||||
|
ethers.utils.parseEther("100000")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Main", function () {
|
describe("Main", function() {
|
||||||
|
it("Should mint successfully", async function() {
|
||||||
|
const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH
|
||||||
|
const daiAmount = ethers.utils.parseEther("400"); // 1 ETH
|
||||||
|
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12); // 1 ETH
|
||||||
|
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
||||||
|
|
||||||
it("Should mint successfully", async function () {
|
const getIds = ["0", "0"];
|
||||||
const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH
|
const setId = "0";
|
||||||
const daiAmount = ethers.utils.parseEther("400") // 1 ETH
|
|
||||||
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12) // 1 ETH
|
|
||||||
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
|
||||||
|
|
||||||
const getIds = ["0", "0"]
|
|
||||||
const setId = "0"
|
|
||||||
|
|
||||||
const spells = [
|
const spells = [
|
||||||
{
|
{
|
||||||
|
@ -132,7 +151,7 @@ describe("UniswapV3", function () {
|
||||||
daiAmount,
|
daiAmount,
|
||||||
"500000000000000000",
|
"500000000000000000",
|
||||||
getIds,
|
getIds,
|
||||||
setId
|
setId,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -148,7 +167,7 @@ describe("UniswapV3", function () {
|
||||||
usdtAmount,
|
usdtAmount,
|
||||||
"300000000000000000",
|
"300000000000000000",
|
||||||
getIds,
|
getIds,
|
||||||
setId
|
setId,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -164,17 +183,36 @@ describe("UniswapV3", function () {
|
||||||
usdtAmount,
|
usdtAmount,
|
||||||
"300000000000000000",
|
"300000000000000000",
|
||||||
getIds,
|
getIds,
|
||||||
setId
|
setId,
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
const tx = await dsaWallet0
|
||||||
let receipt = await tx.wait()
|
.connect(wallet0)
|
||||||
|
.cast(...encodeSpells(spells), wallet1.address);
|
||||||
|
let receipt = await tx.wait();
|
||||||
let castEvent = new Promise((resolve, reject) => {
|
let castEvent = new Promise((resolve, reject) => {
|
||||||
dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => {
|
dsaWallet0.on(
|
||||||
const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[0]);
|
"LogCast",
|
||||||
const params1 = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[2]);
|
(
|
||||||
|
origin,
|
||||||
|
sender,
|
||||||
|
value,
|
||||||
|
targetNames,
|
||||||
|
targets,
|
||||||
|
eventNames,
|
||||||
|
eventParams,
|
||||||
|
event
|
||||||
|
) => {
|
||||||
|
const params = abiCoder.decode(
|
||||||
|
["uint256", "uint256", "uint256", "uint256", "int24", "int24"],
|
||||||
|
eventParams[0]
|
||||||
|
);
|
||||||
|
const params1 = abiCoder.decode(
|
||||||
|
["uint256", "uint256", "uint256", "uint256", "int24", "int24"],
|
||||||
|
eventParams[2]
|
||||||
|
);
|
||||||
tokenIds.push(params[0]);
|
tokenIds.push(params[0]);
|
||||||
tokenIds.push(params1[0]);
|
tokenIds.push(params1[0]);
|
||||||
liquidities.push(params[1]);
|
liquidities.push(params[1]);
|
||||||
|
@ -183,28 +221,29 @@ describe("UniswapV3", function () {
|
||||||
resolve({
|
resolve({
|
||||||
eventNames,
|
eventNames,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(new Error('timeout'));
|
reject(new Error("timeout"));
|
||||||
}, 60000)
|
}, 60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
let event = await castEvent
|
let event = await castEvent;
|
||||||
|
|
||||||
const data = await nftManager.positions(tokenIds[0])
|
const data = await nftManager.positions(tokenIds[0]);
|
||||||
|
|
||||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||||
}).timeout(10000000000);
|
}).timeout(10000000000);
|
||||||
|
|
||||||
it("Should deposit successfully", async function () {
|
it("Should deposit successfully", async function() {
|
||||||
const daiAmount = ethers.utils.parseEther("400") // 1 ETH
|
const daiAmount = ethers.utils.parseEther("400"); // 1 ETH
|
||||||
const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH
|
const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH
|
||||||
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12) // 1 ETH
|
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12); // 1 ETH
|
||||||
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
||||||
|
|
||||||
const getIds = ["0", "0"]
|
const getIds = ["0", "0"];
|
||||||
const setId = "0"
|
const setId = "0";
|
||||||
|
|
||||||
const spells = [
|
const spells = [
|
||||||
{
|
{
|
||||||
|
@ -216,120 +255,120 @@ describe("UniswapV3", function () {
|
||||||
ethAmount,
|
ethAmount,
|
||||||
"500000000000000000",
|
"500000000000000000",
|
||||||
getIds,
|
getIds,
|
||||||
setId
|
setId,
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
const tx = await dsaWallet0
|
||||||
const receipt = await tx.wait()
|
.connect(wallet0)
|
||||||
|
.cast(...encodeSpells(spells), wallet1.address);
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
|
||||||
let castEvent = new Promise((resolve, reject) => {
|
let castEvent = new Promise((resolve, reject) => {
|
||||||
dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => {
|
dsaWallet0.on(
|
||||||
const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256"], eventParams[0]);
|
"LogCast",
|
||||||
|
(
|
||||||
|
origin,
|
||||||
|
sender,
|
||||||
|
value,
|
||||||
|
targetNames,
|
||||||
|
targets,
|
||||||
|
eventNames,
|
||||||
|
eventParams,
|
||||||
|
event
|
||||||
|
) => {
|
||||||
|
const params = abiCoder.decode(
|
||||||
|
["uint256", "uint256", "uint256", "uint256"],
|
||||||
|
eventParams[0]
|
||||||
|
);
|
||||||
liquidities[0] = liquidities[0].add(params[1]);
|
liquidities[0] = liquidities[0].add(params[1]);
|
||||||
event.removeListener();
|
event.removeListener();
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
eventNames,
|
eventNames,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(new Error('timeout'));
|
reject(new Error("timeout"));
|
||||||
}, 60000)
|
}, 60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
let event = await castEvent
|
let event = await castEvent;
|
||||||
|
|
||||||
const data = await nftManager.positions(tokenIds[0])
|
const data = await nftManager.positions(tokenIds[0]);
|
||||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("Should withdraw successfully", async function () {
|
it("Should withdraw successfully", async function() {
|
||||||
|
const getId = "0";
|
||||||
|
const setIds = ["0", "0"];
|
||||||
|
|
||||||
const getId = "0"
|
const data = await nftManager.positions(tokenIds[0]);
|
||||||
const setIds = ["0", "0"]
|
let data1 = await nftManager.positions(tokenIds[1]);
|
||||||
|
|
||||||
const data = await nftManager.positions(tokenIds[0])
|
|
||||||
let data1 = await nftManager.positions(tokenIds[1])
|
|
||||||
|
|
||||||
const spells = [
|
const spells = [
|
||||||
{
|
{
|
||||||
connector: connectorName,
|
connector: connectorName,
|
||||||
method: "withdraw",
|
method: "withdraw",
|
||||||
args: [
|
args: [tokenIds[0], data.liquidity, 0, 0, getId, setIds],
|
||||||
tokenIds[0],
|
|
||||||
data.liquidity,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
getId,
|
|
||||||
setIds
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
connector: connectorName,
|
connector: connectorName,
|
||||||
method: "withdraw",
|
method: "withdraw",
|
||||||
args: [
|
args: [0, data1.liquidity, 0, 0, getId, setIds],
|
||||||
0,
|
|
||||||
data1.liquidity,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
getId,
|
|
||||||
setIds
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
const tx = await dsaWallet0
|
||||||
const receipt = await tx.wait()
|
.connect(wallet0)
|
||||||
|
.cast(...encodeSpells(spells), wallet1.address);
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
|
||||||
data1 = await nftManager.positions(tokenIds[1])
|
data1 = await nftManager.positions(tokenIds[1]);
|
||||||
expect(data1.liquidity.toNumber()).to.be.equals(0);
|
expect(data1.liquidity.toNumber()).to.be.equals(0);
|
||||||
})
|
});
|
||||||
|
|
||||||
it("Should collect successfully", async function () {
|
it("Should collect successfully", async function() {
|
||||||
|
const ethAmount = ethers.utils.parseEther("0.2"); // 1 ETH
|
||||||
const ethAmount = ethers.utils.parseEther("0.2") // 1 ETH
|
const daiAmount = ethers.utils.parseEther("800"); // 1 ETH
|
||||||
const daiAmount = ethers.utils.parseEther("800") // 1 ETH
|
const getIds = ["0", "0"];
|
||||||
const getIds = ["0", "0"]
|
const setIds = ["0", "0"];
|
||||||
const setIds = ["0", "0"]
|
|
||||||
|
|
||||||
const spells = [
|
const spells = [
|
||||||
{
|
{
|
||||||
connector: connectorName,
|
connector: connectorName,
|
||||||
method: "collect",
|
method: "collect",
|
||||||
args: [
|
args: [tokenIds[0], daiAmount, ethAmount, getIds, setIds],
|
||||||
tokenIds[0],
|
},
|
||||||
daiAmount,
|
];
|
||||||
ethAmount,
|
|
||||||
getIds,
|
|
||||||
setIds
|
|
||||||
],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
const tx = await dsaWallet0
|
||||||
const receipt = await tx.wait()
|
.connect(wallet0)
|
||||||
})
|
.cast(...encodeSpells(spells), wallet1.address);
|
||||||
|
const receipt = await tx.wait();
|
||||||
it("Should burn successfully", async function () {
|
});
|
||||||
|
|
||||||
|
it("Should burn successfully", async function() {
|
||||||
const spells = [
|
const spells = [
|
||||||
{
|
{
|
||||||
connector: connectorName,
|
connector: connectorName,
|
||||||
method: "burn",
|
method: "burn",
|
||||||
args: [
|
args: [tokenIds[0]],
|
||||||
tokenIds[0]
|
},
|
||||||
],
|
];
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
const tx = await dsaWallet0
|
||||||
const receipt = await tx.wait()
|
.connect(wallet0)
|
||||||
})
|
.cast(...encodeSpells(spells), wallet1.address);
|
||||||
})
|
const receipt = await tx.wait();
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const getMinTick = (tickSpacing) => Math.ceil(-887272 / tickSpacing) * tickSpacing
|
const getMinTick = (tickSpacing) =>
|
||||||
const getMaxTick = (tickSpacing) => Math.floor(887272 / tickSpacing) * tickSpacing
|
Math.ceil(-887272 / tickSpacing) * tickSpacing;
|
||||||
|
const getMaxTick = (tickSpacing) =>
|
||||||
|
Math.floor(887272 / tickSpacing) * tickSpacing;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user