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")),
|
||||
},
|
||||
avax: {
|
||||
url: 'https://api.avax.network/ext/bc/C/rpc',
|
||||
url: "https://api.avax.network/ext/bc/C/rpc",
|
||||
chainId: 43114,
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000,
|
||||
gasPrice: parseInt(utils.parseUnits("225", "gwei"))
|
||||
}
|
||||
gasPrice: parseInt(utils.parseUnits("225", "gwei")),
|
||||
},
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: ETHERSCAN_API_KEY,
|
||||
|
@ -97,4 +97,4 @@ module.exports = {
|
|||
mocha: {
|
||||
timeout: 100 * 1000,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
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,335 +1,374 @@
|
|||
const { expect } = require("chai");
|
||||
const hre = require("hardhat");
|
||||
const { web3, deployments, waffle, ethers } = hre;
|
||||
const { provider, deployContract } = waffle
|
||||
const { provider, deployContract } = waffle;
|
||||
|
||||
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
|
||||
const buildDSAv2 = require("../../scripts/buildDSAv2")
|
||||
const encodeSpells = require("../../scripts/encodeSpells.js")
|
||||
const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js")
|
||||
const getMasterSigner = require("../../scripts/getMasterSigner")
|
||||
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
|
||||
const buildDSAv2 = require("../../scripts/buildDSAv2");
|
||||
const encodeSpells = require("../../scripts/encodeSpells.js");
|
||||
const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js");
|
||||
const getMasterSigner = require("../../scripts/getMasterSigner");
|
||||
const addLiquidity = require("../../scripts/addLiquidity");
|
||||
|
||||
const addresses = require("../../scripts/constant/addresses");
|
||||
const abis = require("../../scripts/constant/abis");
|
||||
const constants = require("../../scripts/constant/constant");
|
||||
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 { eth } = require("../../scripts/constant/tokens");
|
||||
const { BigNumber } = require("ethers");
|
||||
|
||||
const FeeAmount = {
|
||||
LOW: 500,
|
||||
MEDIUM: 3000,
|
||||
HIGH: 10000,
|
||||
}
|
||||
LOW: 500,
|
||||
MEDIUM: 3000,
|
||||
HIGH: 10000,
|
||||
};
|
||||
|
||||
const TICK_SPACINGS = {
|
||||
500: 10,
|
||||
3000: 60,
|
||||
10000: 200
|
||||
}
|
||||
500: 10,
|
||||
3000: 60,
|
||||
10000: 200,
|
||||
};
|
||||
|
||||
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"
|
||||
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"
|
||||
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7";
|
||||
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f";
|
||||
|
||||
let tokenIds = []
|
||||
let liquidities = []
|
||||
const abiCoder = ethers.utils.defaultAbiCoder
|
||||
let tokenIds = [];
|
||||
let liquidities = [];
|
||||
const abiCoder = ethers.utils.defaultAbiCoder;
|
||||
|
||||
describe("UniswapV3", function () {
|
||||
const connectorName = "UniswapV3-v1"
|
||||
describe("UniswapV3", function() {
|
||||
const connectorName = "UniswapV3-v1";
|
||||
|
||||
let dsaWallet0
|
||||
let masterSigner;
|
||||
let instaConnectorsV2;
|
||||
let connector;
|
||||
let nftManager;
|
||||
let dsaWallet0;
|
||||
let masterSigner;
|
||||
let instaConnectorsV2;
|
||||
let connector;
|
||||
let nftManager;
|
||||
|
||||
const wallets = provider.getWallets()
|
||||
const [wallet0, wallet1, wallet2, wallet3] = wallets
|
||||
before(async () => {
|
||||
await hre.network.provider.request({
|
||||
method: "hardhat_reset",
|
||||
params: [
|
||||
{
|
||||
forking: {
|
||||
jsonRpcUrl: hre.config.networks.hardhat.forking.url,
|
||||
blockNumber: 13005785,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
masterSigner = await getMasterSigner(wallet3)
|
||||
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
|
||||
nftManager = await ethers.getContractAt(nftManagerAbi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88");
|
||||
connector = await deployAndEnableConnector({
|
||||
connectorName,
|
||||
contractArtifact: connectV2UniswapV3Artifacts,
|
||||
signer: masterSigner,
|
||||
connectors: instaConnectorsV2
|
||||
})
|
||||
console.log("Connector address", connector.address)
|
||||
})
|
||||
const wallets = provider.getWallets();
|
||||
const [wallet0, wallet1, wallet2, wallet3] = wallets;
|
||||
before(async () => {
|
||||
await hre.network.provider.request({
|
||||
method: "hardhat_reset",
|
||||
params: [
|
||||
{
|
||||
forking: {
|
||||
jsonRpcUrl: hre.config.networks.hardhat.forking.url,
|
||||
blockNumber: 13005785,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
masterSigner = await getMasterSigner(wallet3);
|
||||
instaConnectorsV2 = await ethers.getContractAt(
|
||||
abis.core.connectorsV2,
|
||||
addresses.core.connectorsV2
|
||||
);
|
||||
nftManager = await ethers.getContractAt(
|
||||
nftManagerAbi,
|
||||
"0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
|
||||
);
|
||||
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;
|
||||
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;
|
||||
});
|
||||
|
||||
describe("DSA wallet setup", function () {
|
||||
it("Should build DSA v2", async function () {
|
||||
dsaWallet0 = await buildDSAv2(wallet0.address)
|
||||
expect(!!dsaWallet0.address).to.be.true;
|
||||
});
|
||||
it("Deposit ETH & DAI into DSA wallet", 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")
|
||||
);
|
||||
|
||||
it("Deposit ETH & DAI into DSA wallet", 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("dai", dsaWallet0.address, ethers.utils.parseEther("100000"));
|
||||
});
|
||||
|
||||
it("Deposit ETH & USDT into DSA wallet", 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"));
|
||||
});
|
||||
await addLiquidity(
|
||||
"dai",
|
||||
dsaWallet0.address,
|
||||
ethers.utils.parseEther("100000")
|
||||
);
|
||||
});
|
||||
|
||||
describe("Main", function () {
|
||||
it("Deposit ETH & USDT into DSA wallet", 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")
|
||||
);
|
||||
|
||||
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"
|
||||
await addLiquidity(
|
||||
"usdt",
|
||||
dsaWallet0.address,
|
||||
ethers.utils.parseEther("100000")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const getIds = ["0", "0"]
|
||||
const setId = "0"
|
||||
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";
|
||||
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
ethAddress,
|
||||
DAI_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
ethAmount,
|
||||
daiAmount,
|
||||
"500000000000000000",
|
||||
getIds,
|
||||
setId
|
||||
],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
DAI_ADDR,
|
||||
USDT_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
daiAmount,
|
||||
usdtAmount,
|
||||
"300000000000000000",
|
||||
getIds,
|
||||
setId
|
||||
],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
ethAddress,
|
||||
USDT_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
ethAmount,
|
||||
usdtAmount,
|
||||
"300000000000000000",
|
||||
getIds,
|
||||
setId
|
||||
],
|
||||
}
|
||||
]
|
||||
const getIds = ["0", "0"];
|
||||
const setId = "0";
|
||||
|
||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
||||
let receipt = await tx.wait()
|
||||
let castEvent = new Promise((resolve, reject) => {
|
||||
dsaWallet0.on('LogCast', (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(params1[0]);
|
||||
liquidities.push(params[1]);
|
||||
event.removeListener();
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
ethAddress,
|
||||
DAI_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
ethAmount,
|
||||
daiAmount,
|
||||
"500000000000000000",
|
||||
getIds,
|
||||
setId,
|
||||
],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
DAI_ADDR,
|
||||
USDT_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
daiAmount,
|
||||
usdtAmount,
|
||||
"300000000000000000",
|
||||
getIds,
|
||||
setId,
|
||||
],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "mint",
|
||||
args: [
|
||||
ethAddress,
|
||||
USDT_ADDR,
|
||||
FeeAmount.MEDIUM,
|
||||
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
|
||||
ethAmount,
|
||||
usdtAmount,
|
||||
"300000000000000000",
|
||||
getIds,
|
||||
setId,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
resolve({
|
||||
eventNames,
|
||||
});
|
||||
});
|
||||
const tx = await dsaWallet0
|
||||
.connect(wallet0)
|
||||
.cast(...encodeSpells(spells), wallet1.address);
|
||||
let receipt = await tx.wait();
|
||||
let castEvent = new Promise((resolve, reject) => {
|
||||
dsaWallet0.on(
|
||||
"LogCast",
|
||||
(
|
||||
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(params1[0]);
|
||||
liquidities.push(params[1]);
|
||||
event.removeListener();
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('timeout'));
|
||||
}, 60000)
|
||||
resolve({
|
||||
eventNames,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
let event = await castEvent
|
||||
setTimeout(() => {
|
||||
reject(new Error("timeout"));
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
const data = await nftManager.positions(tokenIds[0])
|
||||
let event = await castEvent;
|
||||
|
||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||
}).timeout(10000000000);
|
||||
const data = await nftManager.positions(tokenIds[0]);
|
||||
|
||||
it("Should deposit successfully", async function () {
|
||||
const daiAmount = ethers.utils.parseEther("400") // 1 ETH
|
||||
const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH
|
||||
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12) // 1 ETH
|
||||
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||
}).timeout(10000000000);
|
||||
|
||||
const getIds = ["0", "0"]
|
||||
const setId = "0"
|
||||
it("Should deposit successfully", async function() {
|
||||
const daiAmount = ethers.utils.parseEther("400"); // 1 ETH
|
||||
const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH
|
||||
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12); // 1 ETH
|
||||
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
||||
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "deposit",
|
||||
args: [
|
||||
tokenIds[0],
|
||||
daiAmount,
|
||||
ethAmount,
|
||||
"500000000000000000",
|
||||
getIds,
|
||||
setId
|
||||
],
|
||||
}
|
||||
]
|
||||
const getIds = ["0", "0"];
|
||||
const setId = "0";
|
||||
|
||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
||||
const receipt = await tx.wait()
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "deposit",
|
||||
args: [
|
||||
tokenIds[0],
|
||||
daiAmount,
|
||||
ethAmount,
|
||||
"500000000000000000",
|
||||
getIds,
|
||||
setId,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let castEvent = new Promise((resolve, reject) => {
|
||||
dsaWallet0.on('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]);
|
||||
event.removeListener();
|
||||
const tx = await dsaWallet0
|
||||
.connect(wallet0)
|
||||
.cast(...encodeSpells(spells), wallet1.address);
|
||||
const receipt = await tx.wait();
|
||||
|
||||
resolve({
|
||||
eventNames,
|
||||
});
|
||||
});
|
||||
let castEvent = new Promise((resolve, reject) => {
|
||||
dsaWallet0.on(
|
||||
"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]);
|
||||
event.removeListener();
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('timeout'));
|
||||
}, 60000)
|
||||
resolve({
|
||||
eventNames,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
let event = await castEvent
|
||||
setTimeout(() => {
|
||||
reject(new Error("timeout"));
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
const data = await nftManager.positions(tokenIds[0])
|
||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||
})
|
||||
let event = await castEvent;
|
||||
|
||||
it("Should withdraw successfully", async function () {
|
||||
const data = await nftManager.positions(tokenIds[0]);
|
||||
expect(data.liquidity).to.be.equals(liquidities[0]);
|
||||
});
|
||||
|
||||
const getId = "0"
|
||||
const setIds = ["0", "0"]
|
||||
it("Should withdraw successfully", async function() {
|
||||
const getId = "0";
|
||||
const setIds = ["0", "0"];
|
||||
|
||||
const data = await nftManager.positions(tokenIds[0])
|
||||
let data1 = await nftManager.positions(tokenIds[1])
|
||||
const data = await nftManager.positions(tokenIds[0]);
|
||||
let data1 = await nftManager.positions(tokenIds[1]);
|
||||
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "withdraw",
|
||||
args: [
|
||||
tokenIds[0],
|
||||
data.liquidity,
|
||||
0,
|
||||
0,
|
||||
getId,
|
||||
setIds
|
||||
],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "withdraw",
|
||||
args: [
|
||||
0,
|
||||
data1.liquidity,
|
||||
0,
|
||||
0,
|
||||
getId,
|
||||
setIds
|
||||
],
|
||||
},
|
||||
]
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "withdraw",
|
||||
args: [tokenIds[0], data.liquidity, 0, 0, getId, setIds],
|
||||
},
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "withdraw",
|
||||
args: [0, data1.liquidity, 0, 0, getId, setIds],
|
||||
},
|
||||
];
|
||||
|
||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
||||
const receipt = await tx.wait()
|
||||
const tx = await dsaWallet0
|
||||
.connect(wallet0)
|
||||
.cast(...encodeSpells(spells), wallet1.address);
|
||||
const receipt = await tx.wait();
|
||||
|
||||
data1 = await nftManager.positions(tokenIds[1])
|
||||
expect(data1.liquidity.toNumber()).to.be.equals(0);
|
||||
})
|
||||
data1 = await nftManager.positions(tokenIds[1]);
|
||||
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 daiAmount = ethers.utils.parseEther("800"); // 1 ETH
|
||||
const getIds = ["0", "0"];
|
||||
const setIds = ["0", "0"];
|
||||
|
||||
const ethAmount = ethers.utils.parseEther("0.2") // 1 ETH
|
||||
const daiAmount = ethers.utils.parseEther("800") // 1 ETH
|
||||
const getIds = ["0", "0"]
|
||||
const setIds = ["0", "0"]
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "collect",
|
||||
args: [tokenIds[0], daiAmount, ethAmount, getIds, setIds],
|
||||
},
|
||||
];
|
||||
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "collect",
|
||||
args: [
|
||||
tokenIds[0],
|
||||
daiAmount,
|
||||
ethAmount,
|
||||
getIds,
|
||||
setIds
|
||||
],
|
||||
}
|
||||
]
|
||||
const tx = await dsaWallet0
|
||||
.connect(wallet0)
|
||||
.cast(...encodeSpells(spells), wallet1.address);
|
||||
const receipt = await tx.wait();
|
||||
});
|
||||
|
||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
||||
const receipt = await tx.wait()
|
||||
})
|
||||
it("Should burn successfully", async function() {
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "burn",
|
||||
args: [tokenIds[0]],
|
||||
},
|
||||
];
|
||||
|
||||
it("Should burn successfully", async function () {
|
||||
const tx = await dsaWallet0
|
||||
.connect(wallet0)
|
||||
.cast(...encodeSpells(spells), wallet1.address);
|
||||
const receipt = await tx.wait();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const spells = [
|
||||
{
|
||||
connector: connectorName,
|
||||
method: "burn",
|
||||
args: [
|
||||
tokenIds[0]
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
|
||||
const receipt = await tx.wait()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const getMinTick = (tickSpacing) => Math.ceil(-887272 / tickSpacing) * tickSpacing
|
||||
const getMaxTick = (tickSpacing) => Math.floor(887272 / tickSpacing) * tickSpacing
|
||||
const getMinTick = (tickSpacing) =>
|
||||
Math.ceil(-887272 / tickSpacing) * tickSpacing;
|
||||
const getMaxTick = (tickSpacing) =>
|
||||
Math.floor(887272 / tickSpacing) * tickSpacing;
|
||||
|
|
Loading…
Reference in New Issue
Block a user