Merge branch 'main' into feat/quickswap

This commit is contained in:
0xPradyuman 2021-12-17 00:54:48 +05:30 committed by GitHub
commit 8910559a21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1470 additions and 3 deletions

View File

@ -96,7 +96,7 @@ abstract contract OneInch is OneInchResolverHelpers {
* @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param sellAmt The amount of the token to sell.
* @param unitAmt The amount of buyAmt/sellAmt with slippage.
* @param callData Data from 1inch API.
* @param callData Data from 1inch API. You can generate calldata for calling 1inch route for exchange: <a href="https://api.1inch.exchange/swagger/ethereum/#/Swap/SwapFactoryCommonController_getSwap" target="_blank">here </a>
* @param setId ID stores the amount of token brought.
*/
function sell(

View File

@ -0,0 +1,34 @@
pragma solidity ^0.7.0;
contract Events {
event LogMint(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amtA,
uint256 amtB,
int24 tickLower,
int24 tickUpper
);
event LogDeposit(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
);
event LogWithdraw(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
);
event LogCollect(
uint256 tokenId,
uint256 amountA,
uint256 amountB
);
event LogBurnPosition(uint256 tokenId);
}

View File

@ -0,0 +1,287 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import {TokenInterface} from "../../../common/interfaces.sol";
import {DSMath} from "../../../common/math.sol";
import {Basic} from "../../../common/basic.sol";
import "./interface.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev uniswap v3 NFT Position Manager & Swap Router
*/
INonfungiblePositionManager constant nftManager =
INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
ISwapRouter constant swapRouter =
ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
struct MintParams {
address tokenA;
address tokenB;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amtA;
uint256 amtB;
uint256 slippage;
}
/**
* @dev Get Last NFT Index
* @param user: User address
*/
function _getLastNftId(address user)
internal
view
returns (uint256 tokenId)
{
uint256 len = nftManager.balanceOf(user);
tokenId = nftManager.tokenOfOwnerByIndex(user, len - 1);
}
function getMinAmount(
TokenInterface token,
uint256 amt,
uint256 slippage
) internal view returns (uint256 minAmt) {
uint256 _amt18 = convertTo18(token.decimals(), amt);
minAmt = wmul(_amt18, sub(WAD, slippage));
minAmt = convert18ToDec(token.decimals(), minAmt);
}
function sortTokenAddress(address _token0, address _token1)
internal
view
returns (address token0, address token1)
{
if (_token0 > _token1) {
(token0, token1) = (_token1, _token0);
} else {
(token0, token1) = (_token0, _token1);
}
}
/**
* @dev Mint function which interact with Uniswap v3
*/
function _mint(MintParams memory params)
internal
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amountA,
uint256 amountB
)
{
(TokenInterface _token0, TokenInterface _token1) = changeMaticAddress(
params.tokenA,
params.tokenB
);
uint256 _amount0 = params.amtA == uint256(-1)
? getTokenBal(TokenInterface(params.tokenA))
: params.amtA;
uint256 _amount1 = params.amtB == uint256(-1)
? getTokenBal(TokenInterface(params.tokenB))
: params.amtB;
convertMaticToWmatic(address(_token0) == wmaticAddr, _token0, _amount0);
convertMaticToWmatic(address(_token1) == wmaticAddr, _token1, _amount1);
approve(_token0, address(nftManager), _amount0);
approve(_token1, address(nftManager), _amount1);
{
(address token0, ) = sortTokenAddress(
address(_token0),
address(_token1)
);
if (token0 != address(_token0)) {
(_token0, _token1) = (_token1, _token0);
(_amount0, _amount1) = (_amount1, _amount0);
}
}
uint256 _minAmt0 = getMinAmount(_token0, _amount0, params.slippage);
uint256 _minAmt1 = getMinAmount(_token1, _amount1, params.slippage);
INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams(
address(_token0),
address(_token1),
params.fee,
params.tickLower,
params.tickUpper,
_amount0,
_amount1,
_minAmt0,
_minAmt1,
address(this),
block.timestamp
);
(tokenId, liquidity, amountA, amountB) = nftManager.mint(params);
}
function getNftTokenPairAddresses(uint256 _tokenId)
internal
view
returns (address token0, address token1)
{
(bool success, bytes memory data) = address(nftManager).staticcall(
abi.encodeWithSelector(nftManager.positions.selector, _tokenId)
);
require(success, "fetching positions failed");
{
(, , token0, token1, , , , ) = abi.decode(
data,
(
uint96,
address,
address,
address,
uint24,
int24,
int24,
uint128
)
);
}
}
/**
* @dev Check if token address is maticAddr and convert it to wmatic
*/
function _checkMATIC(
address _token0,
address _token1,
uint256 _amount0,
uint256 _amount1
) internal {
bool isMatic0 = _token0 == wmaticAddr;
bool isMatic1 = _token1 == wmaticAddr;
convertMaticToWmatic(isMatic0, TokenInterface(_token0), _amount0);
convertMaticToWmatic(isMatic1, TokenInterface(_token1), _amount1);
approve(TokenInterface(_token0), address(nftManager), _amount0);
approve(TokenInterface(_token1), address(nftManager), _amount1);
}
/**
* @dev addLiquidityWrapper function wrapper of _addLiquidity
*/
function _addLiquidityWrapper(
uint256 tokenId,
uint256 amountA,
uint256 amountB,
uint256 slippage
)
internal
returns (
uint256 liquidity,
uint256 amtA,
uint256 amtB
)
{
(address token0, address token1) = getNftTokenPairAddresses(tokenId);
(liquidity, amtA, amtB) = _addLiquidity(
tokenId,
token0,
token1,
amountA,
amountB,
slippage
);
}
/**
* @dev addLiquidity function which interact with Uniswap v3
*/
function _addLiquidity(
uint256 _tokenId,
address _token0,
address _token1,
uint256 _amount0,
uint256 _amount1,
uint256 _slippage
)
internal
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
)
{
_checkMATIC(_token0, _token1, _amount0, _amount1);
uint256 _amount0Min = getMinAmount(
TokenInterface(_token0),
_amount0,
_slippage
);
uint256 _amount1Min = getMinAmount(
TokenInterface(_token1),
_amount1,
_slippage
);
INonfungiblePositionManager.IncreaseLiquidityParams
memory params = INonfungiblePositionManager.IncreaseLiquidityParams(
_tokenId,
_amount0,
_amount1,
_amount0Min,
_amount1Min,
block.timestamp
);
(liquidity, amount0, amount1) = nftManager.increaseLiquidity(params);
}
/**
* @dev decreaseLiquidity function which interact with Uniswap v3
*/
function _decreaseLiquidity(
uint256 _tokenId,
uint128 _liquidity,
uint256 _amount0Min,
uint256 _amount1Min
) internal returns (uint256 amount0, uint256 amount1) {
INonfungiblePositionManager.DecreaseLiquidityParams
memory params = INonfungiblePositionManager.DecreaseLiquidityParams(
_tokenId,
_liquidity,
_amount0Min,
_amount1Min,
block.timestamp
);
(amount0, amount1) = nftManager.decreaseLiquidity(params);
}
/**
* @dev collect function which interact with Uniswap v3
*/
function _collect(
uint256 _tokenId,
uint128 _amount0Max,
uint128 _amount1Max
) internal returns (uint256 amount0, uint256 amount1) {
INonfungiblePositionManager.CollectParams
memory params = INonfungiblePositionManager.CollectParams(
_tokenId,
address(this),
_amount0Max,
_amount1Max
);
(amount0, amount1) = nftManager.collect(params);
}
/**
* @dev Burn Function
*/
function _burn(uint256 _tokenId) internal {
nftManager.burn(_tokenId);
}
}

View File

@ -0,0 +1,368 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params)
external
payable
returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params)
external
payable
returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params)
external
payable
returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params)
external
payable
returns (uint256 amountIn);
}
/// @title Creates and initializes V3 Pools
/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
/// require the pool to exist.
interface IPoolInitializer {
/// @notice Creates a new pool if it does not exist, then initializes if not initialized
/// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
/// @param token0 The contract address of token0 of the pool
/// @param token1 The contract address of token1 of the pool
/// @param fee The fee amount of the v3 pool for the specified token pair
/// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
/// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}
/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
/// @return Returns the address of the Uniswap V3 factory
function factory() external view returns (address);
/// @return Returns the address of WETH9
function WETH9() external view returns (address);
}
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
/// @param recipient The address receiving ETH
function unwrapWETH9(uint256 amountMinimum, address recipient)
external
payable;
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`
/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
/// that use ether for the input amount
function refundETH() external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to `recipient`
/// @param amountMinimum The minimum amount of token required for a transfer
/// @param recipient The destination address of the token
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}
/// @title ERC721 with permit
/// @notice Extension to ERC721 that includes a permit function for signature based approvals
interface IERC721Permit is IERC721 {
/// @notice The permit typehash used in the permit signature
/// @return The typehash for the permit
function PERMIT_TYPEHASH() external pure returns (bytes32);
/// @notice The domain separator used in the permit signature
/// @return The domain seperator used in encoding of permit signature
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Approve of a specific token ID for spending by spender via signature
/// @param spender The account that is being approved
/// @param tokenId The ID of the token that is being approved for spending
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is
IPoolInitializer,
IPeripheryPayments,
IPeripheryImmutableState,
IERC721Metadata,
IERC721Enumerable,
IERC721Permit
{
/// @notice Emitted when liquidity is increased for a position NFT
/// @dev Also emitted when a token is minted
/// @param tokenId The ID of the token for which liquidity was increased
/// @param liquidity The amount by which liquidity for the NFT position was increased
/// @param amount0 The amount of token0 that was paid for the increase in liquidity
/// @param amount1 The amount of token1 that was paid for the increase in liquidity
event IncreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when liquidity is decreased for a position NFT
/// @param tokenId The ID of the token for which liquidity was decreased
/// @param liquidity The amount by which liquidity for the NFT position was decreased
/// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
/// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
event DecreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when tokens are collected for a position NFT
/// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
/// @param tokenId The ID of the token for which underlying tokens were collected
/// @param recipient The address of the account that received the collected tokens
/// @param amount0 The amount of token0 owed to the position that was collected
/// @param amount1 The amount of token1 owed to the position that was collected
event Collect(
uint256 indexed tokenId,
address recipient,
uint256 amount0,
uint256 amount1
);
/// @notice Returns the position information associated with a given token ID.
/// @dev Throws if the token ID is not valid.
/// @param tokenId The ID of the token that represents the position
/// @return nonce The nonce for permits
/// @return operator The address that is approved for spending
/// @return token0 The address of the token0 for a specific pool
/// @return token1 The address of the token1 for a specific pool
/// @return fee The fee associated with the pool
/// @return tickLower The lower end of the tick range for the position
/// @return tickUpper The higher end of the tick range for the position
/// @return liquidity The liquidity of the position
/// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
/// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
/// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
/// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
/// @param params tokenId The ID of the token for which liquidity is being increased,
/// amount0Desired The desired amount of token0 to be spent,
/// amount1Desired The desired amount of token1 to be spent,
/// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 to acheive resulting liquidity
/// @return amount1 The amount of token1 to acheive resulting liquidity
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position
/// @param params tokenId The ID of the token for which liquidity is being decreased,
/// amount The amount by which liquidity will be decreased,
/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 accounted to the position's tokens owed
/// @return amount1 The amount of token1 accounted to the position's tokens owed
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
/// @param params tokenId The ID of the NFT for which tokens are being collected,
/// recipient The account that should receive the tokens,
/// amount0Max The maximum amount of token0 to collect,
/// amount1Max The maximum amount of token1 to collect
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(CollectParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
/// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
/// must be collected first.
/// @param tokenId The ID of the token that is being burned
function burn(uint256 tokenId) external payable;
}

View File

@ -0,0 +1,210 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
/**
* @title Uniswap v3.
* @dev Decentralized Exchange.
*/
import {TokenInterface} from "../../../common/interfaces.sol";
import {Helpers} from "./helpers.sol";
import {Events} from "./events.sol";
abstract contract UniswapResolver is Helpers, Events {
/**
* @dev Mint New Position
* @notice Mint New NFT LP Position
* @param tokenA tokenA addreess
* @param tokenB tokenB addreess
* @param fee fee percentage
* @param tickLower Lower tick
* @param tickUpper Upper tick
* @param amtA amount of tokenA
* @param amtB amount of tokenB
* @param slippage slippage percentage
* @param getIds ID to retrieve amtA
* @param setId ID stores the amount of LP token
*/
function mint(
address tokenA,
address tokenB,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint256 amtA,
uint256 amtB,
uint256 slippage,
uint256[] calldata getIds,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
MintParams memory params;
{
params = MintParams(
tokenA,
tokenB,
fee,
tickLower,
tickUpper,
amtA,
amtB,
slippage
);
}
params.amtA = getUint(getIds[0], params.amtA);
params.amtB = getUint(getIds[1], params.amtB);
(
uint256 _tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
) = _mint(params);
setUint(setId, liquidity);
_eventName = "LogMint(uint256,uint256,uint256,uint256,int24,int24)";
_eventParam = abi.encode(
_tokenId,
liquidity,
amountA,
amountB,
params.tickLower,
params.tickUpper
);
}
/**
* @dev Increase Liquidity
* @notice Increase Liquidity of NFT Position
* @param tokenId NFT LP Token ID.
* @param amountA tokenA amounts.
* @param amountB tokenB amounts.
* @param slippage slippage.
* @param getIds IDs to retrieve token amounts
* @param setId stores the liquidity amount
*/
function deposit(
uint256 tokenId,
uint256 amountA,
uint256 amountB,
uint256 slippage,
uint256[] calldata getIds,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
amountA = getUint(getIds[0], amountA);
amountB = getUint(getIds[1], amountB);
(
uint256 _liquidity,
uint256 _amtA,
uint256 _amtB
) = _addLiquidityWrapper(tokenId, amountA, amountB, slippage);
setUint(setId, _liquidity);
_eventName = "LogDeposit(uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
}
/**
* @dev Decrease Liquidity
* @notice Decrease Liquidity of NFT Position
* @param tokenId NFT LP Token ID.
* @param liquidity LP Token amount.
* @param amountAMin Min amount of tokenA.
* @param amountBMin Min amount of tokenB.
* @param getId ID to retrieve LP token amounts
* @param setIds stores the amount of output tokens
*/
function withdraw(
uint256 tokenId,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
uint256 getId,
uint256[] calldata setIds
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
uint128 _liquidity = uint128(getUint(getId, liquidity));
(uint256 _amtA, uint256 _amtB) = _decreaseLiquidity(
tokenId,
_liquidity,
amountAMin,
amountBMin
);
setUint(setIds[0], _amtA);
setUint(setIds[1], _amtB);
_eventName = "LogWithdraw(uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
}
/**
* @dev Collect function
* @notice Collect from NFT Position
* @param tokenId NFT LP Token ID.
* @param amount0Max Max amount of token0.
* @param amount1Max Max amount of token1.
* @param getIds IDs to retrieve amounts
* @param setIds stores the amount of output tokens
*/
function collect(
uint256 tokenId,
uint256 amount0Max,
uint256 amount1Max,
uint256[] calldata getIds,
uint256[] calldata setIds
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
uint128 _amount0Max = uint128(getUint(getIds[0], amount0Max));
uint128 _amount1Max = uint128(getUint(getIds[1], amount1Max));
(uint256 amount0, uint256 amount1) = _collect(
tokenId,
_amount0Max,
_amount1Max
);
setUint(setIds[0], amount0);
setUint(setIds[1], amount1);
_eventName = "LogCollect(uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, amount0, amount1);
}
/**
* @dev Burn Function
* @notice Burn NFT LP Position
* @param tokenId NFT LP Token ID
*/
function burn(uint256 tokenId)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
_burn(tokenId);
_eventName = "LogBurnPosition(uint256)";
_eventParam = abi.encode(tokenId);
}
}
contract ConnectV2UniswapV3Polygon is UniswapResolver {
string public constant name = "UniswapV3-v1";
}

View File

@ -0,0 +1,29 @@
pragma solidity ^0.7.0;
contract Events {
event LogDeposit(uint256 tokenId);
event LogDepositAndStake(uint256 tokenId, bytes32 incentiveId);
event LogWithdraw(uint256 indexed tokenId);
event LogDepositTransfer(uint256 indexed tokenId, address to);
event LogStake(uint256 indexed tokenId, bytes32 incentiveId);
event LogUnstake(uint256 indexed tokenId, bytes32 incentiveId);
event LogRewardClaimed(
address indexed rewardToken,
uint256 amount
);
event LogIncentiveCreated(
bytes32 incentiveId,
address poolAddr,
address refundee,
uint256 startTime,
uint256 endTime,
uint256 reward
);
}

View File

@ -0,0 +1,87 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import {TokenInterface} from "../../../common/interfaces.sol";
import {DSMath} from "../../../common/math.sol";
import {Basic} from "../../../common/basic.sol";
import "./interface.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev uniswap v3 NFT Position Manager & Swap Router
*/
INonfungiblePositionManager constant nftManager =
INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
IUniswapV3Staker constant staker =
IUniswapV3Staker(0x1f98407aaB862CdDeF78Ed252D6f557aA5b0f00d);
/**
* @dev Get Last NFT Index
* @param user: User address
*/
function _getLastNftId(address user)
internal
view
returns (uint256 tokenId)
{
uint256 len = nftManager.balanceOf(user);
tokenId = nftManager.tokenOfOwnerByIndex(user, len - 1);
}
function getPoolAddress(uint256 _tokenId)
internal
view
returns (address pool)
{
(bool success, bytes memory data) = address(nftManager).staticcall(
abi.encodeWithSelector(nftManager.positions.selector, _tokenId)
);
require(success, "fetching positions failed");
{
(, , address token0, address token1, uint24 fee, , , ) = abi.decode(
data,
(
uint96,
address,
address,
address,
uint24,
int24,
int24,
uint128
)
);
pool = PoolAddress.computeAddress(
nftManager.factory(),
PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee})
);
}
}
function _stake(
uint256 _tokenId,
IUniswapV3Staker.IncentiveKey memory _incentiveId
) internal {
staker.stakeToken(_incentiveId, _tokenId);
}
function _unstake(
IUniswapV3Staker.IncentiveKey memory _key,
uint256 _tokenId
) internal {
staker.unstakeToken(_key, _tokenId);
}
function _claimRewards(
IERC20Minimal _rewardToken,
address _to,
uint256 _amountRequested
) internal returns (uint256 rewards) {
rewards = staker.claimReward(_rewardToken, _to, _amountRequested);
}
}

View File

@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import '@uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol';
import '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import '@uniswap/v3-periphery/contracts/interfaces/IMulticall.sol';
/// @title Uniswap V3 Staker Interface
/// @notice Allows staking nonfungible liquidity tokens in exchange for reward tokens
interface IUniswapV3Staker is IERC721Receiver, IMulticall {
/// @param rewardToken The token being distributed as a reward
/// @param pool The Uniswap V3 pool
/// @param startTime The time when the incentive program begins
/// @param endTime The time when rewards stop accruing
/// @param refundee The address which receives any remaining reward tokens when the incentive is ended
struct IncentiveKey {
IERC20Minimal rewardToken;
IUniswapV3Pool pool;
uint256 startTime;
uint256 endTime;
address refundee;
}
/// @notice The Uniswap V3 Factory
function factory() external view returns (IUniswapV3Factory);
/// @notice The nonfungible position manager with which this staking contract is compatible
function nonfungiblePositionManager() external view returns (INonfungiblePositionManager);
/// @notice The max duration of an incentive in seconds
function maxIncentiveDuration() external view returns (uint256);
/// @notice The max amount of seconds into the future the incentive startTime can be set
function maxIncentiveStartLeadTime() external view returns (uint256);
/// @notice Represents a staking incentive
/// @param incentiveId The ID of the incentive computed from its parameters
/// @return totalRewardUnclaimed The amount of reward token not yet claimed by users
/// @return totalSecondsClaimedX128 Total liquidity-seconds claimed, represented as a UQ32.128
/// @return numberOfStakes The count of deposits that are currently staked for the incentive
function incentives(bytes32 incentiveId)
external
view
returns (
uint256 totalRewardUnclaimed,
uint160 totalSecondsClaimedX128,
uint96 numberOfStakes
);
/// @notice Returns information about a deposited NFT
/// @return owner The owner of the deposited NFT
/// @return numberOfStakes Counter of how many incentives for which the liquidity is staked
/// @return tickLower The lower tick of the range
/// @return tickUpper The upper tick of the range
function deposits(uint256 tokenId)
external
view
returns (
address owner,
uint48 numberOfStakes,
int24 tickLower,
int24 tickUpper
);
/// @notice Returns information about a staked liquidity NFT
/// @param tokenId The ID of the staked token
/// @param incentiveId The ID of the incentive for which the token is staked
/// @return secondsPerLiquidityInsideInitialX128 secondsPerLiquidity represented as a UQ32.128
/// @return liquidity The amount of liquidity in the NFT as of the last time the rewards were computed
function stakes(uint256 tokenId, bytes32 incentiveId)
external
view
returns (uint160 secondsPerLiquidityInsideInitialX128, uint128 liquidity);
/// @notice Returns amounts of reward tokens owed to a given address according to the last time all stakes were updated
/// @param rewardToken The token for which to check rewards
/// @param owner The owner for which the rewards owed are checked
/// @return rewardsOwed The amount of the reward token claimable by the owner
function rewards(IERC20Minimal rewardToken, address owner) external view returns (uint256 rewardsOwed);
/// @notice Creates a new liquidity mining incentive program
/// @param key Details of the incentive to create
/// @param reward The amount of reward tokens to be distributed
function createIncentive(IncentiveKey memory key, uint256 reward) external;
/// @notice Ends an incentive after the incentive end time has passed and all stakes have been withdrawn
/// @param key Details of the incentive to end
/// @return refund The remaining reward tokens when the incentive is ended
function endIncentive(IncentiveKey memory key) external returns (uint256 refund);
/// @notice Transfers ownership of a deposit from the sender to the given recipient
/// @param tokenId The ID of the token (and the deposit) to transfer
/// @param to The new owner of the deposit
function transferDeposit(uint256 tokenId, address to) external;
/// @notice Withdraws a Uniswap V3 LP token `tokenId` from this contract to the recipient `to`
/// @param tokenId The unique identifier of an Uniswap V3 LP token
/// @param to The address where the LP token will be sent
/// @param data An optional data array that will be passed along to the `to` address via the NFT safeTransferFrom
function withdrawToken(
uint256 tokenId,
address to,
bytes memory data
) external;
/// @notice Stakes a Uniswap V3 LP token
/// @param key The key of the incentive for which to stake the NFT
/// @param tokenId The ID of the token to stake
function stakeToken(IncentiveKey memory key, uint256 tokenId) external;
/// @notice Unstakes a Uniswap V3 LP token
/// @param key The key of the incentive for which to unstake the NFT
/// @param tokenId The ID of the token to unstake
function unstakeToken(IncentiveKey memory key, uint256 tokenId) external;
/// @notice Transfers `amountRequested` of accrued `rewardToken` rewards from the contract to the recipient `to`
/// @param rewardToken The token being distributed as a reward
/// @param to The address where claimed rewards will be sent to
/// @param amountRequested The amount of reward tokens to claim. Claims entire reward amount if set to 0.
/// @return reward The amount of reward tokens claimed
function claimReward(
IERC20Minimal rewardToken,
address to,
uint256 amountRequested
) external returns (uint256 reward);
/// @notice Calculates the reward amount that will be received for the given stake
/// @param key The key of the incentive
/// @param tokenId The ID of the token
/// @return reward The reward accrued to the NFT for the given incentive thus far
function getRewardInfo(IncentiveKey memory key, uint256 tokenId)
external
returns (uint256 reward, uint160 secondsInsideX128);
/// @notice Event emitted when a liquidity mining incentive has been created
/// @param rewardToken The token being distributed as a reward
/// @param pool The Uniswap V3 pool
/// @param startTime The time when the incentive program begins
/// @param endTime The time when rewards stop accruing
/// @param refundee The address which receives any remaining reward tokens after the end time
/// @param reward The amount of reward tokens to be distributed
event IncentiveCreated(
IERC20Minimal indexed rewardToken,
IUniswapV3Pool indexed pool,
uint256 startTime,
uint256 endTime,
address refundee,
uint256 reward
);
/// @notice Event that can be emitted when a liquidity mining incentive has ended
/// @param incentiveId The incentive which is ending
/// @param refund The amount of reward tokens refunded
event IncentiveEnded(bytes32 indexed incentiveId, uint256 refund);
/// @notice Emitted when ownership of a deposit changes
/// @param tokenId The ID of the deposit (and token) that is being transferred
/// @param oldOwner The owner before the deposit was transferred
/// @param newOwner The owner after the deposit was transferred
event DepositTransferred(uint256 indexed tokenId, address indexed oldOwner, address indexed newOwner);
/// @notice Event emitted when a Uniswap V3 LP token has been staked
/// @param tokenId The unique identifier of an Uniswap V3 LP token
/// @param liquidity The amount of liquidity staked
/// @param incentiveId The incentive in which the token is staking
event TokenStaked(uint256 indexed tokenId, bytes32 indexed incentiveId, uint128 liquidity);
/// @notice Event emitted when a Uniswap V3 LP token has been unstaked
/// @param tokenId The unique identifier of an Uniswap V3 LP token
/// @param incentiveId The incentive in which the token is staking
event TokenUnstaked(uint256 indexed tokenId, bytes32 indexed incentiveId);
/// @notice Event emitted when a reward token has been claimed
/// @param to The address where claimed rewards were sent to
/// @param reward The amount of reward tokens claimed
event RewardClaimed(address indexed to, uint256 reward);
}

View File

@ -0,0 +1,255 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
/**
* @title Uniswap v3.
* @dev Decentralized Exchange.
*/
import {TokenInterface} from "../../../common/interfaces.sol";
import "./interface.sol";
import {Helpers} from "./helpers.sol";
import {Events} from "./events.sol";
abstract contract UniswapResolver is Helpers, Events {
/**
* @dev Deposit NFT token
* @notice Transfer deposited NFT token
* @param _tokenId NFT LP Token ID
*/
function deposit(uint256 _tokenId)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (_tokenId == 0) _tokenId = _getLastNftId(address(this));
nftManager.safeTransferFrom(
address(this),
address(staker),
_tokenId,
""
);
_eventName = "LogDeposit(uint256)";
_eventParam = abi.encode(_tokenId);
}
/**
* @dev Deposit and Stake NFT token
* @notice To Deposit and Stake NFT for Staking
* @param _rewardToken _rewardToken address
* @param _startTime stake start time
* @param _endTime stake end time
* @param _refundee refundee address
* @param _tokenId NFT LP token id
*/
function depositAndStake (
address _rewardToken,
uint256 _startTime,
uint256 _endTime,
address _refundee,
uint256 _tokenId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (_tokenId == 0) _tokenId = _getLastNftId(address(this));
nftManager.safeTransferFrom(
address(this),
address(staker),
_tokenId,
""
);
address poolAddr = getPoolAddress(_tokenId);
IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
.IncentiveKey(
IERC20Minimal(_rewardToken),
pool,
_startTime,
_endTime,
_refundee
);
_stake(_tokenId, _key);
_eventName = "LogDepositAndStake(uint256,bytes32)";
_eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
}
/**
* @dev Deposit Transfer
* @notice Transfer deposited NFT token
* @param _tokenId NFT LP Token ID
* @param _to address to transfer
*/
function transferDeposit(uint256 _tokenId, address _to)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
staker.transferDeposit(_tokenId, _to);
_eventName = "LogDepositTransfer(uint256,address)";
_eventParam = abi.encode(_tokenId, _to);
}
/**
* @dev Withdraw NFT LP token
* @notice Withdraw NFT LP token from staking pool
* @param _tokenId NFT LP Token ID
*/
function withdraw(uint256 _tokenId)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
staker.withdrawToken(_tokenId, address(this), "");
_eventName = "LogWithdraw(uint256)";
_eventParam = abi.encode(_tokenId);
}
/**
* @dev Stake NFT LP token
* @notice Stake NFT LP Position
* @param _rewardToken _rewardToken address
* @param _startTime stake start time
* @param _endTime stake end time
* @param _refundee refundee address
* @param _tokenId NFT LP token id
*/
function stake (
address _rewardToken,
uint256 _startTime,
uint256 _endTime,
address _refundee,
uint256 _tokenId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
address poolAddr = getPoolAddress(_tokenId);
IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
.IncentiveKey(
IERC20Minimal(_rewardToken),
pool,
_startTime,
_endTime,
_refundee
);
_stake(_tokenId, _key);
_eventName = "LogStake(uint256,bytes32)";
_eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
}
/**
* @dev Unstake NFT LP token
* @notice Unstake NFT LP Position
* @param _rewardToken _rewardToken address
* @param _startTime stake start time
* @param _endTime stake end time
* @param _refundee refundee address
* @param _tokenId NFT LP token id
*/
function unstake(
address _rewardToken,
uint256 _startTime,
uint256 _endTime,
address _refundee,
uint256 _tokenId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
address poolAddr = getPoolAddress(_tokenId);
IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
.IncentiveKey(
IERC20Minimal(_rewardToken),
pool,
_startTime,
_endTime,
_refundee
);
_unstake(_key, _tokenId);
_eventName = "LogUnstake(uint256,bytes32)";
_eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
}
/**
* @dev Claim rewards
* @notice Claim rewards
* @param _rewardToken _rewardToken address
* @param _amount requested amount
*/
function claimRewards(
address _rewardToken,
uint256 _amount
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
uint256 rewards = _claimRewards(
IERC20Minimal(_rewardToken),
address(this),
_amount
);
_eventName = "LogRewardClaimed(address,uint256)";
_eventParam = abi.encode(_rewardToken, rewards);
}
/**
* @dev Create incentive
* @notice Create incentive
* @param _rewardToken _rewardToken address
* @param _length incentive length
* @param _refundee refundee address
* @param _poolAddr Uniswap V3 Pool address
* @param _reward reward amount
*/
function createIncentive(
address _rewardToken,
uint256 _length,
address _refundee,
address _poolAddr,
uint256 _reward
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
IUniswapV3Pool pool = IUniswapV3Pool(_poolAddr);
uint256 _startTime = block.timestamp;
uint256 _endTime = _startTime + _length;
IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
.IncentiveKey(
IERC20Minimal(_rewardToken),
pool,
_startTime,
_endTime,
_refundee
);
if (_rewardToken != maticAddr) {
IERC20Minimal(_rewardToken).approve(address(staker), _reward);
}
staker.createIncentive(_key, _reward);
_eventName = "LogIncentiveCreated(bytes32,address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(keccak256(abi.encode(_key)), _poolAddr, _refundee, _startTime, _endTime, _reward);
}
}
contract ConnectV2UniswapV3StakerPolygon is UniswapResolver {
string public constant name = "Uniswap-V3-Staker-v1.1";
}

View File

@ -5,7 +5,7 @@ async function main() {
const accounts = await ethers.getSigners();
const connectMapping: Record<string, string> = {
"1INCH-A": "ConnectV2OneInch",
"1INCH-A": "ConnectV2OneInch",
"1INCH-B": "ConnectV2OneProto",
"AAVE-V1-A": "ConnectV2AaveV1",
"AAVE-V2-A": "ConnectV2AaveV2",
@ -19,6 +19,8 @@ async function main() {
"MAKERDAO-A": "ConnectV2Maker",
"UNISWAP-A": "ConnectV2UniswapV2",
"QUICKSWAP-A": "ConnectV2QuickswapPolygon"
"UniswapV3-v1" : "ConnectV2UniswapV3Polygon",
"Uniswap-V3-Staker-v1.1" : "ConnectV2UniswapV3StakerPolygon"
};
const addressMapping: Record<string, string> = {};

View File

@ -1,4 +1,4 @@
import { ethers } from "hardhat";
import hre, { ethers } from "hardhat";
export const deployConnector = async (connectorName: string) => {
const Connector = await ethers.getContractFactory(connectorName);
@ -6,5 +6,17 @@ export const deployConnector = async (connectorName: string) => {
await connector.deployed();
console.log(`${connectorName} Deployed: ${connector.address}`);
try {
await hre.run("verify:verify", {
address: connector.address,
constructorArguments: []
}
)
} catch (error) {
console.log(`Failed to verify: ${connectorName}@${connector.address}`)
console.log(error)
console.log()
}
return connector.address;
};