1
+ import "module-alias/register" ;
2
+ import { BigNumber } from "@ethersproject/bignumber" ;
3
+
4
+ import { Address } from "@utils/types" ;
5
+ import { Account } from "@utils/test/types" ;
6
+ import { ADDRESS_ZERO , MAX_UINT_256 , ZERO } from "@utils/constants" ;
7
+ import {
8
+ GeneralIndexModule ,
9
+ SetToken ,
10
+ AMMSplitter ,
11
+ UniswapV2IndexExchangeAdapter
12
+ } from "@utils/contracts" ;
13
+ import DeployHelper from "@utils/deploys" ;
14
+ import {
15
+ bitcoin ,
16
+ ether ,
17
+ preciseDiv ,
18
+ preciseMul ,
19
+ } from "@utils/index" ;
20
+ import {
21
+ cacheBeforeEach ,
22
+ getAccounts ,
23
+ getSystemFixture ,
24
+ getUniswapFixture ,
25
+ getWaffleExpect
26
+ } from "@utils/test/index" ;
27
+ import { SystemFixture , UniswapFixture } from "@utils/fixtures" ;
28
+ import { ContractTransaction } from "ethers" ;
29
+
30
+ const expect = getWaffleExpect ( ) ;
31
+
32
+ describe ( "AMMSplitterGeneralIndexModule" , ( ) => {
33
+ let owner : Account ;
34
+ let trader : Account ;
35
+ let positionModule : Account ;
36
+ let deployer : DeployHelper ;
37
+ let setup : SystemFixture ;
38
+
39
+ let uniswapSetup : UniswapFixture ;
40
+ let sushiswapSetup : UniswapFixture ;
41
+ let tradeSplitter : AMMSplitter ;
42
+
43
+ let index : SetToken ;
44
+ let indexModule : GeneralIndexModule ;
45
+
46
+ let tradeSplitterAdapter : UniswapV2IndexExchangeAdapter ;
47
+ let tradeSplitterAdapterName : string ;
48
+
49
+ let indexComponents : Address [ ] ;
50
+ let indexUnits : BigNumber [ ] ;
51
+
52
+ before ( async ( ) => {
53
+ [
54
+ owner ,
55
+ trader ,
56
+ positionModule ,
57
+ ] = await getAccounts ( ) ;
58
+
59
+ deployer = new DeployHelper ( owner . wallet ) ;
60
+ setup = getSystemFixture ( owner . address ) ;
61
+ uniswapSetup = getUniswapFixture ( owner . address ) ;
62
+ sushiswapSetup = getUniswapFixture ( owner . address ) ;
63
+
64
+ await setup . initialize ( ) ;
65
+ await uniswapSetup . initialize ( owner , setup . weth . address , setup . wbtc . address , setup . dai . address ) ;
66
+ await sushiswapSetup . initialize ( owner , setup . weth . address , setup . wbtc . address , setup . dai . address ) ;
67
+
68
+ indexModule = await deployer . modules . deployGeneralIndexModule (
69
+ setup . controller . address ,
70
+ setup . weth . address
71
+ ) ;
72
+ await setup . controller . addModule ( indexModule . address ) ;
73
+ await setup . controller . addModule ( positionModule . address ) ;
74
+
75
+
76
+ tradeSplitter = await deployer . product . deployAMMSplitter ( uniswapSetup . router . address , sushiswapSetup . router . address ) ;
77
+ tradeSplitterAdapter = await deployer . adapters . deployUniswapV2IndexExchangeAdapter ( tradeSplitter . address ) ;
78
+ tradeSplitterAdapterName = "TRADESPLITTER" ;
79
+
80
+
81
+ await setup . integrationRegistry . batchAddIntegration (
82
+ [ indexModule . address ] ,
83
+ [ tradeSplitterAdapterName ] ,
84
+ [ tradeSplitterAdapter . address ]
85
+ ) ;
86
+ } ) ;
87
+
88
+ cacheBeforeEach ( async ( ) => {
89
+ indexComponents = [ setup . wbtc . address , setup . dai . address ] ;
90
+ indexUnits = [ bitcoin ( 0.01 ) , ether ( 4000 ) ] ;
91
+
92
+ index = await setup . createSetToken (
93
+ indexComponents ,
94
+ indexUnits ,
95
+ [ setup . issuanceModule . address , indexModule . address , positionModule . address ] ,
96
+ ) ;
97
+
98
+ await setup . issuanceModule . initialize ( index . address , ADDRESS_ZERO ) ;
99
+ await index . connect ( positionModule . wallet ) . initializeModule ( ) ;
100
+
101
+ await setup . weth . connect ( owner . wallet ) . approve ( sushiswapSetup . router . address , ether ( 2000 ) ) ;
102
+ await setup . dai . connect ( owner . wallet ) . approve ( sushiswapSetup . router . address , ether ( 400000 ) ) ;
103
+ await sushiswapSetup . router . connect ( owner . wallet ) . addLiquidity (
104
+ setup . weth . address ,
105
+ setup . dai . address ,
106
+ ether ( 200 ) ,
107
+ ether ( 400000 ) ,
108
+ ether ( 148 ) ,
109
+ ether ( 173000 ) ,
110
+ owner . address ,
111
+ MAX_UINT_256
112
+ ) ;
113
+
114
+ await setup . weth . connect ( owner . wallet ) . approve ( uniswapSetup . router . address , ether ( 1000 ) ) ;
115
+ await setup . wbtc . connect ( owner . wallet ) . approve ( uniswapSetup . router . address , ether ( 26 ) ) ;
116
+ await uniswapSetup . router . addLiquidity (
117
+ setup . weth . address ,
118
+ setup . wbtc . address ,
119
+ ether ( 1000 ) ,
120
+ bitcoin ( 25.5555 ) ,
121
+ ether ( 999 ) ,
122
+ ether ( 25.3 ) ,
123
+ owner . address ,
124
+ MAX_UINT_256
125
+ ) ;
126
+ } ) ;
127
+
128
+ describe ( "when module is initalized" , async ( ) => {
129
+ let subjectSetToken : SetToken ;
130
+ let subjectCaller : Account ;
131
+
132
+ let newComponents : Address [ ] ;
133
+ let newTargetUnits : BigNumber [ ] ;
134
+ let oldTargetUnits : BigNumber [ ] ;
135
+ let issueAmount : BigNumber ;
136
+
137
+ async function initSetToken (
138
+ setToken : SetToken , components : Address [ ] , tradeMaximums : BigNumber [ ] , exchanges : string [ ] , coolOffPeriods : BigNumber [ ]
139
+ ) {
140
+ await indexModule . initialize ( setToken . address ) ;
141
+ await indexModule . setTradeMaximums ( setToken . address , components , tradeMaximums ) ;
142
+ await indexModule . setExchanges ( setToken . address , components , exchanges ) ;
143
+ await indexModule . setCoolOffPeriods ( setToken . address , components , coolOffPeriods ) ;
144
+ await indexModule . setTraderStatus ( setToken . address , [ trader . address ] , [ true ] ) ;
145
+ }
146
+
147
+ const startRebalance = async ( ) => {
148
+ await setup . approveAndIssueSetToken ( subjectSetToken , issueAmount ) ;
149
+ await indexModule . startRebalance (
150
+ subjectSetToken . address ,
151
+ newComponents ,
152
+ newTargetUnits ,
153
+ oldTargetUnits ,
154
+ await index . positionMultiplier ( )
155
+ ) ;
156
+ } ;
157
+
158
+ before ( async ( ) => {
159
+ newComponents = [ ] ;
160
+ oldTargetUnits = [ bitcoin ( 0.1 ) , ether ( 1 ) ] ;
161
+ newTargetUnits = [ ] ;
162
+ issueAmount = ether ( "20.000000000000000001" ) ;
163
+ } ) ;
164
+
165
+ cacheBeforeEach ( async ( ) => {
166
+ await initSetToken (
167
+ index ,
168
+ [ setup . wbtc . address , setup . dai . address ] ,
169
+ [ bitcoin ( 1000 ) , ether ( 100000 ) ] ,
170
+ [ tradeSplitterAdapterName , tradeSplitterAdapterName ] ,
171
+ [ ZERO , ZERO ]
172
+ ) ;
173
+ } ) ;
174
+
175
+ describe ( "#trade" , async ( ) => {
176
+ let subjectComponent : Address ;
177
+ let subjectEthQuantityLimit : BigNumber ;
178
+
179
+ let expectedOut : BigNumber ;
180
+
181
+ const initializeSubjectVariables = ( ) => {
182
+ subjectSetToken = index ;
183
+ subjectCaller = trader ;
184
+ subjectComponent = setup . dai . address ;
185
+ subjectEthQuantityLimit = ZERO ;
186
+ } ;
187
+
188
+ async function subject ( ) : Promise < ContractTransaction > {
189
+ return await indexModule . connect ( subjectCaller . wallet ) . trade (
190
+ subjectSetToken . address ,
191
+ subjectComponent ,
192
+ subjectEthQuantityLimit
193
+ ) ;
194
+ }
195
+
196
+ describe ( "with default target units" , async ( ) => {
197
+ beforeEach ( async ( ) => {
198
+ initializeSubjectVariables ( ) ;
199
+
200
+ expectedOut = ( await tradeSplitter . getAmountsOut (
201
+ preciseMul ( ether ( 3999 ) , issueAmount ) ,
202
+ [ setup . dai . address , setup . weth . address ]
203
+ ) ) [ 1 ] ;
204
+
205
+ subjectEthQuantityLimit = BigNumber . from ( 0 ) ;
206
+ } ) ;
207
+ cacheBeforeEach ( startRebalance ) ;
208
+
209
+ it ( "should sell using TradeSplitter" , async ( ) => {
210
+ const currentWethAmount = await setup . weth . balanceOf ( subjectSetToken . address ) ;
211
+ const totalSupply = await subjectSetToken . totalSupply ( ) ;
212
+
213
+ await subject ( ) ;
214
+
215
+ const expectedWethPositionUnits = preciseDiv ( currentWethAmount . add ( expectedOut ) , totalSupply ) ;
216
+ const expectedDaiPositionUnits = ether ( 1 ) ;
217
+
218
+ const wethPositionUnits = await subjectSetToken . getDefaultPositionRealUnit ( setup . weth . address ) ;
219
+ const daiPositionUnits = await subjectSetToken . getDefaultPositionRealUnit ( setup . dai . address ) ;
220
+
221
+ expect ( wethPositionUnits ) . to . eq ( expectedWethPositionUnits ) ;
222
+ expect ( daiPositionUnits ) . to . eq ( expectedDaiPositionUnits ) ;
223
+ } ) ;
224
+ } ) ;
225
+ } ) ;
226
+
227
+ describe ( "#tradeRemainingWETH" , async ( ) => {
228
+
229
+ let subjectComponent : Address ;
230
+ let subjectEthQuantityLimit : BigNumber ;
231
+
232
+ let expectedOut : BigNumber ;
233
+
234
+ const initializeSubjectVariables = ( ) => {
235
+ subjectSetToken = index ;
236
+ subjectCaller = trader ;
237
+ subjectComponent = setup . dai . address ;
238
+ subjectEthQuantityLimit = ZERO ;
239
+ } ;
240
+
241
+ async function subject ( ) : Promise < ContractTransaction > {
242
+ return await indexModule . connect ( subjectCaller . wallet ) . tradeRemainingWETH (
243
+ subjectSetToken . address ,
244
+ subjectComponent ,
245
+ subjectEthQuantityLimit
246
+ ) ;
247
+ }
248
+
249
+ describe ( "with default target units" , async ( ) => {
250
+
251
+ beforeEach ( async ( ) => {
252
+ initializeSubjectVariables ( ) ;
253
+ } ) ;
254
+ cacheBeforeEach ( startRebalance ) ;
255
+
256
+ context ( "when it is the last trade" , async ( ) => {
257
+
258
+ beforeEach ( async ( ) => {
259
+ await indexModule . connect ( subjectCaller . wallet ) . trade (
260
+ subjectSetToken . address ,
261
+ subjectComponent ,
262
+ subjectEthQuantityLimit
263
+ ) ;
264
+
265
+ expectedOut = ( await tradeSplitter . getAmountsOut (
266
+ await setup . weth . balanceOf ( subjectSetToken . address ) ,
267
+ [ setup . weth . address , setup . wbtc . address ]
268
+ ) ) [ 1 ] ;
269
+
270
+ subjectComponent = setup . wbtc . address ;
271
+ subjectEthQuantityLimit = ZERO ;
272
+ } ) ;
273
+
274
+ it ( "should buy using TradeSplitter" , async ( ) => {
275
+ const currentWbtcAmount = await setup . wbtc . balanceOf ( subjectSetToken . address ) ;
276
+ const totalSupply = await subjectSetToken . totalSupply ( ) ;
277
+
278
+ await subject ( ) ;
279
+
280
+ const expectedWbtcPositionUnits = preciseDiv ( currentWbtcAmount . add ( expectedOut ) , totalSupply ) ;
281
+ const expectedWethPositionUnits = ether ( 0 ) ;
282
+
283
+ const wethPositionUnits = await subjectSetToken . getDefaultPositionRealUnit ( setup . weth . address ) ;
284
+ const wbtcPositionUnits = await subjectSetToken . getDefaultPositionRealUnit ( setup . wbtc . address ) ;
285
+
286
+ expect ( wbtcPositionUnits ) . to . eq ( expectedWbtcPositionUnits ) ;
287
+ expect ( wethPositionUnits ) . to . eq ( expectedWethPositionUnits ) ;
288
+ } ) ;
289
+ } ) ;
290
+ } ) ;
291
+ } ) ;
292
+ } ) ;
293
+ } ) ;
0 commit comments