Skip to content

Commit 2bb2eeb

Browse files
committed
rollover vault unit tests
1 parent d6c24e9 commit 2bb2eeb

File tree

5 files changed

+2572
-3
lines changed

5 files changed

+2572
-3
lines changed

spot-contracts/test/helpers.ts

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,83 @@ export const logReserveComposition = async (perp: Contract) => {
214214
export const checkReserveComposition = async (perp: Contract, tokens: Contract[], balances: BigNumber[] = []) => {
215215
const checkBalances = balances.length > 0;
216216
expect(await perp.callStatic.getReserveCount()).to.eq(tokens.length);
217+
218+
const tokenMap = {};
219+
const tokenBalanceMap = {};
217220
for (const i in tokens) {
218-
expect(await perp.callStatic.inReserve(tokens[i].address)).to.eq(true);
219-
expect(await perp.callStatic.getReserveAt(i)).to.eq(tokens[i].address);
221+
tokenMap[tokens[i].address] = true;
222+
if (checkBalances) {
223+
tokenBalanceMap[tokens[i].address] = balances[i];
224+
}
225+
}
226+
227+
const ERC20 = await ethers.getContractFactory("MockERC20");
228+
for (let j = 0; j < tokens.length; j++) {
229+
const reserveToken = ERC20.attach(await perp.callStatic.getReserveAt(j));
230+
expect(tokenMap[reserveToken.address]).to.eq(true);
220231
if (checkBalances) {
221-
expect(await tokens[i].balanceOf(perp.reserve())).to.eq(balances[i]);
232+
expect(await reserveToken.balanceOf(await perp.reserve())).to.eq(tokenBalanceMap[reserveToken.address]);
222233
}
223234
}
224235
await expect(perp.callStatic.getReserveAt(tokens.length)).to.be.reverted;
225236
};
237+
238+
export const getReserveTokens = async (perp: Contract) => {
239+
const ERC20 = await ethers.getContractFactory("MockERC20");
240+
const reserves: Contract[] = [];
241+
for (let i = 0; i < (await perp.callStatic.getReserveCount()); i++) {
242+
reserves.push(await ERC20.attach(await perp.callStatic.getReserveAt(i)));
243+
}
244+
return reserves;
245+
};
246+
247+
export const logVaultAssets = async (vault: Contract) => {
248+
const ERC20 = await ethers.getContractFactory("MockERC20");
249+
const deployedCount = (await vault.deployedCount()).toNumber();
250+
const earnedCount = (await vault.earnedCount()).toNumber();
251+
const count = 1 + deployedCount + earnedCount;
252+
console.log("Asset count", count);
253+
254+
const underlying = await ERC20.attach(await vault.underlying());
255+
console.log(
256+
0,
257+
underlying.address,
258+
utils.formatUnits(await vault.assetBalance(underlying.address), await underlying.decimals()),
259+
);
260+
for (let i = 0; i < deployedCount; i++) {
261+
const token = await ERC20.attach(await vault.deployedAt(i));
262+
console.log(
263+
i + 1,
264+
token.address,
265+
utils.formatUnits(await vault.assetBalance(token.address), await token.decimals()),
266+
);
267+
}
268+
for (let j = 0; j < earnedCount; j++) {
269+
const token = await ERC20.attach(await vault.earnedAt(j));
270+
console.log(
271+
j + 1 + deployedCount,
272+
token.address,
273+
utils.formatUnits(await vault.assetBalance(token.address), await token.decimals()),
274+
);
275+
}
276+
};
277+
278+
export const checkVaultAssetComposition = async (vault: Contract, tokens: Contract[], balances: BigNumber[] = []) => {
279+
expect(1 + (await vault.deployedCount()).toNumber() + (await vault.earnedCount()).toNumber()).to.eq(tokens.length);
280+
for (const i in tokens) {
281+
expect(await vault.assetBalance(tokens[i].address)).to.eq(balances[i]);
282+
}
283+
};
284+
285+
export const getVaultAssets = async (vault: Contract) => {
286+
const ERC20 = await ethers.getContractFactory("MockERC20");
287+
const assets: Contract[] = [];
288+
assets.push(await ERC20.attach(await vault.underlying()));
289+
for (let i = 0; i < (await vault.deployedCount()); i++) {
290+
assets.push(await ERC20.attach(await vault.deployedAt(i)));
291+
}
292+
for (let i = 0; i < (await vault.earnedCount()); i++) {
293+
assets.push(await ERC20.attach(await vault.earnedAt(i)));
294+
}
295+
return assets;
296+
};
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
import { expect, use } from "chai";
2+
import { network, ethers, upgrades } from "hardhat";
3+
import { Contract, Transaction, Signer } from "ethers";
4+
import {
5+
setupCollateralToken,
6+
mintCollteralToken,
7+
toFixedPtAmt,
8+
setupBondFactory,
9+
depositIntoBond,
10+
getTranches,
11+
toDiscountFixedPtAmt,
12+
toPriceFixedPtAmt,
13+
getDepositBond,
14+
advancePerpQueueToBondMaturity,
15+
} from "../helpers";
16+
import { smock, FakeContract } from "@defi-wonderland/smock";
17+
18+
use(smock.matchers);
19+
20+
let vault: Contract, perp: FakeContract, collateralToken: Contract, deployer: Signer, otherUser: Signer;
21+
describe("RolloverVault", function () {
22+
beforeEach(async function () {
23+
await network.provider.send("hardhat_reset");
24+
25+
const accounts = await ethers.getSigners();
26+
deployer = accounts[0];
27+
otherUser = accounts[1];
28+
29+
({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC"));
30+
await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer);
31+
32+
const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche");
33+
perp = await smock.fake(PerpetualTranche);
34+
35+
await perp.collateral.returns(collateralToken.address);
36+
await perp.feeToken.returns(perp.address);
37+
38+
const RolloverVault = await ethers.getContractFactory("RolloverVault");
39+
vault = await upgrades.deployProxy(RolloverVault.connect(deployer));
40+
await collateralToken.approve(vault.address, toFixedPtAmt("1"));
41+
await vault.init("RolloverVault", "VSHARE", collateralToken.address, perp.address);
42+
});
43+
44+
afterEach(async function () {
45+
await network.provider.send("hardhat_reset");
46+
});
47+
48+
describe("#init", function () {
49+
it("should set erc20 parameters", async function () {
50+
expect(await vault.name()).to.eq("RolloverVault");
51+
expect(await vault.symbol()).to.eq("VSHARE");
52+
expect(await vault.decimals()).to.eq(18);
53+
});
54+
55+
it("should set owner", async function () {
56+
expect(await vault.owner()).to.eq(await deployer.getAddress());
57+
});
58+
59+
it("should set ext service references", async function () {
60+
expect(await vault.perp()).to.eq(perp.address);
61+
});
62+
63+
it("should set deposit asset reference", async function () {
64+
expect(await vault.underlying()).to.eq(collateralToken.address);
65+
});
66+
67+
it("should initialize lists", async function () {
68+
expect(await vault.deployedCount()).to.eq(0);
69+
expect(await vault.earnedCount()).to.eq(1);
70+
expect(await vault.earnedAt(0)).to.eq(perp.address);
71+
await expect(vault.earnedAt(1)).to.be.revertedWith("OutOfBounds");
72+
expect(await vault.isVaultAsset(collateralToken.address)).to.eq(true);
73+
expect(await vault.isVaultAsset(perp.address)).to.eq(true);
74+
});
75+
76+
it("should NOT be paused", async function () {
77+
expect(await vault.paused()).to.eq(false);
78+
});
79+
});
80+
81+
describe("#pause", function () {
82+
let tx: Transaction;
83+
beforeEach(async function () {
84+
await vault.connect(deployer).transferOwnership(await otherUser.getAddress());
85+
});
86+
87+
describe("when triggered by non-owner", function () {
88+
it("should revert", async function () {
89+
await expect(vault.connect(deployer).pause()).to.be.revertedWith("Ownable: caller is not the owner");
90+
});
91+
});
92+
93+
describe("when already paused", function () {
94+
beforeEach(async function () {
95+
await vault.connect(otherUser).pause();
96+
});
97+
it("should revert", async function () {
98+
await expect(vault.connect(otherUser).pause()).to.be.revertedWith("Pausable: paused");
99+
});
100+
});
101+
102+
describe("when valid", function () {
103+
beforeEach(async function () {
104+
tx = await vault.connect(otherUser).pause();
105+
await tx;
106+
});
107+
it("should pause", async function () {
108+
expect(await vault.paused()).to.eq(true);
109+
});
110+
it("should emit event", async function () {
111+
await expect(tx)
112+
.to.emit(vault, "Paused")
113+
.withArgs(await otherUser.getAddress());
114+
});
115+
});
116+
});
117+
118+
describe("#unpause", function () {
119+
let tx: Transaction;
120+
beforeEach(async function () {
121+
await vault.connect(deployer).transferOwnership(await otherUser.getAddress());
122+
});
123+
124+
describe("when triggered by non-owner", function () {
125+
beforeEach(async function () {
126+
await vault.connect(otherUser).pause();
127+
});
128+
129+
it("should revert", async function () {
130+
await expect(vault.connect(deployer).unpause()).to.be.revertedWith("Ownable: caller is not the owner");
131+
});
132+
});
133+
134+
describe("when not paused", function () {
135+
it("should revert", async function () {
136+
await expect(vault.connect(otherUser).unpause()).to.be.revertedWith("Pausable: not paused");
137+
});
138+
});
139+
140+
describe("when valid", function () {
141+
beforeEach(async function () {
142+
tx = await vault.connect(otherUser).pause();
143+
await tx;
144+
tx = await vault.connect(otherUser).unpause();
145+
await tx;
146+
});
147+
it("should unpause", async function () {
148+
expect(await vault.paused()).to.eq(false);
149+
});
150+
it("should emit event", async function () {
151+
await expect(tx)
152+
.to.emit(vault, "Unpaused")
153+
.withArgs(await otherUser.getAddress());
154+
});
155+
});
156+
});
157+
158+
describe("#transferERC20", function () {
159+
let transferToken: Contract, toAddress: string;
160+
161+
beforeEach(async function () {
162+
const Token = await ethers.getContractFactory("MockERC20");
163+
transferToken = await Token.deploy();
164+
await transferToken.init("Mock Token", "MOCK");
165+
await transferToken.mint(vault.address, "100");
166+
toAddress = await deployer.getAddress();
167+
});
168+
169+
describe("when triggered by non-owner", function () {
170+
it("should revert", async function () {
171+
await expect(
172+
vault.connect(otherUser).transferERC20(transferToken.address, toAddress, "100"),
173+
).to.be.revertedWith("Ownable: caller is not the owner");
174+
});
175+
});
176+
177+
describe("when non vault asset", function () {
178+
it("should transfer", async function () {
179+
await expect(() => vault.transferERC20(transferToken.address, toAddress, "100")).to.changeTokenBalance(
180+
transferToken,
181+
deployer,
182+
"100",
183+
);
184+
});
185+
});
186+
187+
describe("when underlying asset", function () {
188+
it("should revert", async function () {
189+
await expect(vault.transferERC20(await vault.underlying(), toAddress, toFixedPtAmt("100"))).to.be.revertedWith(
190+
"UnauthorizedTransferOut",
191+
);
192+
});
193+
});
194+
195+
describe("when earned asset", function () {
196+
it("should revert", async function () {
197+
await expect(vault.transferERC20(await vault.earnedAt(0), toAddress, toFixedPtAmt("100"))).to.be.revertedWith(
198+
"UnauthorizedTransferOut",
199+
);
200+
});
201+
});
202+
203+
describe("when deployed asset", function () {
204+
beforeEach(async function () {
205+
const bondFactory = await setupBondFactory();
206+
({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC"));
207+
const BondIssuer = await ethers.getContractFactory("BondIssuer");
208+
const issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address);
209+
await issuer.init(4800, [200, 300, 500], 1200, 0);
210+
211+
const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy");
212+
const feeStrategy = await smock.fake(FeeStrategy);
213+
await feeStrategy.computeMintFees.returns(["0", "0"]);
214+
await feeStrategy.computeBurnFees.returns(["0", "0"]);
215+
await feeStrategy.computeRolloverFees.returns(["0", "0"]);
216+
217+
const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy");
218+
const pricingStrategy = await smock.fake(PricingStrategy);
219+
await pricingStrategy.decimals.returns(8);
220+
await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1"));
221+
await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1"));
222+
223+
const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy");
224+
const discountStrategy = await smock.fake(DiscountStrategy);
225+
await discountStrategy.decimals.returns(18);
226+
await discountStrategy.computeTrancheDiscount
227+
.whenCalledWith(collateralToken.address)
228+
.returns(toDiscountFixedPtAmt("1"));
229+
230+
const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche");
231+
perp = await upgrades.deployProxy(
232+
PerpetualTranche.connect(deployer),
233+
[
234+
"PerpetualTranche",
235+
"PERP",
236+
collateralToken.address,
237+
issuer.address,
238+
feeStrategy.address,
239+
pricingStrategy.address,
240+
discountStrategy.address,
241+
],
242+
{
243+
initializer: "init(string,string,address,address,address,address,address)",
244+
},
245+
);
246+
247+
await feeStrategy.feeToken.returns(perp.address);
248+
await perp.updateTolerableTrancheMaturity(1200, 4800);
249+
await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1"));
250+
await discountStrategy.computeTrancheDiscount.returns(toDiscountFixedPtAmt("1"));
251+
await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp));
252+
253+
const bond = await getDepositBond(perp);
254+
const tranches = await getTranches(bond);
255+
await depositIntoBond(bond, toFixedPtAmt("1000"), deployer);
256+
await tranches[0].approve(perp.address, toFixedPtAmt("200"));
257+
await perp.deposit(tranches[0].address, toFixedPtAmt("200"));
258+
await advancePerpQueueToBondMaturity(perp, bond);
259+
260+
await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer);
261+
const RolloverVault = await ethers.getContractFactory("RolloverVault");
262+
vault = await upgrades.deployProxy(RolloverVault.connect(deployer));
263+
await vault.init("RolloverVault", "VSHARE", collateralToken.address, perp.address);
264+
await collateralToken.transfer(vault.address, toFixedPtAmt("1000"));
265+
await vault.deploy();
266+
expect(await vault.deployedCount()).to.eq(2);
267+
});
268+
it("should revert", async function () {
269+
await expect(vault.transferERC20(await vault.deployedAt(0), toAddress, toFixedPtAmt("100"))).to.be.revertedWith(
270+
"UnauthorizedTransferOut",
271+
);
272+
await expect(vault.transferERC20(await vault.deployedAt(1), toAddress, toFixedPtAmt("100"))).to.be.revertedWith(
273+
"UnauthorizedTransferOut",
274+
);
275+
});
276+
});
277+
});
278+
});

0 commit comments

Comments
 (0)