Skip to content

Commit 8508edc

Browse files
adamchen0510dong77
authored andcommitted
[Protocol3] Add unit tests for UserStakingPool (#266) (#319)
* [Protocol3] modify the comment * [Protocol3] require protocolFeeVaultAddress not zero when claim * [Protocol3] Add unit tests for UserStakingPool (#266) * [Protocol3] Correct var return sequence * [Protocol3] Update total.claimedAt after user claim() Algorithm: Suppose UserA and UserB staked and UserA claim(), then should have: total.stake * (now - total.claimedAt) = userB.stake * (now - userB.claimedAt) ===> total.claimedAt = now - (userB.stake * (now - userB.claimedAt))/total.stake ===> total.claimedAt = now - (prevTotalPoints - prevUserAPoints)/total.stake * [Protocol3] Add multi users staking test * Update UserStakingPool.sol * [Protocol3] Add TRANSFER_FAILURE test * [Protocol3] Update multi users staking test User A stake first, User B stake after 30 days, User C stake after 30days again. Then skip 90 days to claim.
1 parent 82896de commit 8508edc

File tree

7 files changed

+263
-6
lines changed

7 files changed

+263
-6
lines changed

packages/loopring_v3/contracts/iface/IProtocolFeeVault.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pragma solidity 0.5.10;
2424
/// and Ether to sell them for LRC by other means such as using a centralized
2525
/// exchange. This option will be disabled once Oedax is production ready.
2626
/// For LRC token, 70% of them can be withdrawn to the UserStakingPool contract
27-
/// to reward LRC stakers; 15% of them can be withdrawn to the Loopring DAO,
28-
/// and the remaining 15% can be burned to reduce LRC's total supply.
27+
/// to reward LRC stakers; 20% of them can be withdrawn to the Loopring DAO,
28+
/// and the remaining 10% can be burned to reduce LRC's total supply.
2929
contract IProtocolFeeVault
3030
{
3131
uint public constant REWARD_PERCENTAGE = 70;

packages/loopring_v3/contracts/impl/ProtocolFeeVault.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ contract IOedax {
3636
/// @param P Numerator part of the target price `p`.
3737
/// @param S Price precision -- (_P / 10**_S) is the float value of the target price.
3838
/// @param M Price factor. `p * M` is the maximum price and `p / M` is the minimum price.
39-
/// @param T1 The maximum auction duration in second.
39+
/// @param T1 The minimum auction duration in second.
4040
/// @param T2 The maximum auction duration in second.
4141
/// @return auctionAddr Auction address.
4242
function createAuction(

packages/loopring_v3/contracts/impl/UserStakingPool.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ contract UserStakingPool is IUserStakingPool, Claimable
168168
returns (uint claimedAmount)
169169
{
170170
require(userClaimWaitTime(msg.sender) == 0, "NEED_TO_WAIT");
171+
require(protocolFeeVaultAddress != address(0), "ZERO_ADDRESS");
171172

172173
uint totalPoints;
173174
uint userPoints;
@@ -178,7 +179,11 @@ contract UserStakingPool is IUserStakingPool, Claimable
178179

179180
total.stake = total.stake.add(claimedAmount);
180181
total.claimedReward = total.claimedReward.add(claimedAmount);
181-
total.claimedAt = totalPoints.sub(userPoints) / total.stake;
182+
if (totalPoints >= userPoints) {
183+
total.claimedAt = now.sub(totalPoints.sub(userPoints) / total.stake);
184+
} else {
185+
total.claimedAt = now;
186+
}
182187

183188
Stake storage user = users[msg.sender];
184189
user.stake = user.stake.add(claimedAmount);
@@ -212,8 +217,8 @@ contract UserStakingPool is IUserStakingPool, Claimable
212217
private
213218
view
214219
returns (
215-
uint userPoints,
216220
uint totalPoints,
221+
uint userPoints,
217222
uint outstandindReward
218223
)
219224
{

packages/loopring_v3/migrations/4_deploy_protocol.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var ExchangeDeposits = artifacts.require("./impl/libexchange/ExchangeDeposits");
1212
var ExchangeGenesis = artifacts.require("./impl/libexchange/ExchangeGenesis");
1313
var ExchangeTokens = artifacts.require("./impl/libexchange/ExchangeTokens");
1414
var ExchangeWithdrawals = artifacts.require("./impl/libexchange/ExchangeWithdrawals");
15+
var UserStakingPool = artifacts.require("./impl/UserStakingPool");
16+
var ProtocolFeeVault = artifacts.require("./impl/ProtocolFeeVault");
1517

1618
module.exports = function(deployer, network, accounts) {
1719
if (network === "live") {
@@ -88,6 +90,18 @@ module.exports = function(deployer, network, accounts) {
8890
0,
8991
),
9092
]);
93+
}).then(() => {
94+
return Promise.all([
95+
deployer.deploy(UserStakingPool, LRCToken.address)
96+
]);
97+
}).then(() => {
98+
return Promise.all([
99+
deployer.deploy(
100+
ProtocolFeeVault,
101+
LRCToken.address,
102+
UserStakingPool.address
103+
)
104+
]);
91105
}).then(() => {
92106
console.log("Deployed contracts addresses:");
93107
console.log("LoopringV3:", LoopringV3.address);

packages/loopring_v3/test/testExchangeUtil.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export class ExchangeTestUtil {
6969
public operator: any;
7070
public activeOperator: number;
7171

72+
public userstakingpool: any;
73+
public protocolfeevault: any;
74+
7275
public ringMatcherAccountID: number[] = [];
7376

7477
public accounts: Account[][] = [];
@@ -2191,6 +2194,11 @@ export class ExchangeTestUtil {
21912194
await Token.approve(contractAddress, amount, {from: owner});
21922195
}
21932196

2197+
public async transferBalance(to: string, token: string, amount: BN) {
2198+
const Token = await this.getTokenContract(token);
2199+
await Token.transfer(to, amount, { from: this.testContext.deployer });
2200+
}
2201+
21942202
public evmIncreaseTime(seconds: number) {
21952203
return new Promise((resolve, reject) => {
21962204
web3.currentProvider.send({
@@ -2734,6 +2742,14 @@ export class ExchangeTestUtil {
27342742
this.contracts.WETHToken.deployed(),
27352743
]);
27362744

2745+
const [userstakingpool, protocolfeevault] = await Promise.all([
2746+
this.contracts.UserStakingPool.deployed(),
2747+
this.contracts.ProtocolFeeVault.deployed()
2748+
]);
2749+
2750+
this.userstakingpool = userstakingpool;
2751+
this.protocolfeevault = protocolfeevault;
2752+
27372753
this.lzDecompressor = await this.contracts.LzDecompressor.new();
27382754

27392755
this.loopringV3 = loopringV3;
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import { expectThrow } from "./expectThrow";
2+
import BN = require("bn.js");
3+
import { ExchangeTestUtil } from "./testExchangeUtil";
4+
5+
contract("UserStakingPool", (accounts: string[]) => {
6+
const owner1 = accounts[0];
7+
const owner2 = accounts[1];
8+
const owner3 = accounts[2];
9+
const emptyAddr = "0x0000000000000000000000000000000000000000";
10+
11+
let userstaking: any;
12+
let protocolfee: any;
13+
let exchangeTestUtil: ExchangeTestUtil;
14+
15+
before(async () => {
16+
exchangeTestUtil = new ExchangeTestUtil();
17+
await exchangeTestUtil.initialize(accounts);
18+
userstaking = exchangeTestUtil.userstakingpool;
19+
protocolfee = exchangeTestUtil.protocolfeevault;
20+
userstaking.setProtocolFeeVault(protocolfee.address);
21+
});
22+
23+
describe("stakeA", () => {
24+
it("should not stake if dont have any LRC", async () => {
25+
await expectThrow(
26+
userstaking.stake(500, { from: owner1 }),
27+
"TRANSFER_FAILURE"
28+
);
29+
});
30+
it("should not withdraw if haven't staked", async () => {
31+
await expectThrow(
32+
userstaking.withdraw(500, { from: owner1 }),
33+
"INSUFFICIENT_FUND"
34+
);
35+
});
36+
it("set User A balance as 1000", async () => {
37+
//const amount = new BN(web3.utils.toWei("1000", "ether"));
38+
const amount = new BN(1000);
39+
await exchangeTestUtil.setBalanceAndApprove(
40+
owner1,
41+
"LRC",
42+
amount,
43+
userstaking.address
44+
);
45+
});
46+
it("should get user staking equal to staked", async () => {
47+
await userstaking.stake(1000, { from: owner1 });
48+
const userstaked = await userstaking.getUserStaking(owner1);
49+
assert.equal(
50+
userstaked.stakeAmount,
51+
1000,
52+
"User staking should equal to expected"
53+
);
54+
});
55+
it("should get total staking equal to staked", async () => {
56+
const userstaked = await userstaking.getTotalStaking();
57+
assert.equal(userstaked, 1000, "Total staking should equal to expected");
58+
});
59+
it("should NEED_TO_WAIT when withdraw", async () => {
60+
await expectThrow(
61+
userstaking.withdraw(500, { from: owner1 }),
62+
"NEED_TO_WAIT"
63+
);
64+
});
65+
it("advance block timestampe after MIN_WITHDRAW_DELAY", async () => {
66+
await exchangeTestUtil.advanceBlockTimestamp(90 * 24 * 60 * 60);
67+
});
68+
it("set protocolfee as 100", async () => {
69+
const amount = new BN(100);
70+
await exchangeTestUtil.transferBalance(
71+
protocolfee.address,
72+
"LRC",
73+
amount
74+
);
75+
});
76+
it("should get claimed equal as expected", async () => {
77+
const userclaimed = await userstaking.claim({ from: owner1 });
78+
const eventArr: any = await exchangeTestUtil.getEventsFromContract(
79+
userstaking,
80+
"LRCRewarded",
81+
web3.eth.blockNumber
82+
);
83+
const items = eventArr.map((eventObj: any) => {
84+
const reward = eventObj.args.amount;
85+
// 70% as staking reward
86+
assert.equal(reward, 70, "User claimed should equal to expected");
87+
});
88+
});
89+
it("should get withdrawed equal as expected", async () => {
90+
await userstaking.withdraw(0, { from: owner1 });
91+
const eventArr: any = await exchangeTestUtil.getEventsFromContract(
92+
userstaking,
93+
"LRCWithdrawn",
94+
web3.eth.blockNumber
95+
);
96+
const items = eventArr.map((eventObj: any) => {
97+
const withdraw = eventObj.args.amount;
98+
assert.equal(withdraw, 1070, "User withdraw should equal to expected");
99+
});
100+
});
101+
});
102+
103+
describe("stakeA&B&C", () => {
104+
it("set User A balance as 1000", async () => {
105+
//const amount = new BN(web3.utils.toWei("1000", "ether"));
106+
const amount = new BN(1000);
107+
await exchangeTestUtil.setBalanceAndApprove(
108+
owner1,
109+
"LRC",
110+
amount,
111+
userstaking.address
112+
);
113+
});
114+
it("set User B balance as 1000", async () => {
115+
const amount = new BN(1000);
116+
await exchangeTestUtil.setBalanceAndApprove(
117+
owner2,
118+
"LRC",
119+
amount,
120+
userstaking.address
121+
);
122+
});
123+
it("set User C balance as 1000", async () => {
124+
const amount = new BN(1000);
125+
await exchangeTestUtil.setBalanceAndApprove(
126+
owner3,
127+
"LRC",
128+
amount,
129+
userstaking.address
130+
);
131+
});
132+
it("should get user A staking equal to staked", async () => {
133+
await userstaking.stake(1000, { from: owner1 });
134+
const userstaked = await userstaking.getUserStaking(owner1);
135+
assert.equal(
136+
userstaked.stakeAmount,
137+
1000,
138+
"User staking should equal to expected"
139+
);
140+
});
141+
it("advance block timestampe after 30 days", async () => {
142+
await exchangeTestUtil.advanceBlockTimestamp(30 * 24 * 60 * 60);
143+
});
144+
it("should get user B staking equal to staked", async () => {
145+
await userstaking.stake(1000, { from: owner2 });
146+
const userstaked = await userstaking.getUserStaking(owner2);
147+
assert.equal(
148+
userstaked.stakeAmount,
149+
1000,
150+
"User staking should equal to expected"
151+
);
152+
});
153+
it("advance block timestampe after 30 days", async () => {
154+
await exchangeTestUtil.advanceBlockTimestamp(30 * 24 * 60 * 60);
155+
});
156+
it("should get user C staking equal to staked", async () => {
157+
await userstaking.stake(1000, { from: owner3 });
158+
const userstaked = await userstaking.getUserStaking(owner3);
159+
assert.equal(
160+
userstaked.stakeAmount,
161+
1000,
162+
"User staking should equal to expected"
163+
);
164+
});
165+
it("advance block timestampe after MIN_WITHDRAW_DELAY: 90 days", async () => {
166+
await exchangeTestUtil.advanceBlockTimestamp(90 * 24 * 60 * 60);
167+
});
168+
it("set protocolfee as 120", async () => {
169+
const amount = new BN(120);
170+
await exchangeTestUtil.transferBalance(
171+
protocolfee.address,
172+
"LRC",
173+
amount
174+
);
175+
});
176+
it("should get user A claimed equal as expected", async () => {
177+
const userclaimed = await userstaking.claim({ from: owner1 });
178+
const eventArr: any = await exchangeTestUtil.getEventsFromContract(
179+
userstaking,
180+
"LRCRewarded",
181+
web3.eth.blockNumber
182+
);
183+
const items = eventArr.map((eventObj: any) => {
184+
const reward = eventObj.args.amount;
185+
// 120 * 70% * (180 / (180 + 120 + 90)) == 35 as staking reward
186+
// as A's staking time is 30 + 30 + 90 days, B's staking time is 30 + 90 days and C's staking time is 90 days
187+
188+
// 34 for round down when div
189+
assert(reward == 35 || reward == 34, "User claimed should equal to expected");
190+
});
191+
});
192+
it("should get user B claimed equal as expected", async () => {
193+
const userclaimed = await userstaking.claim({ from: owner2 });
194+
const eventArr: any = await exchangeTestUtil.getEventsFromContract(
195+
userstaking,
196+
"LRCRewarded",
197+
web3.eth.blockNumber
198+
);
199+
const items = eventArr.map((eventObj: any) => {
200+
const reward = eventObj.args.amount;
201+
// 120 * 70% * (180 / (180 + 120 + 90)) == 28 as staking reward
202+
assert(reward == 27 || reward == 28 || reward == 29, "User claimed should equal to expected");
203+
});
204+
});
205+
it("should get user C claimed equal as expected", async () => {
206+
const userclaimed = await userstaking.claim({ from: owner3 });
207+
const eventArr: any = await exchangeTestUtil.getEventsFromContract(
208+
userstaking,
209+
"LRCRewarded",
210+
web3.eth.blockNumber
211+
);
212+
const items = eventArr.map((eventObj: any) => {
213+
const reward = eventObj.args.amount;
214+
// 120 * 70% * (180 / (180 + 120 + 90)) == 21 as staking reward
215+
assert(reward == 20 || reward == 21 || reward == 22, "User claimed should equal to expected");
216+
});
217+
});
218+
});
219+
});

packages/loopring_v3/util/Artifacts.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
export class Artifacts {
32
public LoopringV3: any;
43
public Exchange: any;
@@ -18,6 +17,8 @@ export class Artifacts {
1817
public LzDecompressor: any;
1918
public TransferContract: any;
2019
public PoseidonContract: any;
20+
public UserStakingPool: any;
21+
public ProtocolFeeVault: any;
2122
constructor(artifacts: any) {
2223
this.LoopringV3 = artifacts.require("impl/LoopringV3");
2324
this.Exchange = artifacts.require("impl/Exchange");
@@ -37,5 +38,7 @@ export class Artifacts {
3738
this.LzDecompressor = artifacts.require("test/LzDecompressor");
3839
this.TransferContract = artifacts.require("test/TransferContract");
3940
this.PoseidonContract = artifacts.require("test/PoseidonContract");
41+
this.UserStakingPool = artifacts.require("impl/UserStakingPool");
42+
this.ProtocolFeeVault = artifacts.require("impl/ProtocolFeeVault");
4043
}
4144
}

0 commit comments

Comments
 (0)