diff --git a/contracts/LiquidityMining.sol b/contracts/LiquidityMining.sol index 8fb9350..648de20 100644 --- a/contracts/LiquidityMining.sol +++ b/contracts/LiquidityMining.sol @@ -27,7 +27,7 @@ contract LiquidityMining is ERC20, Ownable { bool public miningAllowed; Tier[] public tiers; - mapping(address user => Stake) public stakes; + mapping(address user => Stake[]) public stakes; event DisableMining(); event StakeLocked( @@ -79,10 +79,12 @@ contract LiquidityMining is ERC20, Ownable { } } + function getStakes(address user) public view returns(Stake[] memory) { + return stakes[user]; + } + function stake(address scoreTo, uint256 amount, uint256 tierId) public { - if (amount > 0) { - STAKING_TOKEN.safeTransferFrom(_msgSender(), address(this), amount); - } + STAKING_TOKEN.safeTransferFrom(_msgSender(), address(this), amount); _stake(_msgSender(), scoreTo, amount, tierId); } @@ -107,8 +109,8 @@ contract LiquidityMining is ERC20, Ownable { stake(scoreTo, amount, tierId); } - function unstake(address to) external { - uint256 amount = _unstake(_msgSender(), to); + function unstake(uint256 id, address to) external { + uint256 amount = _unstake(_msgSender(), id, to); STAKING_TOKEN.safeTransfer(to, amount); } @@ -119,38 +121,28 @@ contract LiquidityMining is ERC20, Ownable { } function _stake(address from, address scoreTo, uint256 amount, uint256 tierId) internal { + require(amount > 0, ZeroAmount()); require(miningAllowed, MiningDisabled()); - Stake memory currentStake = stakes[from]; + Stake memory currentStake; Tier memory tier = tiers[tierId]; - uint256 pendingScore = 0; - if (notReached(currentStake.until)) { - uint256 remainingTime = till(currentStake.until); - require(tier.period >= remainingTime, DecreasingPeriod()); - pendingScore = Math.ceilDiv( - currentStake.amount * remainingTime * uint256(currentStake.multiplier), - MULTIPLIER_PRECISION * currentStake.period - ); - } - currentStake.amount += amount; + currentStake.amount = amount; currentStake.period = tier.period; currentStake.until = timeNow() + tier.period; currentStake.multiplier = tier.multiplier; - stakes[from] = currentStake; - uint256 newPendingScore = + stakes[from].push(currentStake); + uint256 addedScore = currentStake.amount * uint256(tier.multiplier) / uint256(MULTIPLIER_PRECISION); - require(newPendingScore > pendingScore, InvalidAddedScore()); - uint256 addedScore = newPendingScore - pendingScore; _mint(scoreTo, addedScore); - emit StakeLocked(from, scoreTo, amount, currentStake.amount, currentStake.until, addedScore); + emit StakeLocked(from, scoreTo, amount, amount, currentStake.until, addedScore); } - function _unstake(address from, address to) internal returns (uint256) { - Stake memory currentStake = stakes[from]; + function _unstake(address from, uint256 id, address to) internal returns (uint256) { + Stake memory currentStake = stakes[from][id]; require(currentStake.amount > 0, ZeroAmount()); require(reached(currentStake.until), Locked()); - delete stakes[from]; + delete stakes[from][id]; emit StakeUnlocked(_msgSender(), to, currentStake.amount); @@ -164,15 +156,4 @@ contract LiquidityMining is ERC20, Ownable { function reached(uint32 timestamp) internal view returns (bool) { return timeNow() >= timestamp; } - - function notReached(uint32 timestamp) internal view returns (bool) { - return !reached(timestamp); - } - - function till(uint32 timestamp) internal view returns (uint32) { - if (reached(timestamp)) { - return 0; - } - return timestamp - timeNow(); - } } diff --git a/contracts/SprinterLiquidityMining.sol b/contracts/SprinterLiquidityMining.sol index 0c96593..a9bf5cc 100644 --- a/contracts/SprinterLiquidityMining.sol +++ b/contracts/SprinterLiquidityMining.sol @@ -60,8 +60,8 @@ contract SprinterLiquidityMining is LiquidityMining { depositAndStake(scoreTo, amount, tierId); } - function unstakeAndWithdraw(address to) external { - uint256 shares = _unstake(_msgSender(), address(this)); + function unstakeAndWithdraw(uint256 id, address to) external { + uint256 shares = _unstake(_msgSender(), id, address(this)); IERC4626(address(LIQUIDITY_HUB)).redeem(shares, to, address(this)); } diff --git a/test/SprinterLiquidityMining.ts b/test/SprinterLiquidityMining.ts index a0f15d0..2a9f754 100644 --- a/test/SprinterLiquidityMining.ts +++ b/test/SprinterLiquidityMining.ts @@ -6,7 +6,7 @@ import hre from "hardhat"; import {Signature, resolveAddress, MaxUint256, getBigInt} from "ethers"; import { getCreateAddress, getContractAt, deploy, - ZERO_ADDRESS, ZERO_BYTES32, divCeil, + ZERO_ADDRESS, ZERO_BYTES32, } from "./helpers"; import { TestUSDC, SprinterUSDCLPShare, LiquidityHub, TransparentUpgradeableProxy, ProxyAdmin, @@ -86,7 +86,7 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.tiers(0)).to.eql([3n * MONTH, 100n]); expect(await liquidityMining.tiers(1)).to.eql([6n * MONTH, 150n]); expect(await liquidityMining.tiers(2)).to.eql([12n * MONTH, 200n]); - expect(await liquidityMining.stakes(user.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([]); await expect(liquidityHub.transfer(user.address, 1n)) .to.be.revertedWithCustomError(liquidityHub, "NotImplemented()"); @@ -131,8 +131,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP); expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 3n * MONTH, until, 100n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to stake by multiple users", async function () { @@ -192,8 +192,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP); expect(await liquidityMining.balanceOf(user2.address)).to.equal(20n * LP * 150n / 100n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(30n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([20n * LP, 6n * MONTH, until2, 150n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 3n * MONTH, until, 100n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([[20n * LP, 6n * MONTH, until2, 150n]]); }); it("Should allow to stake with a score to another address", async function () { @@ -230,8 +230,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(0n); expect(await liquidityMining.balanceOf(user2.address)).to.equal(10n * LP); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 3n * MONTH, until, 100n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to stake with a different tier", async function () { @@ -268,8 +268,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP * 150n / 100n); expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 6n * MONTH, until, 150n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 6n * MONTH, until, 150n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to stake with permit", async function () { @@ -281,7 +281,7 @@ describe("SprinterLiquidityMining", function () { await usdc.connect(deployer).transfer(user.address, 10n * USDC); await usdc.connect(user).approve(liquidityHub.target, 10n * USDC); await liquidityHub.connect(user).deposit(10n * USDC, user.address); - + const domain = { name: "Sprinter USDC LP Share", version: "1", @@ -306,7 +306,7 @@ describe("SprinterLiquidityMining", function () { nonce: 0n, deadline: 2000000000n, })); - + const tx = liquidityMining.connect(user).stakeWithPermit( user2.address, 10n * LP, @@ -360,7 +360,7 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user2.address)).to.equal(10n * LP); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - const tx = liquidityMining.connect(user).unstake(user.address); + const tx = liquidityMining.connect(user).unstake(0n, user.address); await expect(tx) .to.emit(lpToken, "Transfer") .withArgs(liquidityMining.target, user.address, 10n * LP); @@ -395,7 +395,7 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user2.address)).to.equal(10n * LP); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - const tx = liquidityMining.connect(user).unstake(user2.address); + const tx = liquidityMining.connect(user).unstake(0n, user2.address); await expect(tx) .to.emit(lpToken, "Transfer") .withArgs(liquidityMining.target, user2.address, 10n * LP); @@ -459,7 +459,7 @@ describe("SprinterLiquidityMining", function () { await usdc.connect(deployer).transfer(user.address, 10n * USDC); await usdc.connect(user).approve(liquidityHub.target, 10n * USDC); await liquidityHub.connect(user).deposit(10n * USDC, user.address); - + const domain = { name: "Sprinter USDC LP Share", version: "1", @@ -484,7 +484,7 @@ describe("SprinterLiquidityMining", function () { nonce: 0n, deadline: 2000000000n, })); - + await liquidityMining.connect(admin).disableMining(); await expect(liquidityMining.connect(user).stakeWithPermit( user2.address, @@ -518,7 +518,7 @@ describe("SprinterLiquidityMining", function () { expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); await liquidityMining.connect(admin).disableMining(); - const tx = liquidityMining.connect(user).unstake(user.address); + const tx = liquidityMining.connect(user).unstake(0n, user.address); await expect(tx) .to.emit(lpToken, "Transfer") .withArgs(liquidityMining.target, user.address, 10n * LP); @@ -545,12 +545,12 @@ describe("SprinterLiquidityMining", function () { await lpToken.connect(user).approve(liquidityMining.target, 10n * LP); await expect(liquidityMining.connect(user).stake(user.address, 0n, 0n)) - .to.be.revertedWithCustomError(liquidityMining, "InvalidAddedScore()"); + .to.be.revertedWithCustomError(liquidityMining, "ZeroAmount()"); }); - it("Should allow to restake by staking 0 amount", async function () { + it("Should not allow to restake by staking 0 amount", async function () { const { - lpToken, liquidityHub, usdc, user, user2, liquidityPool, USDC, LP, + lpToken, liquidityHub, usdc, user, USDC, LP, liquidityMining, deployer, } = await loadFixture(deployAll); @@ -561,31 +561,9 @@ describe("SprinterLiquidityMining", function () { await liquidityMining.connect(user).stake(user.address, 10n * LP, 0n); const extraSeconds = 100n; await time.setNextBlockTimestamp(await now() + extraSeconds); - const tx = liquidityMining.connect(user).stake(user.address, 0n, 0n); - await expect(tx) - .to.emit(liquidityMining, "Transfer") - .withArgs(ZERO_ADDRESS, user.address, 10n * LP * extraSeconds / (3n * MONTH)); - const until = await now() + 3n * MONTH; - await expect(tx) - .to.emit(liquidityMining, "StakeLocked") - .withArgs( - user.address, - user.address, - 0n, - 10n * LP, - until, - 10n * LP * extraSeconds / (3n * MONTH), - ); - expect(await lpToken.balanceOf(user.address)).to.equal(0n); - expect(await lpToken.balanceOf(liquidityMining.target)).to.equal(10n * LP); - expect(await liquidityMining.totalSupply()).to.equal(10n * LP * (3n * MONTH + extraSeconds) / (3n * MONTH)); - expect(await liquidityMining.balanceOf(user.address)).to.equal( - 10n * LP * (3n * MONTH + extraSeconds) / (3n * MONTH) - ); - expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); - expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + + await expect(liquidityMining.connect(user).stake(user.address, 0n, 0n)) + .to.be.revertedWithCustomError(liquidityMining, "ZeroAmount()"); }); it("Should allow to restake by staking positive amount", async function () { @@ -601,7 +579,7 @@ describe("SprinterLiquidityMining", function () { await liquidityMining.connect(user).stake(user.address, 10n * LP, 0n); const extraSeconds = 100n; await time.setNextBlockTimestamp(await now() + extraSeconds); - const addedScore = 10n * LP * extraSeconds / (3n * MONTH) + 1n * LP; + const addedScore = 1n * LP; const tx = liquidityMining.connect(user).stake(user.address, 1n * LP, 0n); await expect(tx) .to.emit(lpToken, "Transfer") @@ -616,7 +594,7 @@ describe("SprinterLiquidityMining", function () { user.address, user.address, 1n * LP, - 11n * LP, + 1n * LP, until, addedScore, ); @@ -626,13 +604,16 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP + addedScore); expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(11n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([11n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([ + [10n * LP, 3n * MONTH, until - extraSeconds, 100n], + [1n * LP, 3n * MONTH, until, 100n] + ]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); - it("Should allow to restake into longer tier with 0 amount", async function () { + it("Should not allow to restake into longer tier with 0 amount", async function () { const { - lpToken, liquidityHub, usdc, user, user2, liquidityPool, USDC, LP, + lpToken, liquidityHub, usdc, user, USDC, LP, liquidityMining, deployer, } = await loadFixture(deployAll); @@ -643,30 +624,9 @@ describe("SprinterLiquidityMining", function () { await liquidityMining.connect(user).stake(user.address, 10n * LP, 0n); const extraSeconds = 100n; await time.setNextBlockTimestamp(await now() + extraSeconds); - const tx = liquidityMining.connect(user).stake(user.address, 0n, 1n); - const addedScore = 10n * LP * 150n / 100n - divCeil(10n * LP * (3n * MONTH - extraSeconds), 3n * MONTH); - await expect(tx) - .to.emit(liquidityMining, "Transfer") - .withArgs(ZERO_ADDRESS, user.address, addedScore); - const until = await now() + 6n * MONTH; - await expect(tx) - .to.emit(liquidityMining, "StakeLocked") - .withArgs( - user.address, - user.address, - 0n, - 10n * LP, - until, - addedScore, - ); - expect(await lpToken.balanceOf(user.address)).to.equal(0n); - expect(await lpToken.balanceOf(liquidityMining.target)).to.equal(10n * LP); - expect(await liquidityMining.totalSupply()).to.equal(10n * LP + addedScore); - expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP + addedScore); - expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); - expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 6n * MONTH, until, 150n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + + await expect(liquidityMining.connect(user).stake(user.address, 0n, 1n)) + .to.be.revertedWithCustomError(liquidityMining, "ZeroAmount()"); }); it("Should allow to restake into longer tier with positive amount", async function () { @@ -683,7 +643,7 @@ describe("SprinterLiquidityMining", function () { const extraSeconds = 100n; await time.setNextBlockTimestamp(await now() + extraSeconds); const tx = liquidityMining.connect(user).stake(user.address, 1n * LP, 1n); - const addedScore = 11n * LP * 150n / 100n - divCeil(10n * LP * (3n * MONTH - extraSeconds), 3n * MONTH); + const addedScore = 1n * LP * 150n / 100n; await expect(tx) .to.emit(liquidityMining, "Transfer") .withArgs(ZERO_ADDRESS, user.address, addedScore); @@ -694,7 +654,7 @@ describe("SprinterLiquidityMining", function () { user.address, user.address, 1n * LP, - 11n * LP, + 1n * LP, until, addedScore, ); @@ -704,8 +664,11 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP + addedScore); expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(11n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([11n * LP, 6n * MONTH, until, 150n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([ + [10n * LP, 3n * MONTH, until - extraSeconds - 3n * MONTH, 100n], + [1n * LP, 6n * MONTH, until, 150n] + ]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to restake into shorter tier if remaining time is even shorter", async function () { @@ -722,7 +685,7 @@ describe("SprinterLiquidityMining", function () { const extraSeconds = 5n * MONTH; await time.setNextBlockTimestamp(await now() + extraSeconds); const tx = liquidityMining.connect(user).stake(user.address, 1n * LP, 0n); - const addedScore = 11n * LP - 10n * LP * (6n * MONTH - extraSeconds) * 150n / (100n * 6n * MONTH); + const addedScore = 1n * LP; await expect(tx) .to.emit(liquidityMining, "Transfer") .withArgs(ZERO_ADDRESS, user.address, addedScore); @@ -733,7 +696,7 @@ describe("SprinterLiquidityMining", function () { user.address, user.address, 1n * LP, - 11n * LP, + 1n * LP, until, addedScore, ); @@ -743,8 +706,11 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(10n * LP * 150n / 100n + addedScore); expect(await liquidityMining.balanceOf(user2.address)).to.equal(0n); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(11n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([11n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([ + [10n * LP, 6n * MONTH, until - 2n * MONTH, 150n], + [1n * LP, 3n * MONTH, until, 100n] + ]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to unstake after restaking", async function () { @@ -753,17 +719,18 @@ describe("SprinterLiquidityMining", function () { liquidityMining, deployer, } = await loadFixture(deployAll); - await usdc.connect(deployer).transfer(user.address, 10n * USDC); - await usdc.connect(user).approve(liquidityHub.target, 10n * USDC); - await liquidityHub.connect(user).deposit(10n * USDC, user.address); - await lpToken.connect(user).approve(liquidityMining.target, 10n * LP); + await usdc.connect(deployer).transfer(user.address, 11n * USDC); + await usdc.connect(user).approve(liquidityHub.target, 11n * USDC); + await liquidityHub.connect(user).deposit(11n * USDC, user.address); + await lpToken.connect(user).approve(liquidityMining.target, 11n * LP); await liquidityMining.connect(user).stake(user.address, 10n * LP, 0n); const extraSeconds = 100n; await time.setNextBlockTimestamp(await now() + extraSeconds); - await liquidityMining.connect(user).stake(user.address, 0n, 0n); + await liquidityMining.connect(user).stake(user.address, 1n * LP, 0n); + const until = await now() + 3n * MONTH; await time.increase(3n * MONTH); - const tx = liquidityMining.connect(user).unstake(user.address); + const tx = liquidityMining.connect(user).unstake(0n, user.address); await expect(tx) .to.emit(lpToken, "Transfer") .withArgs(liquidityMining.target, user.address, 10n * LP); @@ -771,58 +738,28 @@ describe("SprinterLiquidityMining", function () { .to.emit(liquidityMining, "StakeUnlocked") .withArgs(user.address, user.address, 10n * LP); expect(await lpToken.balanceOf(user.address)).to.equal(10n * LP); - expect(await lpToken.balanceOf(liquidityMining.target)).to.equal(0n); - expect(await liquidityMining.totalSupply()).to.equal(10n * LP * (3n * MONTH + extraSeconds) / (3n * MONTH)); - expect(await liquidityMining.balanceOf(user.address)).to.equal( - 10n * LP * (3n * MONTH + extraSeconds) / (3n * MONTH) - ); - expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([0n, 0n, 0n, 0n]); - }); - - it("Should not allow to decrease remaining staking period", async function () { - const { - lpToken, liquidityHub, usdc, user, USDC, LP, - liquidityMining, deployer, - } = await loadFixture(deployAll); - - await usdc.connect(deployer).transfer(user.address, 11n * USDC); - await usdc.connect(user).approve(liquidityHub.target, 11n * USDC); - await liquidityHub.connect(user).deposit(11n * USDC, user.address); - await lpToken.connect(user).approve(liquidityMining.target, 11n * LP); - await liquidityMining.connect(user).stake(user.address, 10n * LP, 1n); - - await expect(liquidityMining.connect(user).stake(user.address, 1n * LP, 0n)) - .to.be.revertedWithCustomError(liquidityMining, "DecreasingPeriod()"); - await expect(liquidityMining.connect(user).stake(user.address, 0n, 0n)) - .to.be.revertedWithCustomError(liquidityMining, "DecreasingPeriod()"); - }); - - it("Should not allow to restake with 0 or negative added score", async function () { - const { - lpToken, liquidityHub, usdc, user, USDC, LP, - deployer, admin, - } = await loadFixture(deployAll); - - const tiers = [ - {period: 3n * MONTH, multiplier: 100n}, - {period: 6n * MONTH, multiplier: 10n}, - ]; - - const liquidityMining = ( - await deploy("SprinterLiquidityMining", deployer, {}, admin.address, liquidityHub.target, tiers) - ) as SprinterLiquidityMining; - - await usdc.connect(deployer).transfer(user.address, 11n * USDC); - await usdc.connect(user).approve(liquidityHub.target, 11n * USDC); - await liquidityHub.connect(user).deposit(11n * USDC, user.address); - await lpToken.connect(user).approve(liquidityMining.target, 11n * LP); - await liquidityMining.connect(user).stake(user.address, 10n * LP, 0n); + expect(await lpToken.balanceOf(liquidityMining.target)).to.equal(1n * LP); + expect(await liquidityMining.totalSupply()).to.equal(11n * LP); + expect(await liquidityMining.balanceOf(user.address)).to.equal(11n * LP); + expect(await usdc.balanceOf(liquidityPool.target)).to.equal(11n * USDC); + expect(await liquidityMining.getStakes(user.address)).to.eql([ + [0n, 0n, 0n, 0n], + [1n * LP, 3n * MONTH, until, 100n] + ]); - await expect(liquidityMining.connect(user).stake(user.address, 1n * LP, 1n)) - .to.be.revertedWithCustomError(liquidityMining, "InvalidAddedScore()"); - await expect(liquidityMining.connect(user).stake(user.address, 0n, 1n)) - .to.be.revertedWithCustomError(liquidityMining, "InvalidAddedScore()"); + const tx2 = liquidityMining.connect(user).unstake(1n, user.address); + await expect(tx2) + .to.emit(lpToken, "Transfer") + .withArgs(liquidityMining.target, user.address, 1n * LP); + await expect(tx2) + .to.emit(liquidityMining, "StakeUnlocked") + .withArgs(user.address, user.address, 1n * LP); + expect(await lpToken.balanceOf(user.address)).to.equal(11n * LP); + expect(await lpToken.balanceOf(liquidityMining.target)).to.equal(0n); + expect(await liquidityMining.totalSupply()).to.equal(11n * LP); + expect(await liquidityMining.balanceOf(user.address)).to.equal(11n * LP); + expect(await usdc.balanceOf(liquidityPool.target)).to.equal(11n * USDC); + expect(await liquidityMining.getStakes(user.address)).to.eql([[0n, 0n, 0n, 0n], [0n, 0n, 0n, 0n]]); }); it("Should not allow to unstake too early", async function () { @@ -839,9 +776,9 @@ describe("SprinterLiquidityMining", function () { await time.increase(3n * MONTH - 10n); - await expect(liquidityMining.connect(user).unstake(user.address)) + await expect(liquidityMining.connect(user).unstake(0n, user.address)) .to.be.revertedWithCustomError(liquidityMining, "Locked()"); - await expect(liquidityMining.connect(user).unstake(user2.address)) + await expect(liquidityMining.connect(user).unstake(0n, user2.address)) .to.be.revertedWithCustomError(liquidityMining, "Locked()"); }); @@ -859,10 +796,10 @@ describe("SprinterLiquidityMining", function () { await time.increase(3n * MONTH + 1n); - await expect(liquidityMining.connect(user2).unstake(user2.address)) - .to.be.revertedWithCustomError(liquidityMining, "ZeroAmount()"); - await liquidityMining.connect(user).unstake(user.address); - await expect(liquidityMining.connect(user).unstake(user.address)) + await expect(liquidityMining.connect(user2).unstake(0n, user2.address)) + .to.be.reverted; + await liquidityMining.connect(user).unstake(0n, user.address); + await expect(liquidityMining.connect(user).unstake(0n, user.address)) .to.be.revertedWithCustomError(liquidityMining, "ZeroAmount()"); }); @@ -948,8 +885,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(0n); expect(await liquidityMining.balanceOf(user2.address)).to.equal(10n * LP); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 3n * MONTH, until, 100n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to deposit and stake with permit", async function () { @@ -1024,8 +961,8 @@ describe("SprinterLiquidityMining", function () { expect(await liquidityMining.balanceOf(user.address)).to.equal(0n); expect(await liquidityMining.balanceOf(user2.address)).to.equal(10n * LP); expect(await usdc.balanceOf(liquidityPool.target)).to.equal(10n * USDC); - expect(await liquidityMining.stakes(user.address)).to.eql([10n * LP, 3n * MONTH, until, 100n]); - expect(await liquidityMining.stakes(user2.address)).to.eql([0n, 0n, 0n, 0n]); + expect(await liquidityMining.getStakes(user.address)).to.eql([[10n * LP, 3n * MONTH, until, 100n]]); + expect(await liquidityMining.getStakes(user2.address)).to.eql([]); }); it("Should allow to unstake and withdraw", async function () { @@ -1042,7 +979,7 @@ describe("SprinterLiquidityMining", function () { await time.increase(3n * MONTH); - const tx = liquidityMining.connect(user).unstakeAndWithdraw(user2.address); + const tx = liquidityMining.connect(user).unstakeAndWithdraw(0n, user2.address); await expect(tx) .to.emit(lpToken, "Transfer") .withArgs(liquidityMining.target, ZERO_ADDRESS, 10n * LP);