diff --git a/contracts/mainnet/connectors/1inch/main.sol b/contracts/mainnet/connectors/1inch/main.sol index bf215788..7f1622b6 100644 --- a/contracts/mainnet/connectors/1inch/main.sol +++ b/contracts/mainnet/connectors/1inch/main.sol @@ -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: here * @param setId ID stores the amount of token brought. */ function sell( diff --git a/contracts/polygon/connectors/uniswap/v3/events.sol b/contracts/polygon/connectors/uniswap/v3/events.sol new file mode 100644 index 00000000..829fadd2 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3/events.sol @@ -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); +} diff --git a/contracts/polygon/connectors/uniswap/v3/helpers.sol b/contracts/polygon/connectors/uniswap/v3/helpers.sol new file mode 100644 index 00000000..f43be452 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3/helpers.sol @@ -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); + } +} diff --git a/contracts/polygon/connectors/uniswap/v3/interface.sol b/contracts/polygon/connectors/uniswap/v3/interface.sol new file mode 100644 index 00000000..f8bc6e84 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3/interface.sol @@ -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; +} diff --git a/contracts/polygon/connectors/uniswap/v3/main.sol b/contracts/polygon/connectors/uniswap/v3/main.sol new file mode 100644 index 00000000..b6195a6f --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3/main.sol @@ -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"; +} diff --git a/contracts/polygon/connectors/uniswap/v3_staker/events.sol b/contracts/polygon/connectors/uniswap/v3_staker/events.sol new file mode 100644 index 00000000..99b98a61 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3_staker/events.sol @@ -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 + ); +} diff --git a/contracts/polygon/connectors/uniswap/v3_staker/helpers.sol b/contracts/polygon/connectors/uniswap/v3_staker/helpers.sol new file mode 100644 index 00000000..258d0961 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3_staker/helpers.sol @@ -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); + } +} diff --git a/contracts/polygon/connectors/uniswap/v3_staker/interface.sol b/contracts/polygon/connectors/uniswap/v3_staker/interface.sol new file mode 100644 index 00000000..5c537e43 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3_staker/interface.sol @@ -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); +} diff --git a/contracts/polygon/connectors/uniswap/v3_staker/main.sol b/contracts/polygon/connectors/uniswap/v3_staker/main.sol new file mode 100644 index 00000000..36e79772 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3_staker/main.sol @@ -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"; +} diff --git a/scripts/deployment/deploy.ts b/scripts/deployment/deploy.ts index ac66d9ad..c32788fd 100644 --- a/scripts/deployment/deploy.ts +++ b/scripts/deployment/deploy.ts @@ -5,7 +5,7 @@ async function main() { const accounts = await ethers.getSigners(); const connectMapping: Record = { - "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 = {}; diff --git a/scripts/deployment/deployConnector.ts b/scripts/deployment/deployConnector.ts index 44e4667d..2aaea507 100644 --- a/scripts/deployment/deployConnector.ts +++ b/scripts/deployment/deployConnector.ts @@ -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; };