-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathTestCFD.spec.ts
322 lines (270 loc) · 12 KB
/
TestCFD.spec.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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/// <reference path="../types/interfaces.d.ts" />
/// <reference path="../types/chai.d.ts" />
import {
CFDContract,
CFDInstance,
UpSideDaiContract,
MakerMedianizerMockContract,
DAITokenMockContract,
DAITokenMockInstance,
IUniswapExchangeContract,
MakerMedianizerMockInstance,
IUniswapFactoryInstance,
IUniswapFactoryContract,
IUniswapExchangeInstance,
UpSideDaiInstance,
UpDaiInstance,
UpDaiContract,
DownDaiInstance,
DownDaiContract
} from "./../types/generated/index.d";
import * as chai from "chai";
import chaiBN from "chai-bn";
import { ether, BN } from "openzeppelin-test-helpers";
import truffleAssert from "truffle-assertions";
chai.use(chaiBN(BN));
const { assert, expect } = chai;
// const BigNumber = require("bignumber.js");
// const EVMRevert = require('./helpers/EVMRevert').EVMRevert;
// const increaseTime = require('./helpers/increaseTime');
// const increaseTimeTo = increaseTime.increaseTimeTo;
const ZERO_ADDR = "0x0000000000000000000000000000000000000000";
const DAITokenMock: DAITokenMockContract = artifacts.require("DAITokenMock");
const MakerMedianizerMock: MakerMedianizerMockContract = artifacts.require(
"MakerMedianizerMock"
);
const IUniswapExchange: IUniswapExchangeContract = artifacts.require(
"IUniswapExchange"
);
const IUniswapFactory: IUniswapFactoryContract = artifacts.require(
"IUniswapFactory"
);
const UpSideDai: UpSideDaiContract = artifacts.require("UpSideDai");
const CFD: CFDContract = artifacts.require("CFD");
const UpDaiContract: UpDaiContract = artifacts.require("UpDai");
const DownDaiContract: DownDaiContract = artifacts.require("DownDai");
contract("CFD", ([provider1, provider2, provider3, trader1, trader2, trader3, random]) => {
const daiAmountMint = ether("100");
const daiAmountDeposit = ether("50");
const daiAmountToSellForUpDai = ether("5");
const daiAmountToSellForDownDai = ether("5");
const oneMonthInSeconds = 60 * 60 * 24 * 30;
const now = new Date().getTime() / 1000;
let dai: DAITokenMockInstance;
let upSideDai: UpSideDaiInstance;
let cfd: CFDInstance;
let makerMedianizer: MakerMedianizerMockInstance;
let uniswapFactory: IUniswapFactoryInstance;
let upDai: UpDaiInstance;
let downDai: DownDaiInstance;
before(async () => {
dai = await DAITokenMock.deployed();
upSideDai = await UpSideDai.deployed();
cfd = await CFD.at(await upSideDai.deployedCFD(1));
uniswapFactory = await IUniswapFactory.at(await cfd.uniswapFactory());
makerMedianizer = await MakerMedianizerMock.at(await cfd.makerMedianizer());
upDai = await UpDaiContract.at(await cfd.upDai());
downDai = await DownDaiContract.at(await cfd.downDai());
dai.mint(provider1, daiAmountMint);
dai.mint(provider2, daiAmountMint);
dai.mint(provider3, daiAmountMint);
dai.mint(trader1, daiAmountMint);
dai.mint(trader2, daiAmountMint);
dai.mint(trader3, daiAmountMint);
dai.mint(random, daiAmountMint);
});
describe("CFD deployment", async () => {
it("check deployment params", async () => {
expect(await upDai.totalSupply()).bignumber.eq(
new BN((0).toString()),
"upDai total supply mismatcsh"
);
expect(await downDai.totalSupply()).bignumber.eq(
new BN((0).toString()),
"downDai total supply mismatch"
);
});
});
describe("Uniswap pools", async () => {
it("get required ETH for up&down pool", async () => {
let _individualDeposits = daiAmountDeposit.div(new BN(2));
let _ethUSDPrice = new BN(await cfd.GetETHUSDPriceFromMedianizer());
let _daiPriceUsd = await cfd.GetDaiPriceUSD();
let upDaiRate;
let downDaiRate;
let _totalUpDaiValue;
let _totalDownDaiValue;
let _upDaiPoolEth;
let _downDaiPoolEth;
let tx1 = await cfd.getCurrentDaiRates(_daiPriceUsd); // To improve test; mock this function math
truffleAssert.eventEmitted(tx1, "UpDownDaiRates", ev => {
upDaiRate = ev.upDaiRate;
downDaiRate = ev.downDaiRate;
return ev;
});
_totalUpDaiValue = upDaiRate.mul(_individualDeposits).div(new BN(10).pow(new BN(18)));
_totalDownDaiValue = downDaiRate.mul(_individualDeposits).div(new BN(10).pow(new BN(18)));
_upDaiPoolEth = _totalUpDaiValue.mul(new BN(10).pow(new BN(18))).div(_ethUSDPrice);
_downDaiPoolEth = _totalDownDaiValue.mul(new BN(10).pow(new BN(18))).div(_ethUSDPrice);
let tx2 = await cfd.getETHCollateralRequirements(daiAmountDeposit);
truffleAssert.eventEmitted(tx2, "NeededEthCollateral", ev => {
expect(_upDaiPoolEth).bignumber.eq(
ev.upDaiPoolEth,
"expected needed ETH collateral for UPDAI pool mismatch"
);
expect(_downDaiPoolEth).bignumber.eq(
ev.downDaiPoolEth,
"expected needed ETH collateral for DOWNDAI pool mismatch"
);
return ev;
});
});
describe("DAI price oracle", async () => {
it("should return relative price", async () => {
let ethUSDPrice = new BN(await cfd.GetETHUSDPriceFromMedianizer());
let daiExchange: IUniswapExchangeInstance = await IUniswapExchange.at(
await uniswapFactory.getExchange(dai.address)
);
let ethDAIPriceSimple = await daiExchange.getEthToTokenInputPrice(
(1000000).toString()
);
let ethDAPriceExact = ethDAIPriceSimple.mul(new BN(10 ** 12));
let expectedPrice = ethUSDPrice
.mul(new BN(10).pow(new BN(18)))
.div(ethDAPriceExact);
const onChainPrice = await cfd.GetDaiPriceUSD();
expect(onChainPrice).bignumber.eq(
expectedPrice,
"expected DAI price mismatch"
);
});
});
describe("Market maker", async () => {
it("deposit liquidity into CFD", async () => {
let cfdUpDaiBalanceBefore = await upDai.balanceOf(cfd.address);
let cfdDownDaiBalanceBefore = await downDai.balanceOf(cfd.address);
let upDaiExchange: IUniswapExchangeInstance = await IUniswapExchange.at(
await uniswapFactory.getExchange(upDai.address)
);
let downDaiExchange: IUniswapExchangeInstance = await IUniswapExchange.at(
await uniswapFactory.getExchange(downDai.address)
);
let neededEthCollateral;
let tx2 = await cfd.getETHCollateralRequirements(daiAmountDeposit);
truffleAssert.eventEmitted(tx2, "NeededEthCollateral", ev => {
neededEthCollateral = ev.upDaiPoolEth.add(ev.downDaiPoolEth);
return ev;
});
console.log("needed ETH: ", web3.utils.fromWei(neededEthCollateral, "ether"));
await dai.approve(cfd.address, daiAmountDeposit, {from: provider1});
await cfd.mint(daiAmountDeposit, {from: provider1, value: neededEthCollateral});
let cfdUpDaiBalanceAfter = await upDai.balanceOf(cfd.address); // for better test, calculate Uniswap exchange addLiquidity()
let cfdDownDaiBalanceAfter = await downDai.balanceOf(cfd.address); // for better test, calculate Uniswap exchange addLiquidity()
console.log("DOWNDAI exchange balance: ", (await downDaiExchange.balanceOf(cfd.address)).toString());
console.log("UPDAi exchange balance: ", (await upDaiExchange.balanceOf(cfd.address)).toString());
let provider1Stake = await cfd.stakes(provider1);
expect(await cfd.totalMintVolumeInDai()).bignumber.eq(
daiAmountDeposit,
"expected minted UPDAI mismatch"
);
expect(provider1Stake[0]).bignumber.eq(
await upDaiExchange.balanceOf(cfd.address),
"expected UPDAI LP mismatch"
);
expect(provider1Stake[1]).bignumber.eq(
await downDaiExchange.balanceOf(cfd.address),
"expected DOWNDAI LP mismatch"
);
/*expect(cfdUpDaiBalanceAfter.sub(cfdUpDaiBalanceBefore)).bignumber.eq(
daiAmountDeposit.div(new BN(2)),
"expected minted UPDAI mismatch"
);
expect(cfdDownDaiBalanceAfter.sub(cfdDownDaiBalanceBefore)).bignumber.eq(
daiAmountDeposit.div(new BN(2)),
"expected minted DOWNDAI mismatch"
);*/
});
});
describe("Buy UP/DOWN tokens", async () => {
it("buy UPDAI token", async () => {
let trader1DaiBalanceBefore = await dai.balanceOf(trader1);
let trader1UpDaiBalanceBefore = await upDai.balanceOf(trader1);
let daiExchange: IUniswapExchangeInstance = await IUniswapExchange.at(
await uniswapFactory.getExchange(dai.address)
);
await dai.approve(daiExchange.address, daiAmountToSellForUpDai, {from: trader1});
await daiExchange.tokenToTokenSwapInput(
daiAmountToSellForUpDai,
ether("1"),
1 * 10**14,
parseInt(now.toString())+oneMonthInSeconds,
upDai.address,
{ from: trader1 }
);
let trader1DaiBalanceAfter = await dai.balanceOf(trader1);
let trader1UpDaiBalanceAfter = await upDai.balanceOf(trader1);
console.log("Trader1 UPDAI balance: ", trader1UpDaiBalanceAfter.toString());
expect(trader1DaiBalanceBefore.sub(trader1DaiBalanceAfter)).bignumber.eq(
daiAmountToSellForUpDai,
"expected sold DAI amount for UPDAI mismatch"
);
});
it("buy DOWNDAI token", async () => {
let trader1DaiBalanceBefore = await dai.balanceOf(trader1);
let trader1DownDaiBalanceBefore = await downDai.balanceOf(trader1);
let daiExchange: IUniswapExchangeInstance = await IUniswapExchange.at(
await uniswapFactory.getExchange(dai.address)
);
await dai.approve(daiExchange.address, daiAmountToSellForDownDai, {from: trader1});
await daiExchange.tokenToTokenSwapInput(
daiAmountToSellForDownDai,
ether("1"),
1 * 10**14,
parseInt(now.toString())+oneMonthInSeconds,
downDai.address,
{ from: trader1 }
);
let trader1DaiBalanceAfter = await dai.balanceOf(trader1);
let trader1DownDaiBalanceAfter = await downDai.balanceOf(trader1);
console.log("Trader1 DOWNDAI balance: ", trader1DownDaiBalanceAfter.toString());
expect(trader1DaiBalanceBefore.sub(trader1DaiBalanceAfter)).bignumber.eq(
daiAmountToSellForDownDai,
"expected sold DAI amount for DOWNDAI mismatch"
);
});
});
describe("Redeem", async () => {
it("redeem during CFD execution time", async () => {
let upDaiRate;
let downDaiRate;
let _convertedUpDai, _convertedDownDai, _totalDaiPayout, _fee, _payout;
let amountToRedeem = await downDai.balanceOf(trader1); // trader should have an equal amount of UP&DOWN DAI tokens to burn
let trader1DaiBalanceBefore = await dai.balanceOf(trader1);
upDai.approve(cfd.address, amountToRedeem, { from: trader1 });
downDai.approve(cfd.address, amountToRedeem, { from: trader1 });
let tx1 = await cfd.redeem(amountToRedeem, { from: trader1 });
truffleAssert.eventEmitted(tx1, "UpDownDaiRates", ev => {
upDaiRate = ev.upDaiRate;
downDaiRate = ev.downDaiRate;
return ev;
});
_convertedUpDai = upDaiRate.mul(amountToRedeem).div(new BN(10).pow(new BN(18)));
_convertedDownDai = downDaiRate.mul(amountToRedeem).div(new BN(10).pow(new BN(18)));
_totalDaiPayout = _convertedUpDai.add(_convertedDownDai);
_fee = _totalDaiPayout.mul(await cfd.feeRate()).div(new BN(10).pow(new BN(18)));
_payout = _totalDaiPayout.sub(_fee);
let trader1DaiBalanceAfter = await dai.balanceOf(trader1);
console.log("Trader1 DAI balance before: ", trader1DaiBalanceBefore.toString());
console.log("Trader1 DAI balance after: ", trader1DaiBalanceAfter.toString());
console.log("Calculated payout: ", _payout.toString());
expect(trader1DaiBalanceAfter.sub(trader1DaiBalanceBefore)).bignumber.eq(
_payout,
"expected DAI payout mismatch"
);
});
it("redeem when CFD in settlement status", async () => {
//
});
});
});
});