-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReadme.md.mustache
824 lines (669 loc) · 37 KB
/
Readme.md.mustache
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
{{> partials/splash}}
[TOC]
## Details
- **Client** {{client_name}}
- **Date** {{date}}
- **Lead reviewer** Daniel Luca (@cleanunicorn)
- **Reviewers** Daniel Luca (@cleanunicorn), Andrei Simion (@andreiashu)
- **Repository**: [{{project_name}}]({{source_repository}})
- **Commit hash** `{{commit_hash}}`
- **Technologies**
- Solidity
- Node.JS
## Issues Summary
| SEVERITY | OPEN | CLOSED |
|----------------|:----------:|:------------:|
{{#issues_summary}}
| {{severity}} | {{open}} | {{closed}} |
{{/issues_summary}}
## Executive summary
This report represents the results of the engagement with **{{client_name}}** to review **{{project_name}}**.
The review was conducted over the course of **{{review_period}}** on **{{date_interval}}**. A total of **{{person_days}} person-days** were spent reviewing the code.
### Day 1
We started by going through the [initially provided document][Code changes summary] created by the client to have an initial idea about the recent changes.
We also set up a kickoff call with the client [which was recorded][kickoff call] to go through the general idea of the feature, the setup and the architecture of the system.
We continued to review the code, while creating an overview of the architecture to help us have a better understanding of the whole trust model.
### Day 2
Since the relationship of the new Sigma Committee contracts under audit is connected to the main Indexed Governance contracts we asked the client for an additional overview of the system, specifically how the contracts will be deployed. We received an additional document detailing the [deployment and ownership strategy][Deplyment strategy].
We also asked for more time than initially estimated because we needed time to process all provided additional documentation.
### Day 3
During the final day of the review, we finalized clarifying any issues we identified, finalized the bulk of the report and set up a meeting with the client to deliver the report.
Recordings:
- [Kickoff call][Kickoff call] password: `C#w%d6CX`
- [Sync #1][Sync #1] password: `TKmx2?9r`
## Scope
Documentation:
- [Code changes summary][Code changes summary]
- [Forum proposal][Forum proposal]
- [IIP 4: Sigma Pilot Snapshot Proposal][IIP4]
- [IIP 4: Sigma Pilot Thread][IIP4 Thread]
- [Deployment strategy][Deplyment strategy]
The initial review focused on the [{{project_name}}]({{source_repository}}) identified by the commit hash `{{commit_hash}}`.
We focused on manually reviewing the codebase, searching for security issues such as, but not limited to re-entrancy problems, transaction ordering, block timestamp dependency, exception handling, call stack depth limitation, integer overflow/underflow, self-destructible contracts, unsecured balance, use of origin, gas costly patterns, architectural problems, code readability.
**Includes:**
- The pull requests described in the document, specifically:
- [PR #13](https://github.com/indexed-finance/sigma-core/pull/13)
- [PR #15](https://github.com/indexed-finance/sigma-core/pull/15)
- [PR #11](https://github.com/indexed-finance/sigma-core/pull/11)
- [PR #7](https://github.com/indexed-finance/sigma-core/pull/7)
- [PR #10](https://github.com/indexed-finance/sigma-core/pull/10)
{{! Links }}
[Code changes summary]: https://hackmd.io/WDQtAVf5Qwe5VfSw03VgAQ "Code changes summary"
[Forum proposal]: https://forum.indexed.finance/t/overview-of-changes-to-smart-contracts/171 "Forum proposal"
[Deplyment strategy]: https://hackmd.io/@d1ll0n/rkEklV4Wd
[Kickoff call]: https://us02web.zoom.us/rec/share/ViV5h5HDjYxWf67tb1wZ6jc6jnNlpGgYWehijbO5sryil6gS1ozus-T_P8d43lI.Jf10udDPfAcjQnoI
[Sync #1]: https://us02web.zoom.us/rec/share/8B34_UCeybBdgPW9qgn9Tj1O-bmES7mLDDgD4ihMuCiwABmKD1Ugt09iesLlc604.ql4AhVJk8dtO5hM6
[IIP4]: https://snapshot.page/#/ndx.eth/proposal/QmbneygJdeXFNzxrtVw7CTZAgLUwPBfxrCBzucefuWC1Q8 "IIP 4: Sigma Pilot"
[IIP4 Thread]: https://forum.indexed.finance/t/iip-4-sigma-pilot/74 "IIP 4: Sigma Pilot Thread"
{{! /Links }}
## Trust model
We identified a few important parts of the system that hold increased trust and are critical to the system's correctness and well behavior.
### Target execution
Executing a transaction assumes the target will behave in a certain way. Even though Ethereum has an immutable contract system, there are ways to change how a contract behaves.
This can be done by:
Changing the contract's execution path by changing a storage slot, an external variable (like a timestamp) or a storage variable included in a separate contract
- Upgrading the contract's implementation to a different bytecode right before the target contract is executed
These are not all the ways the apparent contract execution can be unclear or misleading compared to the actual execution path and behavior.
Because the EVM offers an (almost) Turing-complete environment, it's virtually impossible to list all the ways the contract can trick an inexperienced observer into thinking it will not be malicious.
This, combined with the fact that some of the contracts can be upgradable and transaction reordering is a reality, can trick even the most experienced observer.
This is why there is a lot of trust the user has to put in the executed target and the actors who can upgrade parts of the system.
### User Interface
The voting process is another critical trustworthy part of the system that the users will put trust in. Even though they can verify on-chain that their votes will behave in a certain way, most of the users will not do that and will trust the web interface. This is because it's more complicated and tedious to do your own verifying. Thus, most of the users will trust the information relayed by the web interface.
The web interface inherently becomes a trust point between the system and the users.
The will also be other user interfaces the token holders will need to trust.
## Issues
{{#issues}}
### [{{title}}]({{url}})
![Issue status: {{status}}](https://img.shields.io/static/v1?label=Status&message={{status}}&color={{status_color}}&style=flat-square) ![{{severity}}](https://img.shields.io/static/v1?label=Severity&message={{severity}}&color={{severity_color}}&style=flat-square)
{{{body}}}
---
{{/issues}}
<div style="page-break-after: always"></div>
## Artifacts
### Ownership and control flow
![Image](./static/diagrams/ownership_control_flow.png)
### UML Diagram
Generated with [sol2uml](https://github.com/naddison36/sol2uml):
```bash
npm link sol2uml --only=production
sol2uml ./code/contracts
```
![Image](./static/diagrams/sol2uml_contract_diagram.svg)
### Surya
Sūrya is a utility tool for smart contract systems. It provides a number of visual outputs and information about the structure of smart contracts. It also supports querying the function call graph in multiple ways to aid in the manual inspection and control flow analysis of contracts.
#### Graphs
##### _CommitteeProxy_
![CommitteeProxy](./static/diagrams/surya_CommitteeProxy.png)
![CommitteeProxy Inheritance](./static/diagrams/surya_inheritance_CommitteeProxy.png)
##### _CommitteeTimelock_
![CommitteeTimelock](./static/diagrams/surya_CommitteeTimelock.png)
![CommitteeTimelock Inheritance](./static/diagrams/surya_inheritance_CommitteeTimelock.png)
##### _MarketCapSortedTokenCategories_
![MarketCapSortedTokenCategories](./static/diagrams/surya_MarketCapSortedTokenCategories.png)
![MarketCapSortedTokenCategories Inheritance](./static/diagrams/surya_inheritance_MarketCapSortedTokenCategories.png)
##### _MarketCapSqrtController_
![MarketCapSqrtController](./static/diagrams/surya_MarketCapSqrtController.png)
![MarketCapSqrtController Inheritance](./static/diagrams/surya_inheritance_MarketCapSqrtController.png)
##### _PoolFactory_
![PoolFactory](./static/diagrams/surya_PoolFactory.png)
![PoolFactory Inheritance](./static/diagrams/surya_inheritance_PoolFactory.png)
##### _PoolInitializer_
![PoolInitializer](./static/diagrams/surya_PoolInitializer.png)
![PoolInitializer Inheritance](./static/diagrams/surya_inheritance_PoolInitializer.png)
##### _UnboundTokenSeller_
![UnboundTokenSeller](./static/diagrams/surya_UnboundTokenSeller.png)
![UnboundTokenSeller Inheritance](./static/diagrams/surya_inheritance_UnboundTokenSeller.png)
### Tests
We ran tests on node version `v14.8.0` and solidity `0.6.12`. Compiling the contracts resulted in some warnings that are not of concern to the security of the project.
```text
$ node --version
v14.8.0
$ cd code
$ npm run test
> @indexed-finance/[email protected] test
> buidler test
(node:44972) Warning: Accessing non-existent property 'VERSION' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:44972) Warning: Accessing non-existent property 'INVALID_ALT_NUMBER' of module exports inside circular dependency
(node:44972) Warning: Accessing non-existent property 'INVALID_ALT_NUMBER' of module exports inside circular dependency
(node:44972) Warning: Accessing non-existent property 'INVALID_ALT_NUMBER' of module exports inside circular dependency
(node:44972) Warning: Accessing non-existent property 'INVALID_ALT_NUMBER' of module exports inside circular dependency
Compiling...
@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/balancer/BMath.sol:119:13: Warning: Only state variables can have a docstring. This will be disallowed in 0.7.0.
uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
^--------------^
contracts/interfaces/ICommitteeTimelock.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/LiquidityAdder.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/MockBorrower.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/MockUnbindSourcePool.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/MockUnbindTokenHandler.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/OracleFallthrough.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/tests/SellerTest.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/tests/util/Diff.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/tests/util/TestOrder.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/tests/util/TestTokenMarkets.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/mocks/tests/util/TestTokens.sol: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
contracts/committee/CommitteeTimelock.sol:18:1: Warning: This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.
contract CommitteeTimelock is ICommitteeTimelock {
^ (Relevant source part starts here and spans across multiple lines).
contracts/committee/CommitteeTimelock.sol:75:3: The payable fallback function is defined here.
fallback() external payable {}
^----------------------------^
Compiled 62 contracts successfully
CommitteeProxy.sol
Constructor & Settings
✓ committee
✓ owner
setCommittee()
✓ Reverts if not called by owner
✓ Sets the committee
executeTransaction()
✓ Reverts if not called by committee
✓ Executes transaction with function signature (44ms)
✓ Executes transaction without function signature (43ms)
✓ Reverts if the call fails
CommitteeTimelock.sol
Constructor & Settings
✓ admin
✓ superUser
✓ delay
✓ GRACE_PERIOD
✓ MINIMUM_DELAY (45ms)
✓ MAXIMUM_DELAY
queueTransaction()
✓ reverts if not an admin
✓ can be called by superUser
✓ can be called by admin (41ms)
executeTransaction()
✓ reverts if not an admin
✓ reverts if transaction with same txHash not queued
✓ reverts if timelock has not passed
✓ reverts if tx call fails
✓ can execute after the timelock passes (64ms)
sudo()
✓ reverts if not called by superUser
✓ reverts if tx call fails (39ms)
✓ allows superUser to immediately execute a tx (53ms)
IndexPool.sol
Constructor & Settings
✓ isPublicSwap()
✓ getSwapFee()
✓ getController()
✓ getExitFeeRecipient()
setExitFeeRecipient
✓ Reverts if not called by current fee recipient
✓ Updates the fee recipient
Control & Public
✓ Functions with _control_ role are only callable by controller
✓ Functions with _public_ modifier are only callable after initialization (122ms)
configure(): fail
✓ Reverts if controller is already set
✓ Reverts if provided controller address is zero (89ms)
initialize(): fail
✓ Reverts if the pool is already initialized
✓ Reverts if array lengths do not match (155ms)
✓ Reverts if less than 2 tokens are provided
✓ Reverts if more than 10 tokens are provided
✓ Reverts if any denorm < MIN_WEIGHT
✓ Reverts if any denorm > MAX_WEIGHT (47ms)
✓ Reverts if any balance < MIN_BALANCE
✓ Reverts if total weight > maximum (45ms)
gulp()
✓ Sends unbound tokens to handler
✓ Updates balance for bound tokens (44ms)
✓ Updates balance for uninitialized tokens (56ms)
✓ Initializes token if minimumm balance is hit (40ms)
setSwapFee()
✓ Reverts if caller is not controller
✓ Reverts if swapFee < 0.0001%
✓ Reverts if swapFee > 10%
✓ Sets swap fee between min and max
setPublicSwap()
✓ Freezes public swaps
✓ Disables functions with _public
✓ Enables public swaps
setMinimumBalance()
✓ Reverts if caller is not controller
✓ Reverts if token is not bound
✓ Reverts if token is initialized
✓ Sets minimum balance of uninitialized token (70ms)
Token Queries
✓ isBound()
✓ getNumTokens()
✓ getCurrentTokens()
✓ getCurrentDesiredTokens() (69ms)
✓ getDenormalizedWeight(): success
✓ getDenormalizedWeight(): fail
✓ getTotalDenormalizedWeight()
✓ getBalance(): success
✓ getBalance(): reverts if token is not bound
✓ getMinimumBalance(): reverts if token is not bound
✓ getMinimumBalance(): reverts if token is ready
✓ getUsedBalance(): returns actual balance for initialized token
✓ getUsedBalance(): returns minimum balance for uninitialized token (74ms)
✓ getUsedBalance(): reverts if token is not bound
getTokenRecord()
✓ Returns expected record for bound token
✓ Reverts if token is not bound
extrapolatePoolValueFromToken()
✓ Succeeds if any token is ready and desired
✓ Reverts if no tokens are both ready and desired
getSpotPrice()
✓ Reverts if either token is unbound
✓ Prices initialized tokens normally (56ms)
✓ Reverts if tokenOut is not ready (91ms)
✓ Uses the minimum balance and weight to price uninitialized tokens
swapExactAmountIn()
✓ Reverts if either token is unbound
✓ Reverts if input amount > 1/2 of balance
✓ Reverts if spotPriceBefore is lower than maxPrice (67ms)
✓ Reverts if spotPriceAfter is lower than maxPrice (170ms)
✓ Reverts if tokenAmountOut < minAmountOut (39ms)
✓ Prices initialized tokens normally (226ms)
✓ Uses the minimum balance and weight to price uninitialized tokens, and uses updated weight for spotPriceAfter (194ms)
✓ Reverts if tokenOut is uninitialized (80ms)
swapExactAmountOut()
✓ Reverts if either token is unbound
✓ Reverts if output amount > 1/3 of balance
✓ Reverts if spot price is lower than maxPrice (44ms)
✓ Prices initialized tokens normally (432ms)
✓ Uses the minimum balance and weight to price uninitialized tokens, and uses updated weight for spotPriceAfter (190ms)
✓ Reverts if tokenOut is uninitialized
joinswapExternAmountIn()
✓ Reverts if tokenAmountIn = 0
✓ Reverts if tokenAmountIn > balanceIn / 2
✓ Reverts if poolAmountOut < minPoolAmountOut
✓ Prices initialized tokens normally
✓ Prices uninitialized tokens using minimum balance and weight (97ms)
joinswapPoolAmountOut()
✓ Reverts if tokenAmountIn > balanceIn / 2
✓ Reverts if tokenAmountIn > maxAmountIn
✓ Prices initialized tokens normally (57ms)
✓ Prices uninitialized tokens using minimum balance and weight (115ms)
joinPool()
✓ Reverts if invalid array length is given
✓ Reverts if zero tokens are requested
✓ Reverts if tokenAmountIn > maxAmountIn
✓ Prices initialized tokens normally (111ms)
✓ Prices uninitialized tokens using minimum balance and weight (232ms)
exitswapPoolAmountIn()
✓ Prices initialized tokens normally (61ms)
✓ Reverts if unbound token is given
✓ Reverts if tokenAmountOut < minAmountOut
✓ Reverts if tokenAmountOut > balanceOut / 3 (60ms)
✓ Reverts if uninitialized token is given (79ms)
exitswapExternAmountOut()
✓ Prices initialized tokens normally (167ms)
✓ Reverts if unbound token is given
✓ Reverts if poolRatio = 0
✓ Reverts if poolAmountIn > maxPoolAmountIn (43ms)
✓ Reverts if tokenAmountOut > balanceOut / 3
✓ Reverts if uninitialized token is given (61ms)
exitPool()
✓ Prices initialized tokens normally (60ms)
✓ Reverts if poolRatio = 0
✓ Reverts if invalid array length is given
✓ Reverts if tokenAmountOut < minAmountOut
✓ Reverts if minAmountOut is not zero for uninitialized tokens (109ms)
✓ Gives 0 for uninitialized tokens
delegateCompLikeToken()
✓ delegates a comp-like token to the provided address
IndexPool.sol
Constructor & Settings
✓ isPublicSwap()
✓ getSwapFee()
✓ getController()
Control & Public
✓ Functions with _control_ role are only callable by controller
✓ Functions with _public_ modifier are only callable after initialization (97ms)
configure(): fail
✓ Reverts if controller is already set
✓ Reverts if provided controller address is zero (77ms)
initialize(): fail
✓ Reverts if the pool is already initialized
✓ Reverts if array lengths do not match (282ms)
✓ Reverts if less than 2 tokens are provided
✓ Reverts if more than 10 tokens are provided
✓ Reverts if any denorm < MIN_WEIGHT (76ms)
✓ Reverts if any denorm > MAX_WEIGHT (74ms)
✓ Reverts if any balance < MIN_BALANCE (80ms)
✓ Reverts if total weight > maximum (112ms)
gulp()
✓ Sends unbound tokens to handler
✓ Updates balance for bound tokens
✓ Updates balance for uninitialized tokens (43ms)
✓ Initializes token if minimumm balance is hit (61ms)
setSwapFee()
✓ Reverts if caller is not controller
✓ Reverts if swapFee < 0.0001%
✓ Reverts if swapFee > 10%
✓ Sets swap fee between min and max
setMinimumBalance()
✓ Reverts if caller is not controller
✓ Reverts if token is not bound
✓ Reverts if token is initialized
✓ Sets minimum balance of uninitialized token (195ms)
Token Queries
✓ isBound() (49ms)
✓ getNumTokens()
✓ getCurrentTokens()
✓ getCurrentDesiredTokens() (71ms)
✓ getDenormalizedWeight(): success
✓ getDenormalizedWeight(): fail
✓ getTotalDenormalizedWeight()
✓ getBalance(): success
✓ getBalance(): reverts if token is not bound
✓ getMinimumBalance(): reverts if token is not bound
✓ getMinimumBalance(): reverts if token is ready
✓ getUsedBalance(): returns actual balance for initialized token
✓ getUsedBalance(): returns minimum balance for uninitialized token (164ms)
✓ getUsedBalance(): reverts if token is not bound
getTokenRecord()
✓ Returns expected record for bound token
✓ Reverts if token is not bound
extrapolatePoolValueFromToken()
✓ Succeeds if any token is ready and desired
✓ Reverts if no tokens are both ready and desired
getSpotPrice()
✓ Reverts if either token is unbound
✓ Prices initialized tokens normally (690ms)
✓ Reverts if tokenOut is not ready (272ms)
✓ Uses the minimum balance and weight to price uninitialized tokens (93ms)
swapExactAmountIn()
✓ Reverts if either token is unbound
✓ Reverts if input amount > 1/2 of balance (63ms)
✓ Reverts if spotPriceBefore is lower than maxPrice (136ms)
✓ Reverts if spotPriceAfter is lower than maxPrice (406ms)
✓ Reverts if tokenAmountOut < minAmountOut (125ms)
✓ Prices initialized tokens normally (2321ms)
✓ Uses the minimum balance and weight to price uninitialized tokens, and uses updated weight for spotPriceAfter (546ms)
✓ Reverts if tokenOut is uninitialized (225ms)
swapExactAmountOut()
✓ Reverts if either token is unbound
✓ Reverts if output amount > 1/3 of balance (85ms)
✓ Reverts if spot price is lower than maxPrice (143ms)
✓ Prices initialized tokens normally (3592ms)
✓ Uses the minimum balance and weight to price uninitialized tokens, and uses updated weight for spotPriceAfter (540ms)
✓ Reverts if tokenOut is uninitialized (63ms)
joinswapExternAmountIn()
✓ Reverts if tokenAmountIn = 0
✓ Reverts if tokenAmountIn > balanceIn / 2
✓ Reverts if poolAmountOut < minPoolAmountOut
✓ Prices initialized tokens normally (353ms)
✓ Prices uninitialized tokens using minimum balance and weight (211ms)
joinswapPoolAmountOut()
✓ Reverts if tokenAmountIn > balanceIn / 2
✓ Reverts if tokenAmountIn > maxAmountIn
✓ Prices initialized tokens normally
✓ Prices uninitialized tokens using minimum balance and weight (200ms)
joinPool()
✓ Reverts if invalid array length is given
✓ Reverts if zero tokens are requested
✓ Reverts if tokenAmountIn > maxAmountIn
✓ Prices initialized tokens normally (283ms)
✓ Prices uninitialized tokens using minimum balance and weight (563ms)
exitswapPoolAmountIn()
✓ Prices initialized tokens normally (193ms)
✓ Reverts if unbound token is given
✓ Reverts if tokenAmountOut < minAmountOut (111ms)
✓ Reverts if tokenAmountOut > balanceOut / 3 (121ms)
✓ Reverts if uninitialized token is given (161ms)
exitswapExternAmountOut()
✓ Prices initialized tokens normally (217ms)
✓ Reverts if unbound token is given
✓ Reverts if poolRatio = 0
✓ Reverts if poolAmountIn > maxPoolAmountIn (117ms)
✓ Reverts if tokenAmountOut > balanceOut / 3 (54ms)
✓ Reverts if uninitialized token is given (168ms)
exitPool()
✓ Prices initialized tokens normally (125ms)
✓ Reverts if poolRatio = 0
✓ Reverts if invalid array length is given
✓ Reverts if tokenAmountOut < minAmountOut
✓ Reverts if minAmountOut is not zero for uninitialized tokens (214ms)
✓ Gives 0 for uninitialized tokens (64ms)
reweighTokens()
reweighTokens(): fail
✓ Reverts if caller is not controller
✓ Reverts if invalid array length is given
✓ Reverts if a token is not bound
✓ Reverts if desiredDenorm < MIN_WEIGHT
✓ Reverts if desiredDenorm > MAX_WEIGHT
reweighTokens(): Set one target to 0
✓ Allows desired weight to be set to 0
Pool records match expected
✓ getCurrentTokens()
✓ getCurrentDesiredTokens()
✓ all::record.denorm
✓ all::record.desiredDenorm
✓ all::record.ready
reweighTokens(): success
✓ reweighTokens()
Adjust weights during swaps and joins
✓ swapExactAmountIn (479ms)
✓ swapExactAmountOut (504ms)
✓ joinswapExternAmountIn (227ms)
✓ joinswapPoolAmountOut (223ms)
Pool records match expected
✓ getCurrentTokens()
✓ getCurrentDesiredTokens()
✓ all::record.denorm
✓ all::record.desiredDenorm
✓ all::record.ready
reindexTokens(): fail
✓ Reverts if caller is not controller
✓ Reverts if array lengths do not match
✓ Reverts if minimumBalance < MIN_BALANCE
✓ Reverts if desiredDenorm > MAX_WEIGHT
reindexTokens(): success
✓ Sets desiredDenorm to MIN_WEIGHT if 0 is provided
✓ Sets desiredDenorm of tokens not included in the call to zero
✓ Swaps new token in until it is initialized (177ms)
Should take 19 steps to remove
✓ Swaps old token out until it is removed (699ms)
MarketCapSortedTokenCategories.sol
categoryIndex()
✓ Sets first category ID to 1
updateCategoryPrices()
✓ Reverts if category does not exist
✓ Updates prices of tokens in category (1184ms)
hasCategory()
✓ Returns false if category does not exist
✓ Returns true if category exists
isTokenInCategory()
✓ Reverts if invalid category ID is given
✓ Returns false if token is not bound (94ms)
✓ Returns true if token is bound (90ms)
✓ Returns false if token is removed (189ms)
createCategory()
✓ Reverts if caller is not owner
✓ Allows owner to create a category
addToken()
✓ Reverts if caller is not owner
✓ Reverts if categoryIndex is 0
✓ Reverts if categoryID > categoryIndex
✓ Reverts if category is already at the maximum (3410ms)
✓ Reverts if token is already bound to same category (184ms)
✓ Resets the lastCategoryUpdate time (577ms)
✓ Returns tokens
removeToken()
✓ Reverts if caller is not owner
✓ Reverts if categoryIndex is 0
✓ Reverts if categoryID > categoryIndex
✓ Reverts if category is empty
✓ Reverts if token not found (152ms)
✓ Resets the lastCategoryUpdate time (227ms)
✓ Swaps with last token in list (3064ms)
addTokens()
✓ Reverts if caller is not owner
✓ Reverts if categoryIndex is 0
✓ Reverts if categoryID > categoryIndex
✓ Reverts if category would exceed maximum after adding the tokens (3115ms)
✓ Reverts if any of the tokens are already bound (221ms)
getCategoryTokens()
✓ Reverts if categoryIndex is 0
✓ Reverts if categoryID > categoryIndex
✓ Returns the category tokens (100ms)
computeAverageMarketCap()
✓ Reverts if the oracle does not have a price observation in the TWAP range
✓ Returns correct token market caps (968ms)
computeAverageMarketCaps()
✓ Reverts if the oracle does not have price observations in the TWAP range
✓ Returns correct token market caps (880ms)
getCategoryMarketCaps()
✓ Reverts if the category does not exist
✓ Reverts if the oracle does not have price observations in the TWAP range (122ms)
✓ Returns expected market caps (762ms)
getTopCategoryTokens()
✓ Reverts if the category does not exist
✓ Reverts if size > number of category tokens (95ms)
✓ Reverts if category has not been sorted recently
✓ Returns top n tokens in descending order of market cap (978ms)
orderCategoryTokensByMarketCap()
✓ Reverts if the category does not exist
✓ Reverts if the oracle does not have price observations in the TWAP range (112ms)
✓ Sorts the category using insertion sort (783ms)
✓ Sets the last category update timestamp
MarketCapSqrtController.sol
Initializer & Settings
✓ defaultSellerPremium(): set to 2
✓ owner()
onlyOwner
✓ All functions with onlyOwner modifier revert if caller is not owner
_havePool
✓ All functions with _havePool modifier revert if pool address not recognized
setDefaultSellerPremium()
✓ Reverts if premium == 0
✓ Reverts if premium >= 20
✓ Sets allowed premium
getInitialTokensAndBalances()
✓ Returns the top n category tokens and target balances weighted by mcap sqrt (1617ms)
✓ Reverts if any token has a target balance below the minimum
prepareIndexPool()
✓ Reverts if size > 10 (869ms)
✓ Reverts if size < 2
✓ Reverts if initialWethValue >= 2^144
✓ Succeeds with valid inputs (99ms)
✓ Deploys the pool and initializer to the correct addresses
✓ Reverts if the pool params are duplicates
✓ Sets expected desired tokens and balances on pool initializer (1917ms)
finishPreparedIndexPool()
✓ Reverts if caller is not initializer
✓ Reverts if array lengths do not match (957ms)
✓ Reverts if pool is already initialized (831ms)
updateSellerPremium()
✓ Reverts if premium == 0
✓ Reverts if premium >= 20
✓ Sets premium within allowed range
setSwapFee()
✓ Sets swap fee on the pool
reweighPool()
✓ Reverts if < 2 weeks have passed
✓ Reweighs the pool and sets desired weights proportional to mcap sqrts (1686ms)
✓ Reverts if reweighIndex % 4 == 0 (5040ms)
reindexPool()
✓ Reverts if < 2 weeks have passed
✓ Reverts if reweighIndex % 4 != 0 (1506ms)
✓ Reverts if category has not been sorted recently (6350ms)
✓ Reindexes the pool with correct minimum balances and desired weights (394ms)
updateMinimumBalance()
✓ Reverts if token is initialized
✓ Updates minimum balance based on extrapolated pool value (6921ms)
delegateCompLikeTokenFromPool()
✓ Delegates a comp-like token in an index pool
OwnableProxy.sol
✓ locks the base implementation contract
initialize ownership
✓ has null owner before initialization
✓ sets owner
✓ prevents initialized contract from being initialized
transfer ownership
✓ changes owner after transfer
✓ prevents non-owners from transferring
✓ guards ownership against stuck state
renounce ownership
✓ loses owner after renouncement
✓ prevents non-owners from renouncement
PoolFactory.sol
approvePoolController()
✓ Reverts if not called by owner
✓ Marks account as approved controller
disapprovePoolController()
✓ Reverts if not called by owner
✓ Marks account as unapproved controller
deployPool()
✓ Reverts if not called by approved controller
✓ Deploys pool
✓ Emits NewPool event with pool info
✓ Stores the pool implementation ID
computePoolAddress()
✓ Returns same address as deployment
isRecognizedPool()
✓ Returns true for deployed pool
✓ Returns false for any other account
PoolInitializer.sol
initialize()
✓ Reverts if array lengths do not match
✓ Succeeds with valid inputs on first call
✓ Reverts if already initialized
_finished_
✓ All functions with _finished_ modifier revert if the initializer is not finished
_not_finished_
✓ All functions with _not_finished_ modifier revert if the initializer is finished (481ms)
isFinished()
✓ Returns false before finished
✓ Returns true after finished (277ms)
getDesiredTokens()
✓ Returns expected tokens
getDesiredAmounts()
✓ Returns remaining desired amounts (992ms)
getDesiredAmount()
✓ Returns expected desired amount (948ms)
getCreditForTokens()
✓ Returns the eth value of the token (791ms)
✓ Reverts if the token is not desired (288ms)
getTotalCredit()
✓ Returns total credited value (66ms)
getCreditOf()
✓ Returns credited value for account (70ms)
contributeTokens(address,uint256,uint256)
✓ Returns credited value (885ms)
✓ Reverts if credit < minCredit (910ms)
✓ Reverts if amountIn = 0 (57ms)
✓ Reverts if token not needed (225ms)
contributeTokens(address[],uint256[],uint256)
✓ Returns credited value (855ms)
✓ Reverts if array lengths do not match
✓ Reverts if credit < minCredit (132ms)
✓ Reverts if amountIn = 0 (70ms)
✓ Reverts if token not needed (169ms)
claimTokens()
✓ Claims tokens for the caller proportional to their credits (520ms)
claimTokens(address)
✓ Claims tokens for the provided account proportional to their credits (312ms)
claimTokens(address[])
✓ Claims tokens for the provided accounts proportional to their credits (472ms)
finish()
✓ Reverts if there are remaining desired amounts for any tokens (153ms)
✓ Sets finished to true (178ms)
UnboundTokenSeller.sol
✓ initialize()
✓ init (361ms)
✓ setPremiumPercent()
✓ handleUnbindToken()
✓ calcInGivenOut() (38ms)
✓ calcOutGivenIn() (63ms)
✓ swapExactTokensForTokens() (76ms)
✓ swapTokensForExactTokens() (70ms)
✓ executeSwapTokensForExactTokens() (114ms)
✓ executeSwapExactTokensForTokens() (116ms)
388 passing (4m)
```
## License
This report falls under the terms described in the included [LICENSE](./LICENSE).
{{> partials/features}}
<link rel="stylesheet" href="./style/print.css"/>