-
Notifications
You must be signed in to change notification settings - Fork 7
/
AcceleratingDistributor.Admin.ts
273 lines (243 loc) · 11 KB
/
AcceleratingDistributor.Admin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
import { expect, ethers, Contract, SignerWithAddress, toWei, getContractFactory } from "./utils";
import { acceleratingDistributorFixture, enableTokenForStaking } from "./AcceleratingDistributor.Fixture";
import { baseEmissionRate, maxMultiplier, secondsToMaxMultiplier } from "./constants";
let timer: Contract, acrossToken: Contract, distributor: Contract, lpToken1: Contract;
let owner: SignerWithAddress, rando: SignerWithAddress;
const zeroAddress = ethers.constants.AddressZero;
describe("AcceleratingDistributor: Admin Functions", async function () {
beforeEach(async function () {
[owner, rando] = await ethers.getSigners();
({ timer, distributor, acrossToken, lpToken1 } = await acceleratingDistributorFixture());
});
it("Enable token for staking", async function () {
expect((await distributor.stakingTokens(lpToken1.address)).enabled).to.be.false;
await distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
expect((await distributor.stakingTokens(lpToken1.address)).enabled).to.be.true;
expect((await distributor.stakingTokens(lpToken1.address)).baseEmissionRate).to.equal(baseEmissionRate);
expect((await distributor.stakingTokens(lpToken1.address)).maxMultiplier).to.equal(maxMultiplier);
expect((await distributor.stakingTokens(lpToken1.address)).secondsToMaxMultiplier).to.equal(secondsToMaxMultiplier);
expect((await distributor.stakingTokens(lpToken1.address)).lastUpdateTime).to.equal(await timer.getCurrentTime());
// Update settings.
const newMultiplier = maxMultiplier.add(toWei(1));
await distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
newMultiplier,
secondsToMaxMultiplier
);
expect((await distributor.stakingTokens(lpToken1.address)).maxMultiplier).to.equal(newMultiplier);
//Disable token for staking.
await distributor.configureStakingToken(
lpToken1.address,
false,
baseEmissionRate,
newMultiplier,
secondsToMaxMultiplier
);
expect((await distributor.stakingTokens(lpToken1.address)).enabled).to.be.false;
});
it("Can only recover excess staked tokens", async function () {
await distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
// Drop tokens directly onto the contract. The owner should be able to fully recover these.
await lpToken1.mint(distributor.address, toWei(420));
await expect(() => distributor.recoverToken(lpToken1.address)).to.changeTokenBalances(
lpToken1,
[distributor, owner],
[toWei(420).mul(-1), toWei(420)]
);
// Stake tokens. Should not be able to recover as no excess above what that contract thinks it should have.
await lpToken1.mint(rando.address, toWei(69));
await lpToken1.connect(rando).approve(distributor.address, toWei(69));
await distributor.connect(rando).stake(lpToken1.address, toWei(69));
await expect(distributor.recoverToken(lpToken1.address)).to.be.revertedWith("Can't recover 0 tokens");
// Mint additional tokens to the contract to simulate someone dropping them accidentally. This should be recoverable.
await lpToken1.mint(distributor.address, toWei(696));
await expect(() => distributor.recoverToken(lpToken1.address)).to.changeTokenBalances(
lpToken1,
[distributor, owner],
[toWei(696).mul(-1), toWei(696)]
);
// The contract should be left with the original stake amount in it as this was not recoverable.
expect(await lpToken1.balanceOf(distributor.address)).to.equal(toWei(69));
await expect(distributor.recoverToken(lpToken1.address)).to.be.revertedWith("Can't recover 0 tokens");
});
it("Can skim any amount of a random token", async function () {
const randomToken = await (await getContractFactory("TestToken", owner)).deploy("RANDO", "RANDO");
const amount = toWei(420);
await randomToken.mint(distributor.address, amount);
await distributor.recoverToken(randomToken.address);
expect(await randomToken.balanceOf(distributor.address)).to.equal(toWei(0));
await expect(distributor.recoverToken(randomToken.address)).to.be.revertedWith("Can't recover 0 tokens");
});
it("Owner can at any time recover reward token", async function () {
await acrossToken.mint(distributor.address, toWei(420));
// Owner can recover tokens at any point in time.
await expect(() => distributor.recoverToken(acrossToken.address)).to.changeTokenBalances(
acrossToken,
[distributor, owner],
[toWei(420).mul(-1), toWei(420)]
);
});
it("Cannot set staking token to reward token", async function () {
await expect(
distributor.configureStakingToken(
acrossToken.address,
true,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
)
).to.be.revertedWith("Staked token is reward token");
});
it("Cannot set bad staking configs", async function () {
await expect(
distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
toWei(10000000),
secondsToMaxMultiplier
)
).to.be.revertedWith("maxMultiplier too large");
await expect(
distributor.configureStakingToken(lpToken1.address, true, baseEmissionRate, maxMultiplier, 0)
).to.be.revertedWith("secondsToMaxMultiplier is 0");
await expect(
distributor.configureStakingToken(
lpToken1.address,
true,
toWei(10000000000),
maxMultiplier,
secondsToMaxMultiplier
)
).to.be.revertedWith("baseEmissionRate too large");
await expect(
distributor.configureStakingToken(lpToken1.address, true, baseEmissionRate, toWei(1), secondsToMaxMultiplier)
).to.not.be.reverted;
await expect(
distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
toWei(".999999999999999999"),
secondsToMaxMultiplier
)
).to.be.revertedWith("maxMultiplier less than 1e18");
});
it("Non owner cant execute admin functions", async function () {
await expect(distributor.connect(rando).configureStakingToken(lpToken1.address, true, 4, 2, 0)).to.be.revertedWith(
"Ownable: caller is not the owner"
);
});
it("Permissioning on staking-related methods", async function () {
await lpToken1.mint(owner.address, toWei(69));
await lpToken1.connect(owner).approve(distributor.address, toWei(69));
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(1))).to.be.revertedWith(
"stakedToken not enabled"
);
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.be.revertedWith(
"stakedToken not initialized"
);
await expect(distributor.connect(owner).withdrawReward(lpToken1.address)).to.be.revertedWith(
"stakedToken not initialized"
);
await expect(distributor.connect(owner).exit(lpToken1.address)).to.be.revertedWith("stakedToken not initialized");
await distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(2))).to.not.be.reverted;
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.not.be.reverted;
await expect(distributor.connect(owner).withdrawReward(lpToken1.address)).to.not.be.reverted;
await expect(distributor.connect(owner).exit(lpToken1.address)).to.not.be.reverted;
// Balance => non-zero before disabling, to verify that unstake/withdraw/exit is still possible.
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(2))).to.not.be.reverted;
await distributor.configureStakingToken(
lpToken1.address,
false,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(1))).to.be.revertedWith(
"stakedToken not enabled"
);
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.not.be.reverted;
await expect(distributor.connect(owner).withdrawReward(lpToken1.address)).to.not.be.reverted;
await expect(distributor.connect(owner).exit(lpToken1.address)).to.not.be.reverted;
});
it("Input validation on staking-related methods", async function () {
await lpToken1.mint(owner.address, toWei(69));
await lpToken1.connect(owner).approve(distributor.address, toWei(69));
// Modifiers take precedence when staking is disabled.
for (const stakingEnabled of [true, false]) {
await distributor.configureStakingToken(
lpToken1.address,
stakingEnabled,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(0))).to.be.revertedWith(
stakingEnabled ? "Invalid amount" : "stakedToken not enabled"
);
await expect(distributor.connect(owner).stakeFor(lpToken1.address, toWei(0), zeroAddress)).to.be.revertedWith(
stakingEnabled ? "Invalid beneficiary" : "stakedToken not enabled"
);
await expect(distributor.connect(owner).stakeFor(lpToken1.address, toWei(1), zeroAddress)).to.be.revertedWith(
stakingEnabled ? "Invalid beneficiary" : "stakedToken not enabled"
);
if (stakingEnabled) {
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(1))).to.not.be.reverted;
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(0))).to.be.revertedWith(
"Invalid amount"
);
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.not.be.reverted;
} else {
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(1))).to.be.revertedWith(
"stakedToken not enabled"
);
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(0))).to.be.revertedWith(
"Invalid amount"
);
// Staked balance is 0.
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.be.reverted;
}
}
// Validate withdrawal guards when staking is disabled.
await distributor.configureStakingToken(
lpToken1.address,
true,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
await expect(distributor.connect(owner).stake(lpToken1.address, toWei(2))).to.not.be.reverted;
await distributor.configureStakingToken(
lpToken1.address,
false,
baseEmissionRate,
maxMultiplier,
secondsToMaxMultiplier
);
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(0))).to.be.revertedWith("Invalid amount");
await expect(distributor.connect(owner).unstake(lpToken1.address, toWei(1))).to.not.be.reverted;
await expect(distributor.connect(owner).exit(lpToken1.address)).to.not.be.reverted;
});
});