From ed0ec8cc2f0e26ef7b1021b6361ed7ed76d1db67 Mon Sep 17 00:00:00 2001 From: 0xthrpw <0xthrpw@gmail.com> Date: Tue, 13 Apr 2021 17:25:41 -0400 Subject: [PATCH 1/5] lp incentive contracts --- contracts/ISuperStaking.sol | 22 +++++ contracts/SuperStaking.sol | 141 ++++++++++++++++++++++++++++ contracts/test/SuperStaking.test.js | 118 +++++++++++++++++++++++ package.json | 1 + 4 files changed, 282 insertions(+) create mode 100644 contracts/ISuperStaking.sol create mode 100644 contracts/SuperStaking.sol create mode 100644 contracts/test/SuperStaking.test.js diff --git a/contracts/ISuperStaking.sol b/contracts/ISuperStaking.sol new file mode 100644 index 0000000..a6152ab --- /dev/null +++ b/contracts/ISuperStaking.sol @@ -0,0 +1,22 @@ +pragma solidity 0.7.3; + +interface ISuperStaking { + // Views + function lastTimeRewardApplicable() external view returns (uint256); + + function rewardPerToken() external view returns (uint256); + + function earned(address account) external view returns (uint256); + + function balanceOf(address account) external view returns (uint256); + + // Mutative + + function stake(uint256 amount) external; + + function withdraw(uint256 amount) external; + + function getReward() external; + + function exit() external; +} \ No newline at end of file diff --git a/contracts/SuperStaking.sol b/contracts/SuperStaking.sol new file mode 100644 index 0000000..4b2a5a4 --- /dev/null +++ b/contracts/SuperStaking.sol @@ -0,0 +1,141 @@ +pragma solidity 0.7.3; + +import "@openzeppelin/contracts/math/Math.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +import './ISuperStaking.sol'; + +contract SuperStaking is ISuperStaking, Ownable, ReentrancyGuard { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + uint256 public periodFinish; + uint256 public rewardRate; + uint256 public rewardsDuration; + uint256 public lastUpdateTime; + uint256 public rewardPerTokenStored; + uint256 public lastBalance; + uint256 public totalSupply; + + IERC20 public rewardsToken; + IERC20 public stakingToken; + + mapping(address => uint256) private userRewardPerTokenPaid; + mapping(address => uint256) private rewards; + mapping(address => uint256) private _balances; + + constructor( + address _owner, + address _rewardsToken, + address _stakingToken + ) { + rewardsToken = IERC20(_rewardsToken); + stakingToken = IERC20(_stakingToken); + } + + function balanceOf(address account) external override view returns (uint256) { + return _balances[account]; + } + + function lastTimeRewardApplicable() public override view returns (uint256) { + return Math.min(block.timestamp, periodFinish); + } + + function rewardPerToken() public override view returns (uint256) { + if (totalSupply == 0) { + return rewardPerTokenStored; + } + return rewardPerTokenStored.add(lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply)); + } + + function earned(address account) public override view returns (uint256) { + return _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]); + } + + /* ========== MUTATIVE FUNCTIONS ========== */ + + function stake(uint256 amount) external override nonReentrant updateReward(msg.sender) { + require(amount > 0, 'Cannot stake 0'); + totalSupply = totalSupply.add(amount); + _balances[msg.sender] = _balances[msg.sender].add(amount); + stakingToken.safeTransferFrom(msg.sender, address(this), amount); + emit Staked(msg.sender, amount); + } + + function withdraw(uint256 amount) public override nonReentrant updateReward(msg.sender) { + require(amount > 0, 'Cannot withdraw 0'); + totalSupply = totalSupply.sub(amount); + _balances[msg.sender] = _balances[msg.sender].sub(amount); + stakingToken.safeTransfer(msg.sender, amount); + emit Withdrawn(msg.sender, amount); + } + + function getReward() public override nonReentrant updateReward(msg.sender) { + uint256 reward = rewards[msg.sender]; + if (reward > 0) { + rewards[msg.sender] = 0; + rewardsToken.safeTransfer(msg.sender, reward); + emit RewardPaid(msg.sender, reward); + } + } + + function exit() external override { + withdraw(_balances[msg.sender]); + getReward(); + } + + /* ========== RESTRICTED FUNCTIONS ========== */ + + function notifyRewardAmount(uint256 reward) external onlyOwner updateReward(address(0)) { + require(rewardRate > 0, 'Reward Rate is not yet set'); + if (block.timestamp >= periodFinish) { + rewardsDuration = reward.div(rewardRate); + } else { + uint256 remaining = periodFinish.sub(block.timestamp); + uint256 leftover = remaining.mul(rewardRate); + rewardsDuration = reward.add(leftover).div(rewardRate); + } + + // Ensure the provided reward amount is not more than the balance in the contract. + // This keeps the reward rate in the right range, preventing overflows due to + // very high values of rewardRate in the earned and rewardsPerToken functions; + // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. + uint256 balance = rewardsToken.balanceOf(address(this)); + require(rewardRate <= balance.div(rewardsDuration), 'Provided reward too high'); + + lastUpdateTime = block.timestamp; + periodFinish = block.timestamp.add(rewardsDuration); + emit RewardAdded(reward); + } + + function setRewardRate(uint256 rewardsPerInterval, uint256 interval) external onlyOwner { + require(rewardsPerInterval > 0 && interval > 0, 'rewardsPerInterval and interval should be greater than 0'); + require(block.timestamp > periodFinish, 'Previous rewards period must be complete before changing the reward rate'); + rewardRate = rewardsPerInterval.div(interval); + + RewardRateUpdated(rewardsPerInterval, interval, rewardRate); + } + + /* ========== MODIFIERS ========== */ + + modifier updateReward(address account) { + rewardPerTokenStored = rewardPerToken(); + lastUpdateTime = lastTimeRewardApplicable(); + if (account != address(0)) { + rewards[account] = earned(account); + userRewardPerTokenPaid[account] = rewardPerTokenStored; + } + _; + } + + /* ========== EVENTS ========== */ + + event RewardAdded(uint256 reward); + event Staked(address indexed user, uint256 amount); + event Withdrawn(address indexed user, uint256 amount); + event RewardPaid(address indexed user, uint256 reward); + event RewardRateUpdated(uint256 rewardsPerInterval, uint256 interval, uint256 rewardRate); +} \ No newline at end of file diff --git a/contracts/test/SuperStaking.test.js b/contracts/test/SuperStaking.test.js new file mode 100644 index 0000000..e48e0c9 --- /dev/null +++ b/contracts/test/SuperStaking.test.js @@ -0,0 +1,118 @@ +//'use strict'; +const hre = require('hardhat') +const { ethers } = require('hardhat') +const { describe, it } = require('mocha') +const { expect } = require('chai') + +const UniswapV2PairABI = require("@uniswap/v2-core/build/UniswapV2Pair.json").abi; + +async function fastForward(amount){ + const currentBlock = await ethers.provider.getBlockNumber() + const block = await ethers.provider.getBlock(currentBlock) + const timestamp = block.timestamp + amount + + await hre.network.provider.request({ + method: 'evm_setNextBlockTimestamp', + params: [timestamp] + }) + await hre.network.provider.send("evm_mine") +} + +const toToken = (count) => { + return count * (10 ** 18) +} + +const fromToken = (count) => { + return parseInt(count) / (10 ** 18) +} + + +SUPER_TOKEN = '0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55'; +SUPER_HOLDER = '0xf8e6E9cEAc3828499DDDf63AC91BBEb42A88e965'; +SUPER_WETH_LP = '0x25647e01bd0967c1b9599fa3521939871d1d0888'; +SUPER_WETH_LP_HOLDER = '0x87ad7b8e02ea527f63051692b9cbd9fd137e8de4'; + + +describe("SuperFarmDAO Liquidity Incentive Program: Tests", function() { + let alice, bob, dev, super_deployer; + let SuperStaking, SuperToken, SuperLP; + + before(async () => { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [SUPER_HOLDER] + }) + super_deployer = await ethers.provider.getSigner(SUPER_HOLDER) + + const signers = await ethers.getSigners(); + const addresses = await Promise.all(signers.map(async signer => signer.getAddress())); + alice = { provider: signers[0].provider, signer: signers[0], address: addresses[0] }; + bob = { provider: signers[1].provider, signer: signers[1], address: addresses[1] }; + dev = { provider: signers[3].provider, signer: signers[3], address: addresses[3] }; + + SuperToken = await ethers.getContractAt('contracts/Token.sol:Token', SUPER_TOKEN); + SuperLP = await ethers.getContractAt(UniswapV2PairABI, SUPER_WETH_LP); + SuperStakingContract = await ethers.getContractFactory('SuperStaking'); + + SuperStaking = await SuperStakingContract.connect(super_deployer).deploy(dev.address, SUPER_TOKEN, SUPER_WETH_LP); + await SuperStaking.deployed(); + + let rewardAmount = await ethers.utils.parseEther('1000000') + await SuperToken.connect(super_deployer).transfer(SuperStaking.address, rewardAmount); + }); + + beforeEach(async () => { + console.log(' -------------------------------------------------------------------------'); + }); + + it('senseless token (in)sanity check', async function () { + let remainingSupply = await ethers.utils.parseEther('1500000') + expect( + await SuperToken.balanceOf(SUPER_HOLDER) + ).to.equal(remainingSupply) + }) + + it("Test Staking Settings", async function() { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [SUPER_WETH_LP_HOLDER] + }) + super_weth_bags = await ethers.provider.getSigner(SUPER_WETH_LP_HOLDER) + let super_weth_bags_balance = await SuperLP.balanceOf(SUPER_WETH_LP_HOLDER) + + let programLength = 2592000; //one month + let rewardAmount = await ethers.utils.parseEther('1000000') + let rewardRate = rewardAmount.div(programLength); + + await SuperStaking.connect(super_deployer).setRewardRate(rewardRate, 1) + expect(await SuperStaking.rewardRate()).to.equal(rewardRate) + + await SuperStaking.connect(super_deployer).notifyRewardAmount(rewardAmount) + + await SuperLP.connect(super_weth_bags).approve(SuperStaking.address, ethers.utils.parseEther('2000000')); + SuperStaking = SuperStaking.connect(super_weth_bags) + let stakingAmount = await ethers.utils.parseEther('20'); + await SuperStaking.stake(stakingAmount); + + expect( + await SuperLP.balanceOf(SuperStaking.address) + ).to.equal(stakingAmount) + + fastForward(programLength); + }); + + it("Check Earnings", async function() { + + const currentBlock = await ethers.provider.getBlockNumber() + const block = await ethers.provider.getBlock(currentBlock) + + await SuperStaking.stake(1); //call updatepool modifier indirectly + expect( + await SuperStaking.earned(SUPER_WETH_LP_HOLDER) + ).to.be.gt(ethers.utils.parseEther('999999')).and.to.be.lt(ethers.utils.parseEther('1000000')) + + let period = await SuperStaking.periodFinish(); + console.log(period.toString(), block.timestamp, "periodFinish"); + }); + +}); diff --git a/package.json b/package.json index 7571b25..ec52286 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "@uniswap/lib": "^1.1.4", "@uniswap/v2-core": "^1.0.1", + "@uniswap/v2-periphery": "^1.1.0-beta.0", "dotenv": "^8.2.0", "limiter": "^1.1.5" }, From 41304551c71bb615b027a23e65759480c0227e87 Mon Sep 17 00:00:00 2001 From: Timothy Clancy Date: Tue, 13 Apr 2021 18:09:18 -0400 Subject: [PATCH 2/5] Included 0.7.3 compiler. --- hardhat-ganache-tests.config.js | 8 ++++++++ hardhat.config.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/hardhat-ganache-tests.config.js b/hardhat-ganache-tests.config.js index 3df3a99..08f638c 100644 --- a/hardhat-ganache-tests.config.js +++ b/hardhat-ganache-tests.config.js @@ -33,6 +33,14 @@ module.exports = { enabled: true } } + }, + { + version: '0.7.3', + settings: { + optimizer: { + enabled: true + } + } } ] }, diff --git a/hardhat.config.js b/hardhat.config.js index 5bb42aa..0bde7f3 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -37,6 +37,14 @@ module.exports = { enabled: true } } + }, + { + version: '0.7.3', + settings: { + optimizer: { + enabled: true + } + } } ] }, From 73e94f8c4ed915596238f2a24c685613e604650e Mon Sep 17 00:00:00 2001 From: Timothy Clancy Date: Tue, 13 Apr 2021 18:15:39 -0400 Subject: [PATCH 3/5] Upgraded Fee1155NFTLockable to version 2. --- contracts/Fee1155NFTLockable.sol | 22 ++++++++++++++++------ hardhat-ganache-tests.config.js | 2 ++ hardhat.config.js | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/contracts/Fee1155NFTLockable.sol b/contracts/Fee1155NFTLockable.sol index 862ed41..320f7a7 100644 --- a/contracts/Fee1155NFTLockable.sol +++ b/contracts/Fee1155NFTLockable.sol @@ -35,10 +35,7 @@ contract Fee1155NFTLockable is ERC1155, Ownable { using SafeMath for uint256; /// A version number for this fee-bearing 1155 item contract's interface. - uint256 public version = 2; - - /// @dev A mask for isolating an item's group ID. - uint256 constant GROUP_MASK = uint256(uint128(~0)) << 128; + uint256 public version = 1; /// The ERC-1155 URI for looking up item metadata using {id} substitution. string public metadataUri; @@ -49,6 +46,12 @@ contract Fee1155NFTLockable is ERC1155, Ownable { /// Specifically whitelist an OpenSea proxy registry address. address public proxyRegistryAddress; + /// A counter to enforce unique IDs for each item group minted. + uint256 public nextItemGroupId; + + /// This mapping tracks the number of unique items within each item group. + mapping (uint256 => uint256) public itemGroupSizes; + /// Whether or not the item collection has been locked to further minting. bool public locked; @@ -67,6 +70,7 @@ contract Fee1155NFTLockable is ERC1155, Ownable { metadataUri = _uri; feeOwner = _feeOwner; proxyRegistryAddress = _proxyRegistryAddress; + nextItemGroupId = 0; locked = false; } @@ -112,7 +116,7 @@ contract Fee1155NFTLockable is ERC1155, Ownable { @param amounts The amount of each corresponding item ID to create. @param data Any associated data to use on items minted in this transaction. */ - function createNFT(address recipient, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external onlyOwner { + function createNFT(address recipient, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external onlyOwner returns (uint256) { require(!locked, "You cannot create more NFTs on a locked collection."); require(ids.length > 0, @@ -120,10 +124,16 @@ contract Fee1155NFTLockable is ERC1155, Ownable { require(ids.length == amounts.length, "IDs length cannot be mismatched with amounts length."); + // Create an item group of requested size using the next available ID. + uint256 shiftedGroupId = nextItemGroupId << 128; + itemGroupSizes[shiftedGroupId] = ids.length; + // Mint the entire batch of items. _mintBatch(recipient, ids, amounts, data); // Increment our next item group ID and return our created item group ID. - emit ItemGroupCreated(ids[0] & GROUP_MASK, ids.length, msg.sender); + nextItemGroupId = nextItemGroupId.add(1); + emit ItemGroupCreated(shiftedGroupId, ids.length, msg.sender); + return shiftedGroupId; } } diff --git a/hardhat-ganache-tests.config.js b/hardhat-ganache-tests.config.js index 08f638c..9544f6d 100644 --- a/hardhat-ganache-tests.config.js +++ b/hardhat-ganache-tests.config.js @@ -1,3 +1,5 @@ +'use strict'; + // Configure environment variables. require('dotenv').config(); diff --git a/hardhat.config.js b/hardhat.config.js index 0bde7f3..fade3b2 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,3 +1,5 @@ +'use strict'; + // Configure environment variables. require('dotenv').config(); From 311947ab846c6ae5586a4edb48ac7e2bf94a7e16 Mon Sep 17 00:00:00 2001 From: Timothy Clancy Date: Tue, 13 Apr 2021 18:25:02 -0400 Subject: [PATCH 4/5] Fixed error in unit test. --- test/ShopPlatformLaunchpad1155.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ShopPlatformLaunchpad1155.test.js b/test/ShopPlatformLaunchpad1155.test.js index dacfc75..5e43731 100644 --- a/test/ShopPlatformLaunchpad1155.test.js +++ b/test/ShopPlatformLaunchpad1155.test.js @@ -630,7 +630,7 @@ describe('ShopPlatformLaunchpad1155', function () { name: 'Test Pool', startBlock: currentBlockNumber, endBlock: currentBlockNumber + 100, - purchaseLimit: 1, + purchaseLimit: 4, requirement: { requiredType: 0, requiredAsset: ethers.constants.AddressZero, From df3db1173a7b35f610a3bc0977453bcfd0c5d08b Mon Sep 17 00:00:00 2001 From: 0xthrpw <0xthrpw@gmail.com> Date: Tue, 13 Apr 2021 23:41:47 -0400 Subject: [PATCH 5/5] tests, fixes for ffwd, impersonation --- contracts/test/SuperStaking.test.js | 118 ---------------------------- package.json | 2 +- test/SuperStaking.test.js | 107 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 119 deletions(-) delete mode 100644 contracts/test/SuperStaking.test.js create mode 100644 test/SuperStaking.test.js diff --git a/contracts/test/SuperStaking.test.js b/contracts/test/SuperStaking.test.js deleted file mode 100644 index e48e0c9..0000000 --- a/contracts/test/SuperStaking.test.js +++ /dev/null @@ -1,118 +0,0 @@ -//'use strict'; -const hre = require('hardhat') -const { ethers } = require('hardhat') -const { describe, it } = require('mocha') -const { expect } = require('chai') - -const UniswapV2PairABI = require("@uniswap/v2-core/build/UniswapV2Pair.json").abi; - -async function fastForward(amount){ - const currentBlock = await ethers.provider.getBlockNumber() - const block = await ethers.provider.getBlock(currentBlock) - const timestamp = block.timestamp + amount - - await hre.network.provider.request({ - method: 'evm_setNextBlockTimestamp', - params: [timestamp] - }) - await hre.network.provider.send("evm_mine") -} - -const toToken = (count) => { - return count * (10 ** 18) -} - -const fromToken = (count) => { - return parseInt(count) / (10 ** 18) -} - - -SUPER_TOKEN = '0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55'; -SUPER_HOLDER = '0xf8e6E9cEAc3828499DDDf63AC91BBEb42A88e965'; -SUPER_WETH_LP = '0x25647e01bd0967c1b9599fa3521939871d1d0888'; -SUPER_WETH_LP_HOLDER = '0x87ad7b8e02ea527f63051692b9cbd9fd137e8de4'; - - -describe("SuperFarmDAO Liquidity Incentive Program: Tests", function() { - let alice, bob, dev, super_deployer; - let SuperStaking, SuperToken, SuperLP; - - before(async () => { - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [SUPER_HOLDER] - }) - super_deployer = await ethers.provider.getSigner(SUPER_HOLDER) - - const signers = await ethers.getSigners(); - const addresses = await Promise.all(signers.map(async signer => signer.getAddress())); - alice = { provider: signers[0].provider, signer: signers[0], address: addresses[0] }; - bob = { provider: signers[1].provider, signer: signers[1], address: addresses[1] }; - dev = { provider: signers[3].provider, signer: signers[3], address: addresses[3] }; - - SuperToken = await ethers.getContractAt('contracts/Token.sol:Token', SUPER_TOKEN); - SuperLP = await ethers.getContractAt(UniswapV2PairABI, SUPER_WETH_LP); - SuperStakingContract = await ethers.getContractFactory('SuperStaking'); - - SuperStaking = await SuperStakingContract.connect(super_deployer).deploy(dev.address, SUPER_TOKEN, SUPER_WETH_LP); - await SuperStaking.deployed(); - - let rewardAmount = await ethers.utils.parseEther('1000000') - await SuperToken.connect(super_deployer).transfer(SuperStaking.address, rewardAmount); - }); - - beforeEach(async () => { - console.log(' -------------------------------------------------------------------------'); - }); - - it('senseless token (in)sanity check', async function () { - let remainingSupply = await ethers.utils.parseEther('1500000') - expect( - await SuperToken.balanceOf(SUPER_HOLDER) - ).to.equal(remainingSupply) - }) - - it("Test Staking Settings", async function() { - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [SUPER_WETH_LP_HOLDER] - }) - super_weth_bags = await ethers.provider.getSigner(SUPER_WETH_LP_HOLDER) - let super_weth_bags_balance = await SuperLP.balanceOf(SUPER_WETH_LP_HOLDER) - - let programLength = 2592000; //one month - let rewardAmount = await ethers.utils.parseEther('1000000') - let rewardRate = rewardAmount.div(programLength); - - await SuperStaking.connect(super_deployer).setRewardRate(rewardRate, 1) - expect(await SuperStaking.rewardRate()).to.equal(rewardRate) - - await SuperStaking.connect(super_deployer).notifyRewardAmount(rewardAmount) - - await SuperLP.connect(super_weth_bags).approve(SuperStaking.address, ethers.utils.parseEther('2000000')); - SuperStaking = SuperStaking.connect(super_weth_bags) - let stakingAmount = await ethers.utils.parseEther('20'); - await SuperStaking.stake(stakingAmount); - - expect( - await SuperLP.balanceOf(SuperStaking.address) - ).to.equal(stakingAmount) - - fastForward(programLength); - }); - - it("Check Earnings", async function() { - - const currentBlock = await ethers.provider.getBlockNumber() - const block = await ethers.provider.getBlock(currentBlock) - - await SuperStaking.stake(1); //call updatepool modifier indirectly - expect( - await SuperStaking.earned(SUPER_WETH_LP_HOLDER) - ).to.be.gt(ethers.utils.parseEther('999999')).and.to.be.lt(ethers.utils.parseEther('1000000')) - - let period = await SuperStaking.periodFinish(); - console.log(period.toString(), block.timestamp, "periodFinish"); - }); - -}); diff --git a/package.json b/package.json index ec52286..244ad66 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "homepage": "https://github.com/SuperFarmDAO/SuperFarm-Contracts#readme", "scripts": { - "test": "npx hardhat test; echo 'Executing test cases which require Ganache ...'; npx hardhat --config hardhat-ganache-tests.config.js --network ganache test;", + "test": "npx hardhat test; echo 'Executing test cases which require Ganache ...';", "lint": "npx eslint ./", "validate": "npm-run-all --parallel test lint", "contract-size": "npx hardhat size-contracts" diff --git a/test/SuperStaking.test.js b/test/SuperStaking.test.js new file mode 100644 index 0000000..8721351 --- /dev/null +++ b/test/SuperStaking.test.js @@ -0,0 +1,107 @@ +'use strict'; +// const hre = require('hardhat'); +const { network, ethers } = require('hardhat'); +const { describe, it } = require('mocha'); +const { expect } = require('chai'); +require('dotenv').config(); + +const overrides = { + gasPrice: ethers.utils.parseUnits('0', 'gwei') +}; + +const UniswapV2PairABI = require('@uniswap/v2-core/build/UniswapV2Pair.json').abi; + +async function fastForward (amount) { + const currentBlock = await ethers.provider.getBlockNumber(); + const block = await ethers.provider.getBlock(currentBlock); + const timestamp = block.timestamp + amount; + + await network.provider.request({ + method: 'evm_setNextBlockTimestamp', + params: [timestamp] + }); + await network.provider.send('evm_mine'); +} + +const SUPER_TOKEN = '0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55'; +const SUPER_HOLDER = '0xf8e6E9cEAc3828499DDDf63AC91BBEb42A88e965'; +const SUPER_WETH_LP = '0x25647e01bd0967c1b9599fa3521939871d1d0888'; +const SUPER_WETH_LP_HOLDER = '0x87ad7b8e02ea527f63051692b9cbd9fd137e8de4'; + +describe('SuperFarmDAO Liquidity Incentive Program: Tests', function () { + let superDeployer; + let SuperStaking, SuperToken, SuperLP; + + before(async () => { + await network.provider.request({ + method: 'hardhat_reset', + params: [{ + forking: { + jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, + blockNumber: 11969707 + } + }] + }); + await network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [SUPER_HOLDER] + }); + + superDeployer = await ethers.provider.getSigner(SUPER_HOLDER); + + SuperToken = await ethers.getContractAt('contracts/Token.sol:Token', SUPER_TOKEN); + + let SuperStakingContract = await ethers.getContractFactory('SuperStaking'); + SuperLP = await ethers.getContractAt(UniswapV2PairABI, SUPER_WETH_LP); + SuperStaking = await SuperStakingContract.connect(superDeployer).deploy(SUPER_HOLDER, SUPER_TOKEN, SUPER_WETH_LP, overrides); + await SuperStaking.deployed(); + + let rewardAmount = await ethers.utils.parseEther('1000000'); + await SuperToken.connect(superDeployer).transfer(SuperStaking.address, rewardAmount, overrides); + }); + + it('Test Staking Settings', async function () { + await network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [SUPER_WETH_LP_HOLDER] + }); + let superWethBags = await ethers.provider.getSigner(SUPER_WETH_LP_HOLDER); + + let programLength = 2592000; // one month + let rewardAmount = ethers.utils.parseEther('1000000'); + let rewardRate = rewardAmount.div(programLength); + + const connectedStake = await SuperStaking.connect(superDeployer); + await connectedStake.setRewardRate(rewardRate, 1, overrides); + expect(await connectedStake.rewardRate()).to.equal(rewardRate); + + await connectedStake.notifyRewardAmount(rewardAmount, overrides); + + await SuperLP.connect(superWethBags).approve(SuperStaking.address, ethers.utils.parseEther('2000000'), overrides); + SuperStaking = SuperStaking.connect(superWethBags); + let stakingAmount = await ethers.utils.parseEther('20'); + await SuperStaking.stake(stakingAmount, overrides); + + expect( + await SuperLP.balanceOf(SuperStaking.address) + ).to.equal(stakingAmount); + + fastForward(programLength); + }); + + it('Check Earnings', async function () { + await SuperStaking.stake(1, overrides); // call updatepool modifier indirectly + expect( + await SuperStaking.earned(SUPER_WETH_LP_HOLDER, overrides) + ).to.be.gt(ethers.utils.parseEther('999999')).and.to.be.lt(ethers.utils.parseEther('1000000')); + // const currentBlock = await ethers.provider.getBlockNumber(); + // const block = await ethers.provider.getBlock(currentBlock); + // let period = await SuperStaking.periodFinish(); + // console.log(period.toString(), block.timestamp, 'periodFinish'); + + await network.provider.request({ + method: 'hardhat_reset', + params: [] + }); + }); +});