diff --git a/.github/workflows/status.yml b/.github/workflows/status.yml index 9dd6986c..3382de78 100644 --- a/.github/workflows/status.yml +++ b/.github/workflows/status.yml @@ -9,18 +9,41 @@ jobs: matrix: node-version: [16.x] steps: - - uses: actions/checkout@v1 - with: - # Checkout the head ref instead of the PR branch that github creates. - ref: ${{ github.head_ref }} - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: Install and build - run: | - npm install - - name: Run status checks - run: node ./status-checks - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Use Cache + uses: actions/cache@v2 + with: + path: | + node_modules + */*/node_modules + key: ${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('**/package-lock.json') }} + - name: Install and build + run: | + npm install + - name: Run status checks + id: status_check + run: | + # Run status checks, Remove ANSI colors from the text + output=$(node ./status-checks | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g') + # Escape newlines so _all_ the output is included in the set-output + output="${output//'%'/'%25'}" + output="${output//$'\n'/'%0A'}" + output="${output//$'\r'/'%0D'}" + echo "::set-output name=status_check_output::$output" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Auto Comment Status Check Result + # Use with caution + uses: bubkoo/auto-comment@v1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + pullRequestSynchronize: "```${{ steps.status_check.outputs.status_check_output }}```" + pullRequestAssigned: "```${{ steps.status_check.outputs.status_check_output }}```" + pullRequestOpened: "```${{ steps.status_check.outputs.status_check_output }}```" + pullRequestReopened: "```${{ steps.status_check.outputs.status_check_output }}```" diff --git a/README.md b/README.md index e9a60cd8..be1b4893 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,3 @@ Few things to consider while writing the connector: ### Support If you can't find something you're looking for or have any questions, ask them at our developers community on [Discord](https://discord.gg/83vvrnY) or simply send an [Email](mailto:info@instadapp.io). - diff --git a/contracts/mainnet/connectors/basic-ERC1155/events.sol b/contracts/mainnet/connectors/basic-ERC1155/events.sol new file mode 100644 index 00000000..2ef9f79a --- /dev/null +++ b/contracts/mainnet/connectors/basic-ERC1155/events.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositERC1155( + address indexed erc1155, + address from, + uint256 tokenId, + uint256 amount, + uint256 getId, + uint256 setId + ); + event LogWithdrawERC1155( + address indexed erc1155, + uint256 tokenId, + address indexed to, + uint256 amount, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/mainnet/connectors/basic-ERC1155/main.sol b/contracts/mainnet/connectors/basic-ERC1155/main.sol new file mode 100644 index 00000000..0aef5cd7 --- /dev/null +++ b/contracts/mainnet/connectors/basic-ERC1155/main.sol @@ -0,0 +1,93 @@ +pragma solidity ^0.7.0; + +/** + * @title Basic. + * @dev Deposit & Withdraw from ERC1155 DSA. + */ +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; + +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {Events} from "./events.sol"; + +abstract contract BasicResolver is Events, DSMath, Basic { + /** + * @dev Deposit Assets To Smart Account. + * @notice Deposit a ERC1155 token to DSA + * @param token Address of token. + * @param tokenId ID of token. + * @param amount Amount to deposit. + * @param getId ID to retrieve amount. + * @param setId ID stores the amount. + */ + function depositERC1155( + address token, + uint256 tokenId, + uint256 amount, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amount = getUint(getId, amount); + + IERC1155 tokenContract = IERC1155(token); + tokenContract.safeTransferFrom( + msg.sender, + address(this), + tokenId, + _amount, + "" + ); + + setUint(setId, _amount); + + _eventName = "LogDepositERC1155(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + token, + msg.sender, + tokenId, + _amount, + getId, + setId + ); + } + + /** + * @dev Withdraw Assets To Smart Account. + * @notice Withdraw a ERC1155 token from DSA + * @param token Address of the token. + * @param tokenId ID of token. + * @param to The address to receive the token upon withdrawal + * @param amount Amount to withdraw. + * @param getId ID to retrieve amount. + * @param setId ID stores the amount. + */ + function withdrawERC1155( + address token, + uint256 tokenId, + address payable to, + uint256 amount, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amount = getUint(getId, amount); + IERC1155 tokenContract = IERC1155(token); + tokenContract.safeTransferFrom(address(this), to, tokenId, _amount, ""); + + setUint(setId, _amount); + + _eventName = "LogWithdrawERC1155(address,uint256,address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, tokenId, to, _amount, getId, setId); + } +} + +contract ConnectV2BasicERC1155 is BasicResolver { + string public constant name = "BASIC-ERC1155-v1.0"; +} diff --git a/contracts/mainnet/connectors/basic-ERC721/events.sol b/contracts/mainnet/connectors/basic-ERC721/events.sol new file mode 100644 index 00000000..7b367145 --- /dev/null +++ b/contracts/mainnet/connectors/basic-ERC721/events.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositERC721( + address indexed erc721, + address from, + uint256 tokenId, + uint256 getId, + uint256 setId + ); + event LogWithdrawERC721( + address indexed erc721, + uint256 tokenId, + address indexed to, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/mainnet/connectors/basic-ERC721/main.sol b/contracts/mainnet/connectors/basic-ERC721/main.sol new file mode 100644 index 00000000..410c6342 --- /dev/null +++ b/contracts/mainnet/connectors/basic-ERC721/main.sol @@ -0,0 +1,76 @@ +pragma solidity ^0.7.0; + +/** + * @title Basic. + * @dev Deposit & Withdraw ERC721 from DSA. + */ +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {Events} from "./events.sol"; + +abstract contract BasicResolver is Events, DSMath, Basic { + /** + * @dev Deposit Assets To Smart Account. + * @notice Deposit a ERC721 token to DSA + * @param token Address of token. + * @param tokenId ID of token. + * @param getId ID to retrieve tokenId. + * @param setId ID stores the tokenId. + */ + function depositERC721( + address token, + uint256 tokenId, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _tokenId = getUint(getId, tokenId); + + IERC721 tokenContract = IERC721(token); + tokenContract.safeTransferFrom(msg.sender, address(this), _tokenId); + + setUint(setId, _tokenId); + + _eventName = "LogDepositERC721(address,address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, msg.sender, _tokenId, getId, setId); + } + + /** + * @dev Withdraw Assets To Smart Account. + * @notice Withdraw a ERC721 token from DSA + * @param token Address of the token. + * @param tokenId ID of token. + * @param to The address to receive the token upon withdrawal + * @param getId ID to retrieve tokenId. + * @param setId ID stores the tokenId. + */ + function withdrawERC721( + address token, + uint256 tokenId, + address payable to, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _tokenId = getUint(getId, tokenId); + IERC721 tokenContract = IERC721(token); + tokenContract.safeTransferFrom(address(this), to, _tokenId); + + setUint(setId, _tokenId); + + _eventName = "LogWithdrawERC721(address,uint256,address,uint256,uint256)"; + _eventParam = abi.encode(token, _tokenId, to, getId, setId); + } +} + +contract ConnectV2BasicERC721 is BasicResolver { + string public constant name = "BASIC-ERC721-v1.0"; +} diff --git a/contracts/mainnet/connectors/uniswap/events.sol b/contracts/mainnet/connectors/uniswap/v2/events.sol similarity index 100% rename from contracts/mainnet/connectors/uniswap/events.sol rename to contracts/mainnet/connectors/uniswap/v2/events.sol diff --git a/contracts/mainnet/connectors/uniswap/helpers.sol b/contracts/mainnet/connectors/uniswap/v2/helpers.sol similarity index 96% rename from contracts/mainnet/connectors/uniswap/helpers.sol rename to contracts/mainnet/connectors/uniswap/v2/helpers.sol index f435a78b..f0a8a6da 100644 --- a/contracts/mainnet/connectors/uniswap/helpers.sol +++ b/contracts/mainnet/connectors/uniswap/v2/helpers.sol @@ -1,8 +1,8 @@ pragma solidity ^0.7.0; -import { TokenInterface } from "../../common/interfaces.sol"; -import { DSMath } from "../../common/math.sol"; -import { Basic } from "../../common/basic.sol"; +import { TokenInterface } from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; import { IUniswapV2Router02, IUniswapV2Factory } from "./interface.sol"; abstract contract Helpers is DSMath, Basic { diff --git a/contracts/mainnet/connectors/uniswap/interface.sol b/contracts/mainnet/connectors/uniswap/v2/interface.sol similarity index 100% rename from contracts/mainnet/connectors/uniswap/interface.sol rename to contracts/mainnet/connectors/uniswap/v2/interface.sol diff --git a/contracts/mainnet/connectors/uniswap/main.sol b/contracts/mainnet/connectors/uniswap/v2/main.sol similarity index 99% rename from contracts/mainnet/connectors/uniswap/main.sol rename to contracts/mainnet/connectors/uniswap/v2/main.sol index a8ee9959..e0daa6a9 100644 --- a/contracts/mainnet/connectors/uniswap/main.sol +++ b/contracts/mainnet/connectors/uniswap/v2/main.sol @@ -5,7 +5,7 @@ pragma solidity ^0.7.0; * @dev Decentralized Exchange. */ -import { TokenInterface } from "../../common/interfaces.sol"; +import { TokenInterface } from "../../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; diff --git a/contracts/mainnet/connectors/uniswapV3/events.sol b/contracts/mainnet/connectors/uniswap/v3/events.sol similarity index 100% rename from contracts/mainnet/connectors/uniswapV3/events.sol rename to contracts/mainnet/connectors/uniswap/v3/events.sol diff --git a/contracts/mainnet/connectors/uniswapV3/helpers.sol b/contracts/mainnet/connectors/uniswap/v3/helpers.sol similarity index 97% rename from contracts/mainnet/connectors/uniswapV3/helpers.sol rename to contracts/mainnet/connectors/uniswap/v3/helpers.sol index 8585f84f..2fc42f8b 100644 --- a/contracts/mainnet/connectors/uniswapV3/helpers.sol +++ b/contracts/mainnet/connectors/uniswap/v3/helpers.sol @@ -1,9 +1,9 @@ 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 {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"; diff --git a/contracts/mainnet/connectors/uniswapV3/interface.sol b/contracts/mainnet/connectors/uniswap/v3/interface.sol similarity index 100% rename from contracts/mainnet/connectors/uniswapV3/interface.sol rename to contracts/mainnet/connectors/uniswap/v3/interface.sol diff --git a/contracts/mainnet/connectors/uniswapV3/main.sol b/contracts/mainnet/connectors/uniswap/v3/main.sol similarity index 98% rename from contracts/mainnet/connectors/uniswapV3/main.sol rename to contracts/mainnet/connectors/uniswap/v3/main.sol index 7fd058a6..c40f871f 100644 --- a/contracts/mainnet/connectors/uniswapV3/main.sol +++ b/contracts/mainnet/connectors/uniswap/v3/main.sol @@ -6,7 +6,7 @@ pragma abicoder v2; * @dev Decentralized Exchange. */ -import {TokenInterface} from "../../common/interfaces.sol"; +import {TokenInterface} from "../../../common/interfaces.sol"; import {Helpers} from "./helpers.sol"; import {Events} from "./events.sol"; diff --git a/contracts/mainnet/connectors/uniswap/v3_staker/events.sol b/contracts/mainnet/connectors/uniswap/v3_staker/events.sol new file mode 100644 index 00000000..fa8bbebf --- /dev/null +++ b/contracts/mainnet/connectors/uniswap/v3_staker/events.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDeposit(uint256 tokenId); + + 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/mainnet/connectors/uniswap/v3_staker/helpers.sol b/contracts/mainnet/connectors/uniswap/v3_staker/helpers.sol new file mode 100644 index 00000000..258d0961 --- /dev/null +++ b/contracts/mainnet/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/mainnet/connectors/uniswap/v3_staker/interface.sol b/contracts/mainnet/connectors/uniswap/v3_staker/interface.sol new file mode 100644 index 00000000..5c537e43 --- /dev/null +++ b/contracts/mainnet/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/mainnet/connectors/uniswap/v3_staker/main.sol b/contracts/mainnet/connectors/uniswap/v3_staker/main.sol new file mode 100644 index 00000000..2da11c59 --- /dev/null +++ b/contracts/mainnet/connectors/uniswap/v3_staker/main.sol @@ -0,0 +1,214 @@ +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 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) + { + if (_tokenId == 0) _tokenId = _getLastNftId(address(this)); + 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) + { + if (_tokenId == 0) _tokenId = _getLastNftId(address(this)); + 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) + { + if (_tokenId == 0) _tokenId = _getLastNftId(address(this)); + 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) + { + if (_tokenId == 0) _tokenId = _getLastNftId(address(this)); + 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 != ethAddr) { + 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 ConnectV2UniswapV3Staker is UniswapResolver { + string public constant name = "Uniswap-V3-Staker-v1"; +} diff --git a/contracts/test/implementation_default.sol b/contracts/test/implementation_default.sol new file mode 100644 index 00000000..e897433b --- /dev/null +++ b/contracts/test/implementation_default.sol @@ -0,0 +1,114 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { Variables } from "./variables.sol"; + +interface IndexInterface { + function list() external view returns (address); +} + +interface ListInterface { + function addAuth(address user) external; + + function removeAuth(address user) external; +} + +contract Constants is Variables { + uint256 public constant implementationVersion = 1; + // InstaIndex Address. + address public immutable instaIndex; + // The Account Module Version. + uint256 public constant version = 2; + + constructor(address _instaIndex) { + instaIndex = _instaIndex; + } +} + +contract Record is Constants { + constructor(address _instaIndex) Constants(_instaIndex) {} + + event LogEnableUser(address indexed user); + event LogDisableUser(address indexed user); + + /** + * @dev Check for Auth if enabled. + * @param user address/user/owner. + */ + function isAuth(address user) public view returns (bool) { + return _auth[user]; + } + + /** + * @dev Enable New User. + * @param user Owner address + */ + function enable(address user) public { + require( + msg.sender == address(this) || msg.sender == instaIndex, + "not-self-index" + ); + require(user != address(0), "not-valid"); + require(!_auth[user], "already-enabled"); + _auth[user] = true; + ListInterface(IndexInterface(instaIndex).list()).addAuth(user); + emit LogEnableUser(user); + } + + /** + * @dev Disable User. + * @param user Owner address + */ + function disable(address user) public { + require(msg.sender == address(this), "not-self"); + require(user != address(0), "not-valid"); + require(_auth[user], "already-disabled"); + delete _auth[user]; + ListInterface(IndexInterface(instaIndex).list()).removeAuth(user); + emit LogDisableUser(user); + } + + /** + * @dev ERC721 token receiver + */ + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external returns (bytes4) { + return 0x150b7a02; // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")) + } + + /** + * @dev ERC1155 token receiver + */ + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) external returns (bytes4) { + return 0xf23a6e61; // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) + } + + /** + * @dev ERC1155 token receiver + */ + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external returns (bytes4) { + return 0xbc197c81; // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")) + } +} + +contract InstaDefaultImplementation is Record { + constructor(address _instaIndex) public Record(_instaIndex) {} + + receive() external payable {} +} diff --git a/contracts/test/variables.sol b/contracts/test/variables.sol new file mode 100644 index 00000000..86853496 --- /dev/null +++ b/contracts/test/variables.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.7.0; + +contract Variables { + // Auth Module(Address of Auth => bool). + mapping (address => bool) internal _auth; +} \ No newline at end of file diff --git a/docs/connectors.json b/docs/connectors.json index 28c96123..75db30dc 100644 --- a/docs/connectors.json +++ b/docs/connectors.json @@ -25,7 +25,11 @@ "REFINANCE-A": "0x6f22931423e8ffC8d51f6E5aF73118fC64b27856", "INST-A": "0x52C2C4a0db049255fF345EB9D3Fb1f555b7a924A", "REFLEXER-A": "0xaC6dc28a6251F49Bbe5755E630107Dccde9ae2C8", - "LIQUITY-A": "0x3643bA40B8e2bd8F77233BDB6abe38c218f31bFe" + "LIQUITY-A": "0x3643bA40B8e2bd8F77233BDB6abe38c218f31bFe", + "UNISWAP-V3-A": "0x25B0c76dE86C3457b9B8b9ee3775F5a7b8D4c475", + "B-COMPOUND-A": "0xa3EeFDc2de9DFA59968bEcff3E15b53E6162460f", + "B-MAKERDAO-A": "0xB0A1f10FeEfECf25064CE7cdF0a65042F7dE7bF0", + "B-LIQUITY-A": "0x19574E5Dfb40bbD63A4F3bdcF27ed662b329b2ff" }, "137" : { "1INCH-A": "0xC0d9210496afE9763F5d8cEb8deFfBa817232A9e", diff --git a/package.json b/package.json index 589d093e..22fdaec2 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "test": "hardhat test", "coverage": "./node_modules/.bin/solidity-coverage", + "check": "node status-checks/huskyCheck.js", "check-husky": "node status-checks/huskyCheck.js", "build-contracts": "sol-merger \"./contracts/connectors/mock.sol\" ./contracts/build" }, diff --git a/scripts/addLiquidity.js b/scripts/addLiquidity.js index 0fb51156..c461a775 100644 --- a/scripts/addLiquidity.js +++ b/scripts/addLiquidity.js @@ -50,6 +50,15 @@ const tokenMapping = { await mineTx(contract.mint(address, amt)); }, }, + inst: { + impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88", + address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", + abi: ["function transfer(address to, uint value)"], + process: async function(owner, address, amt) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(address, amt)); + }, + } }; module.exports = async (tokenName, address, amt) => { diff --git a/scripts/constant/abi/core/InstaImplementations.json b/scripts/constant/abi/core/InstaImplementations.json new file mode 100644 index 00000000..c1bae964 --- /dev/null +++ b/scripts/constant/abi/core/InstaImplementations.json @@ -0,0 +1,206 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "InstaImplementations", + "sourceName": "contracts/v2/registry/implementations.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_instaIndex", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4[]", + "name": "sigs", + "type": "bytes4[]" + } + ], + "name": "LogAddImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4[]", + "name": "sigs", + "type": "bytes4[]" + } + ], + "name": "LogRemoveImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "LogSetDefaultImplementation", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + }, + { + "internalType": "bytes4[]", + "name": "_sigs", + "type": "bytes4[]" + } + ], + "name": "addImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "defaultImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_sig", + "type": "bytes4" + } + ], + "name": "getImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_impl", + "type": "address" + } + ], + "name": "getImplementationSigs", + "outputs": [ + { + "internalType": "bytes4[]", + "name": "", + "type": "bytes4[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_sig", + "type": "bytes4" + } + ], + "name": "getSigImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "instaIndex", + "outputs": [ + { + "internalType": "contract IndexInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "removeImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_defaultImplementation", + "type": "address" + } + ], + "name": "setDefaultImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561001057600080fd5b506040516116383803806116388339818101604052602081101561003357600080fd5b8101908080519060200190929190505050808073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1660601b81525050505060805160601c61158e6100aa6000398061039f528061085b5280610c5d5280610dfb525061158e6000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063b39c45931161005b578063b39c4593146101e2578063dc9cc64514610216578063ef7e5c7b1461028d578063f0c01b421461030457610088565b806322175a321461008d5780637c16ffc4146100d157806389396dc814610115578063a41098bf146101ae575b600080fd5b6100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061039d565b005b610113600480360360208110156100e757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610859565b005b6101576004803603602081101561012b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b82565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561019a57808201518184015260208101905061017f565b505050509050019250505060405180910390f35b6101b6610c5b565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ea610c7f565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102616004803603602081101561022c57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610ca3565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102d8600480360360208110156102a357600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610d7e565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61039b6004803603604081101561031a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561035757600080fd5b82018360208201111561036957600080fd5b8035906020019184602083028401116401000000008311171561038b57600080fd5b9091929391929390505050610df9565b005b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b15801561040357600080fd5b505afa158015610417573d6000803e3d6000fd5b505050506040513d602081101561042d57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610564576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501415610600576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114f8602b913960400191505060405180910390fd5b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906004019060208260030104928301926001038202915080841161067a5790505b5050505050905060005b815181101561077a5760008282815181106106ee57fe5b6020026020010151905060016000827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555080806001019150506106d7565b50600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107c69190611305565b8173ffffffffffffffffffffffffffffffffffffffff167fb7d759e6cdda23e8a1749bce345fc77355b8a22eeaf92c6e4e7257d959c162c7826040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610842578082015181840152602081019050610827565b505050509050019250505060405180910390a25050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b505afa1580156108d3573d6000803e3d6000fd5b505050506040513d60208110156108e957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806114656039913960400191505060405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ac5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806115236036913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f3b337225b8d68d037e0c721876335a3832bd08162c943fe5f88e8d428597ca8f60405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c4f57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610bfc5790505b50505050509050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610d545780610d76565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff165b915050919050565b600060016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501461105b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f81526020018061149e602f913960400191505060405180910390fd5b60005b8282905081101561123357600083838381811061107757fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169050600073ffffffffffffffffffffffffffffffffffffffff1660016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611195576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806114426023913960400191505060405180910390fd5b8460016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050808060010191505061105e565b508181600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020919061128292919061132d565b508273ffffffffffffffffffffffffffffffffffffffff167ff9c512a86be00aaec236065d0a439f064133f367de889c782448c3578a3f30c5838360405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2505050565b50805460008255600701600890049060005260206000209081019061132a91906113f9565b50565b828054828255906000526020600020906007016008900481019282156113e85791602002820160005b838211156113b65783357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191683826101000a81548163ffffffff021916908360e01c02179055509260200192600401602081600301049283019260010302611356565b80156113e65782816101000a81549063ffffffff02191690556004016020816003010492830192600103026113b6565b505b5090506113f59190611416565b5090565b5b808211156114125760008160009055506001016113fa565b5090565b5b8082111561143d57600081816101000a81549063ffffffff021916905550600101611417565b509056fe496d706c656d656e746174696f6e733a205f73696720616c7265616479206164646564496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2061646472657373206e6f742076616c6964496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e20616c72656164792061646465642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f742076616c69642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f7420666f756e642e496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2063616e6e6f742062652073616d65a2646970667358221220d42dcb568934728d15f318f76a6028fe848a484b097ea24581f45425fcde152864736f6c63430007000033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063b39c45931161005b578063b39c4593146101e2578063dc9cc64514610216578063ef7e5c7b1461028d578063f0c01b421461030457610088565b806322175a321461008d5780637c16ffc4146100d157806389396dc814610115578063a41098bf146101ae575b600080fd5b6100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061039d565b005b610113600480360360208110156100e757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610859565b005b6101576004803603602081101561012b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b82565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561019a57808201518184015260208101905061017f565b505050509050019250505060405180910390f35b6101b6610c5b565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ea610c7f565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102616004803603602081101561022c57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610ca3565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102d8600480360360208110156102a357600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610d7e565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61039b6004803603604081101561031a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561035757600080fd5b82018360208201111561036957600080fd5b8035906020019184602083028401116401000000008311171561038b57600080fd5b9091929391929390505050610df9565b005b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b15801561040357600080fd5b505afa158015610417573d6000803e3d6000fd5b505050506040513d602081101561042d57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610564576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501415610600576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114f8602b913960400191505060405180910390fd5b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906004019060208260030104928301926001038202915080841161067a5790505b5050505050905060005b815181101561077a5760008282815181106106ee57fe5b6020026020010151905060016000827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555080806001019150506106d7565b50600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107c69190611305565b8173ffffffffffffffffffffffffffffffffffffffff167fb7d759e6cdda23e8a1749bce345fc77355b8a22eeaf92c6e4e7257d959c162c7826040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610842578082015181840152602081019050610827565b505050509050019250505060405180910390a25050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b505afa1580156108d3573d6000803e3d6000fd5b505050506040513d60208110156108e957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806114656039913960400191505060405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ac5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806115236036913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f3b337225b8d68d037e0c721876335a3832bd08162c943fe5f88e8d428597ca8f60405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c4f57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610bfc5790505b50505050509050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610d545780610d76565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff165b915050919050565b600060016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501461105b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f81526020018061149e602f913960400191505060405180910390fd5b60005b8282905081101561123357600083838381811061107757fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169050600073ffffffffffffffffffffffffffffffffffffffff1660016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611195576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806114426023913960400191505060405180910390fd5b8460016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050808060010191505061105e565b508181600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020919061128292919061132d565b508273ffffffffffffffffffffffffffffffffffffffff167ff9c512a86be00aaec236065d0a439f064133f367de889c782448c3578a3f30c5838360405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2505050565b50805460008255600701600890049060005260206000209081019061132a91906113f9565b50565b828054828255906000526020600020906007016008900481019282156113e85791602002820160005b838211156113b65783357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191683826101000a81548163ffffffff021916908360e01c02179055509260200192600401602081600301049283019260010302611356565b80156113e65782816101000a81549063ffffffff02191690556004016020816003010492830192600103026113b6565b505b5090506113f59190611416565b5090565b5b808211156114125760008160009055506001016113fa565b5090565b5b8082111561143d57600081816101000a81549063ffffffff021916905550600101611417565b509056fe496d706c656d656e746174696f6e733a205f73696720616c7265616479206164646564496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2061646472657373206e6f742076616c6964496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e20616c72656164792061646465642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f742076616c69642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f7420666f756e642e496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2063616e6e6f742062652073616d65a2646970667358221220d42dcb568934728d15f318f76a6028fe848a484b097ea24581f45425fcde152864736f6c63430007000033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/status-checks/check.js b/status-checks/check.js index a76d81ae..c0848d77 100644 --- a/status-checks/check.js +++ b/status-checks/check.js @@ -3,13 +3,11 @@ const path = require('path') const forbiddenStrings = ['selfdestruct'] -const getConnectorsList = async () => { +const getConnectorsList = async (connectorsRootsDirs) => { try { const connectors = [] - const connectorsRootsDirs = ['mainnet', 'polygon'] for (let index = 0; index < connectorsRootsDirs.length; index++) { - const root = `contracts/${connectorsRootsDirs[index]}/connectors` - const dirs = [root] + const dirs = [connectorsRootsDirs[index]] while (dirs.length) { const currentDir = dirs.pop() const subs = fs.readdirSync(currentDir, { withFileTypes: true }) @@ -326,9 +324,14 @@ const checkHeadComments = async (connector) => { async function checkMain () { try { + const connectorsRootsDirsDefault = ['mainnet', 'polygon'].map(v=> `contracts/${v}/connectors`) + const customPathArg = process.argv.find(a => a.startsWith('connector=')) + const connectorsRootsDirs = customPathArg + ? [customPathArg.slice(10)] + : connectorsRootsDirsDefault const errors = [] const warnings = [] - const connectors = await getConnectorsList() + const connectors = await getConnectorsList(connectorsRootsDirs) for (let index = 0; index < connectors.length; index++) { const { forbiddenErrors, code } = await checkForbidden(connectors[index].path) connectors[index].code = code diff --git a/status-checks/readme.md b/status-checks/readme.md new file mode 100644 index 00000000..ed376c25 --- /dev/null +++ b/status-checks/readme.md @@ -0,0 +1,7 @@ + +# Check run + +use +`npm run check` +to check `connectors` directory. Use `connector=$` argument to check specific connector: +`npm run check connector=contracts/mainnet/common` diff --git a/test/basic-ERC1155/ERC1155-transfer.js b/test/basic-ERC1155/ERC1155-transfer.js new file mode 100644 index 00000000..cd2f2b12 --- /dev/null +++ b/test/basic-ERC1155/ERC1155-transfer.js @@ -0,0 +1,127 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle +const {abi: implementationsABI} = require("../../scripts/constant/abi/core/InstaImplementations.json") + +const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js") +const buildDSAv2 = require("../../scripts/buildDSAv2") +const encodeSpells = require("../../scripts/encodeSpells.js") +const getMasterSigner = require("../../scripts/getMasterSigner") + +const addresses = require("../../scripts/constant/addresses"); +const abis = require("../../scripts/constant/abis"); +const constants = require("../../scripts/constant/constant"); +const tokens = require("../../scripts/constant/tokens"); + +const connectV2BasicERC1155Artifacts = require("../../artifacts/contracts/mainnet/connectors/basic-ERC1155/main.sol/ConnectV2BasicERC1155.json") +const erc1155Artifacts = require("../../artifacts/@openzeppelin/contracts/token/ERC1155/IERC1155.sol/IERC1155.json") + +const TOKEN_CONTRACT_ADDR = "0x1ca3262009b21F944e6b92a2a88D039D06F1acFa"; +const TOKEN_OWNER_ADDR = "0x1ca3262009b21F944e6b92a2a88D039D06F1acFa"; +const TOKEN_ID = "1"; + +const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D" + +describe("BASIC-ERC1155", function () { + const connectorName = "BASIC-ERC1155-A" + + let dsaWallet0 + let masterSigner; + let instaConnectorsV2; + let connector; + let nftContract; + let tokenOwner; + let instaImplementationsMapping; + + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [TOKEN_OWNER_ADDR], + }); + + await network.provider.send("hardhat_setBalance", [ + TOKEN_OWNER_ADDR, + "0x1000000000000000", + ]); + + // get tokenOwner + tokenOwner = await ethers.getSigner( + TOKEN_OWNER_ADDR + ); + nftContract = await ethers.getContractAt(erc1155Artifacts.abi, TOKEN_CONTRACT_ADDR) + masterSigner = await getMasterSigner(wallet3) + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + + instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr); + InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation") + instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); + await instaAccountV2DefaultImpl.deployed() + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: connectV2BasicERC1155Artifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + + describe("Implementations", function () { + + it("Should add default implementation to mapping.", async function () { + const tx = await instaImplementationsMapping.connect(masterSigner).setDefaultImplementation(instaAccountV2DefaultImpl.address); + await tx.wait() + expect(await instaImplementationsMapping.defaultImplementation()).to.be.equal(instaAccountV2DefaultImpl.address); + }); + + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(tokenOwner.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should deposit successfully", async () => { + console.log("DSA wallet address", dsaWallet0.address) + await nftContract.connect(tokenOwner).setApprovalForAll(dsaWallet0.address, true); + const spells = [ + { + connector: connectorName, + method: "depositERC1155", + args: [ + TOKEN_CONTRACT_ADDR, + TOKEN_ID, + 1, + "0", + "0" + ] + } + ]; + + const tx = await dsaWallet0 + .connect(tokenOwner) + .cast(...encodeSpells(spells), tokenOwner.address); + const receipt = await tx.wait(); + }); + }) +}) diff --git a/test/basic-ERC721/ERC721-transfer.js b/test/basic-ERC721/ERC721-transfer.js new file mode 100644 index 00000000..01a5de0b --- /dev/null +++ b/test/basic-ERC721/ERC721-transfer.js @@ -0,0 +1,126 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle +const {abi: implementationsABI} = require("../../scripts/constant/abi/core/InstaImplementations.json") + +const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js") +const buildDSAv2 = require("../../scripts/buildDSAv2") +const encodeSpells = require("../../scripts/encodeSpells.js") +const getMasterSigner = require("../../scripts/getMasterSigner") + +const addresses = require("../../scripts/constant/addresses"); +const abis = require("../../scripts/constant/abis"); +const constants = require("../../scripts/constant/constant"); +const tokens = require("../../scripts/constant/tokens"); + +const connectV2BasicERC721Artifacts = require("../../artifacts/contracts/mainnet/connectors/basic-ERC721/main.sol/ConnectV2BasicERC721.json") +const erc721Artifacts = require("../../artifacts/@openzeppelin/contracts/token/ERC721/IERC721.sol/IERC721.json") + +const TOKEN_CONTRACT_ADDR = "0x4d695c615a7aacf2d7b9c481b66045bb2457dfde"; +const TOKEN_OWNER_ADDR = "0x8c6b10d42ff08e56133fca0dac75e1931b1fcc23"; +const TOKEN_ID = "38"; + +const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D" + +describe("BASIC-ERC721", function () { + const connectorName = "BASIC-ERC721-A" + + let dsaWallet0 + let masterSigner; + let instaConnectorsV2; + let connector; + let nftContract; + let tokenOwner; + let instaImplementationsMapping; + + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [TOKEN_OWNER_ADDR], + }); + + await network.provider.send("hardhat_setBalance", [ + TOKEN_OWNER_ADDR, + "0x1000000000000000", + ]); + + // get tokenOwner + tokenOwner = await ethers.getSigner( + TOKEN_OWNER_ADDR + ); + nftContract = await ethers.getContractAt(erc721Artifacts.abi, TOKEN_CONTRACT_ADDR) + masterSigner = await getMasterSigner(wallet3) + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + + instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr); + InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation") + instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); + await instaAccountV2DefaultImpl.deployed() + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: connectV2BasicERC721Artifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + + describe("Implementations", function () { + + it("Should add default implementation to mapping.", async function () { + const tx = await instaImplementationsMapping.connect(masterSigner).setDefaultImplementation(instaAccountV2DefaultImpl.address); + await tx.wait() + expect(await instaImplementationsMapping.defaultImplementation()).to.be.equal(instaAccountV2DefaultImpl.address); + }); + + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(tokenOwner.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should deposit successfully", async () => { + console.log("DSA wallet address", dsaWallet0.address) + await nftContract.connect(tokenOwner).setApprovalForAll(dsaWallet0.address, true); + const spells = [ + { + connector: connectorName, + method: "depositERC721", + args: [ + TOKEN_CONTRACT_ADDR, + TOKEN_ID, + "0", + "0" + ] + } + ]; + + const tx = await dsaWallet0 + .connect(tokenOwner) + .cast(...encodeSpells(spells), tokenOwner.address); + const receipt = await tx.wait(); + }); + }) +}) diff --git a/test/uniswapStake/uniswapStake.test.js b/test/uniswapStake/uniswapStake.test.js new file mode 100644 index 00000000..043bd07f --- /dev/null +++ b/test/uniswapStake/uniswapStake.test.js @@ -0,0 +1,342 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle + +const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js") +const buildDSAv2 = require("../../scripts/buildDSAv2") +const encodeSpells = require("../../scripts/encodeSpells.js") +const getMasterSigner = require("../../scripts/getMasterSigner") +const addLiquidity = require("../../scripts/addLiquidity"); + +const addresses = require("../../scripts/constant/addresses"); +const abis = require("../../scripts/constant/abis"); +const { abi: nftManagerAbi } = require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json") + +const connectV2UniswapStakerArtifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/v3_staker/main.sol/ConnectV2UniswapV3Staker.json"); +const connectV2UniswapV3Artifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/v3/main.sol/ConnectV2UniswapV3.json"); + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000, +} + +const TICK_SPACINGS = { + 500: 10, + 3000: 60, + 10000: 200 +} + +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f" +const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +const INST_ADDR = "0x6f40d4a6237c257fff2db00fa0510deeecd303eb" + +let tokenIds = [] +const abiCoder = ethers.utils.defaultAbiCoder + +describe("UniswapV3", function () { + const connectorStaker = "UniswapStaker-v1" + const connectorUniswap = "UniswapV3-v1" + + let dsaWallet0 + let masterSigner; + let instaConnectorsV2; + let connector; + let startTime, endTime; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + masterSigner = await getMasterSigner(wallet3) + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + nftManager = await ethers.getContractAt(nftManagerAbi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"); + connector = await deployAndEnableConnector({ + connectorName: connectorStaker, + contractArtifact: connectV2UniswapStakerArtifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + + uniswapConnector = await deployAndEnableConnector({ + connectorName: connectorUniswap, + contractArtifact: connectV2UniswapV3Artifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + + it("Deposit ETH & USDT & INST into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000")); + await addLiquidity("inst", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH + const daiAmount = ethers.utils.parseEther("400") // 1 ETH + const instAmount = ethers.utils.parseEther("50") + + it("Should mint successfully", async function () { + const getIds = ["0", "0"] + const setId = "0" + + const spells = [ + { + connector: connectorUniswap, + method: "mint", + args: [ + DAI_ADDR, + ethAddress, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + daiAmount, + ethAmount, + "500000000000000000", + getIds, + setId + ], + }, + { + connector: connectorUniswap, + method: "mint", + args: [ + INST_ADDR, + ethAddress, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + instAmount, + ethAmount, + "500000000000000000", + getIds, + setId + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => { + const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[0]); + const params1 = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[1]); + tokenIds.push(params[0]); + tokenIds.push(params1[0]); + event.removeListener(); + + resolve({ + eventNames, + }); + }); + + setTimeout(() => { + reject(new Error('timeout')); + }, 60000) + }); + + let event = await castEvent + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance) + }); + + it("Should create incentive successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "createIncentive", + args: [ + ethAddress, + "1000", + dsaWallet0.address, + "0xc2e9f25be6257c210d7adf0d4cd6e3e881ba25f8", + ethers.utils.parseEther("0.01") + ], + }, + { + connector: connectorStaker, + method: "createIncentive", + args: [ + INST_ADDR, + "50", + dsaWallet0.address, + "0xcba27c8e7115b4eb50aa14999bc0866674a96ecb", + ethers.utils.parseEther("0.01") + ], + }] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + let receipt = await tx.wait() + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => { + const params = abiCoder.decode(["bytes32","address","address","uint256","uint256","uint256"], eventParams[0]); + const params1 = abiCoder.decode(["bytes32","address","address","uint256","uint256","uint256"], eventParams[1]); + event.removeListener(); + + resolve({ start: [params[3], params1[3]], end: [params[4], params1[4]] }); + }); + + setTimeout(() => { + reject(new Error('timeout')); + }, 60000) + }); + + let event = await castEvent + startTime = event.start; + endTime = event.end; + }); + + it("Should stake successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "deposit", + args: [ + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "stake", + args: [ + ethAddress, + startTime[0], + endTime[0], + dsaWallet0.address, + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "deposit", + args: [ + tokenIds[1] + ], + }, + { + connector: connectorStaker, + method: "stake", + args: [ + INST_ADDR, + startTime[1], + endTime[1], + dsaWallet0.address, + tokenIds[1] + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance) + }); + + it("Should claim rewards successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "claimRewards", + args: [ + ethAddress, + "1000", + ], + connector: connectorStaker, + method: "claimRewards", + args: [ + INST_ADDR, + "1000", + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + }); + + it("Should unstake successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "unstake", + args: [ + ethAddress, + startTime[0], + endTime[0], + dsaWallet0.address, + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "withdraw", + args: [ + tokenIds[0], + ], + }, + { + connector: connectorStaker, + method: "unstake", + args: [ + INST_ADDR, + startTime[1], + endTime[1], + dsaWallet0.address, + tokenIds[1] + ], + }, + { + connector: connectorStaker, + method: "withdraw", + args: [ + tokenIds[1] + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance) + }); + }) +}) + +const getMinTick = (tickSpacing) => Math.ceil(-887272 / tickSpacing) * tickSpacing +const getMaxTick = (tickSpacing) => Math.floor(887272 / tickSpacing) * tickSpacing