Explorer
Blockchain
Blocks
Blocks
Uncles
Forked Blocks (Reorgs)
Transactions
Validated
Pending
Verified contracts
Tokens
All
REI
APIs
GraphQL
RPC
Eth RPC
REI Network
Mainnets
Scan Mainnet
Testnets
FAUCET
Scan Testnet
Sign in
/
Search
/
Search
Connection Lost
New Solidity Smart Contract Verification
Contract Address
The 0x address supplied on contract creation.
Contract Name
Must match the name specified in the code. For example, in
contract MyContract {..}
MyContract
is the contract name.
Include nightly builds
No
Yes
Select yes if you want to show nightly builds.
Compiler
latest
v0.8.30-nightly.2025.3.15+commit.a6522927
v0.8.30-nightly.2025.3.14+commit.c8893ec9
v0.8.30-nightly.2025.3.12+commit.340ae32d
v0.8.29+commit.ab55807c
v0.8.29-nightly.2025.3.11+commit.7c461bff
v0.8.29-nightly.2025.3.10+commit.fbbe2ddc
v0.8.29-nightly.2025.3.8+commit.d084819c
v0.8.29-nightly.2025.3.6+commit.721bb93f
v0.8.29-nightly.2025.3.5+commit.3dc2cf86
v0.8.29-nightly.2025.3.3+commit.1965d747
v0.8.29-nightly.2025.3.2+commit.a83a8201
v0.8.29-nightly.2025.3.1+commit.cff2a28f
v0.8.29-nightly.2025.2.27+commit.5879562d
v0.8.29-nightly.2025.2.26+commit.a7e6cb53
v0.8.29-nightly.2025.2.24+commit.eb216716
v0.8.29-nightly.2025.2.21+commit.53ab29fb
v0.8.29-nightly.2025.2.18+commit.053807e5
v0.8.29-nightly.2025.2.17+commit.72030706
v0.8.29-nightly.2025.2.16+commit.65e0fd7d
v0.8.29-nightly.2025.2.15+commit.80eb5279
v0.8.29-nightly.2025.2.14+commit.868154c5
v0.8.29-nightly.2025.2.13+commit.9005353d
v0.8.29-nightly.2025.2.12+commit.cfd32a3f
v0.8.29-nightly.2025.2.11+commit.5be188c8
v0.8.29-nightly.2025.2.10+commit.c3152941
v0.8.29-nightly.2025.2.7+commit.9ff6d268
v0.8.29-nightly.2025.2.6+commit.03ad799e
v0.8.29-nightly.2025.2.5+commit.a1506a38
v0.8.29-nightly.2025.2.4+commit.46ab9c50
v0.8.29-nightly.2025.2.3+commit.eb3b7216
v0.8.29-nightly.2025.2.1+commit.278b9e5d
v0.8.29-nightly.2025.1.31+commit.170b86fa
v0.8.29-nightly.2025.1.30+commit.21fdda69
v0.8.29-nightly.2025.1.29+commit.f632ec84
v0.8.29-nightly.2025.1.28+commit.8c54f4db
v0.8.29-nightly.2025.1.27+commit.e1e33b2c
v0.8.29-nightly.2025.1.24+commit.ee297e4e
v0.8.29-nightly.2025.1.23+commit.0b1b9025
v0.8.29-nightly.2025.1.22+commit.6daaff77
v0.8.29-nightly.2025.1.20+commit.d750b9df
v0.8.29-nightly.2025.1.18+commit.94121658
v0.8.29-nightly.2025.1.17+commit.fc45ba09
v0.8.29-nightly.2025.1.15+commit.71988f75
v0.8.29-nightly.2025.1.14+commit.ed71f949
v0.8.29-nightly.2025.1.9+commit.6355ff69
v0.8.29-nightly.2025.1.6+commit.3c94682f
v0.8.29-nightly.2024.12.20+commit.13943d9c
v0.8.29-nightly.2024.12.19+commit.55c65789
v0.8.29-nightly.2024.12.18+commit.2fe4769f
v0.8.29-nightly.2024.12.17+commit.6c28421e
v0.8.29-nightly.2024.12.16+commit.10fbfca1
v0.8.29-nightly.2024.12.13+commit.03e27398
v0.8.29-nightly.2024.12.12+commit.9ad66603
v0.8.29-nightly.2024.12.10+commit.ea52aca0
v0.8.29-nightly.2024.12.9+commit.a62a4550
v0.8.29-nightly.2024.12.6+commit.953a4ca3
v0.8.29-nightly.2024.12.5+commit.32ae8216
v0.8.29-nightly.2024.12.4+commit.8da621c4
v0.8.29-nightly.2024.12.3+commit.df91531c
v0.8.29-nightly.2024.12.2+commit.6b000bde
v0.8.29-nightly.2024.11.29+commit.b4ecc58b
v0.8.29-nightly.2024.11.28+commit.1cce9744
v0.8.29-nightly.2024.11.27+commit.8ea8efd9
v0.8.29-nightly.2024.11.25+commit.6fc4c033
v0.8.29-nightly.2024.11.22+commit.46e1f81b
v0.8.29-nightly.2024.11.21+commit.72795fbf
v0.8.29-nightly.2024.11.20+commit.4e954a31
v0.8.29-nightly.2024.11.11+commit.b0d991bd
v0.8.29-nightly.2024.11.8+commit.323ad934
v0.8.29-nightly.2024.10.31+commit.fca0bd31
v0.8.29-nightly.2024.10.30+commit.654ce6b3
v0.8.29-nightly.2024.10.29+commit.2486a8c4
v0.8.29-nightly.2024.10.25+commit.879d8e69
v0.8.29-nightly.2024.10.24+commit.a83ea408
v0.8.29-nightly.2024.10.23+commit.66a1edb0
v0.8.29-nightly.2024.10.22+commit.b16d8119
v0.8.29-nightly.2024.10.21+commit.abc46f30
v0.8.29-nightly.2024.10.17+commit.cb576b1a
v0.8.29-nightly.2024.10.16+commit.39943862
v0.8.29-nightly.2024.10.15+commit.7264b475
v0.8.29-nightly.2024.10.14+commit.1c0695f6
v0.8.29-nightly.2024.10.13+commit.56902580
v0.8.29-nightly.2024.10.12+commit.589f8b1e
v0.8.29-nightly.2024.10.11+commit.73b7dcd6
v0.8.29-nightly.2024.10.9+commit.d6fa8491
v0.8.29-nightly.2024.1.21+commit.3edd2e17
v0.8.28+commit.7893614a
v0.8.28-nightly.2024.10.8+commit.fde19a5b
v0.8.28-nightly.2024.10.7+commit.0e02c469
v0.8.28-nightly.2024.10.2+commit.256e9528
v0.8.28-nightly.2024.10.1+commit.4ab3e361
v0.8.28-nightly.2024.9.30+commit.a8a679d1
v0.8.28-nightly.2024.9.27+commit.4ba39cbe
v0.8.28-nightly.2024.9.26+commit.f369cdd0
v0.8.28-nightly.2024.9.25+commit.3ee5f2df
v0.8.28-nightly.2024.9.24+commit.bd733546
v0.8.28-nightly.2024.9.23+commit.60cea4d7
v0.8.28-nightly.2024.9.20+commit.33b67f0a
v0.8.28-nightly.2024.9.19+commit.7d331510
v0.8.28-nightly.2024.9.18+commit.ba4d7684
v0.8.28-nightly.2024.9.17+commit.154fc1df
v0.8.28-nightly.2024.9.16+commit.322d6561
v0.8.28-nightly.2024.9.11+commit.4049f51e
v0.8.28-nightly.2024.9.10+commit.6ef580cd
v0.8.28-nightly.2024.9.9+commit.62909dec
v0.8.28-nightly.2024.9.4+commit.09e9aa65
v0.8.27+commit.40a35a09
v0.8.27-nightly.2024.9.3+commit.3c3da637
v0.8.27-nightly.2024.9.2+commit.d6f95196
v0.8.27-nightly.2024.8.30+commit.40e005be
v0.8.27-nightly.2024.8.29+commit.ae1c3d48
v0.8.27-nightly.2024.8.28+commit.9e8369fa
v0.8.27-nightly.2024.8.27+commit.a9b9c7da
v0.8.27-nightly.2024.8.26+commit.431911d7
v0.8.27-nightly.2024.8.15+commit.60b2b033
v0.8.27-nightly.2024.8.14+commit.add191dc
v0.8.27-nightly.2024.8.13+commit.5647d991
v0.8.27-nightly.2024.8.12+commit.6d3340af
v0.8.27-nightly.2024.8.9+commit.55a5fd9b
v0.8.27-nightly.2024.8.8+commit.f99c2392
v0.8.27-nightly.2024.8.6+commit.c92b32f6
v0.8.27-nightly.2024.8.5+commit.5dbaa13b
v0.8.27-nightly.2024.8.2+commit.7094e307
v0.8.27-nightly.2024.8.1+commit.1bb78727
v0.8.27-nightly.2024.7.31+commit.72451086
v0.8.27-nightly.2024.7.30+commit.e73ab6a6
v0.8.27-nightly.2024.7.25+commit.065c2c3d
v0.8.27-nightly.2024.7.24+commit.b4c395b8
v0.8.27-nightly.2024.7.23+commit.35231a1b
v0.8.27-nightly.2024.7.20+commit.17c36142
v0.8.27-nightly.2024.7.19+commit.81a05f37
v0.8.27-nightly.2024.7.18+commit.8e74c486
v0.8.27-nightly.2024.7.17+commit.91b1254c
v0.8.27-nightly.2024.7.16+commit.9d90b391
v0.8.27-nightly.2024.7.15+commit.e0ab0f28
v0.8.27-nightly.2024.7.12+commit.64facd49
v0.8.27-nightly.2024.7.10+commit.e9d42394
v0.8.27-nightly.2024.7.9+commit.5e9d38e9
v0.8.27-nightly.2024.7.2+commit.d42f92bd
v0.8.27-nightly.2024.7.1+commit.8c6fed72
v0.8.27-nightly.2024.6.28+commit.f3f77208
v0.8.27-nightly.2024.6.26+commit.31d87b02
v0.8.27-nightly.2024.6.25+commit.c967d468
v0.8.27-nightly.2024.6.21+commit.ce4be6e1
v0.8.27-nightly.2024.6.20+commit.7711a8ec
v0.8.27-nightly.2024.6.17+commit.d0190e1d
v0.8.27-nightly.2024.6.14+commit.acfc3dae
v0.8.27-nightly.2024.6.13+commit.e5d60a2b
v0.8.27-nightly.2024.6.11+commit.07f25f56
v0.8.27-nightly.2024.6.6+commit.0f982266
v0.8.27-nightly.2024.6.5+commit.dd4f189a
v0.8.27-nightly.2024.6.4+commit.5da0f474
v0.8.27-nightly.2024.6.3+commit.cb1d21a9
v0.8.27-nightly.2024.5.30+commit.b849b327
v0.8.27-nightly.2024.5.29+commit.12b404f8
v0.8.27-nightly.2024.5.28+commit.15b99ca8
v0.8.27-nightly.2024.5.27+commit.318e036b
v0.8.27-nightly.2024.5.24+commit.a5b74ca6
v0.8.27-nightly.2024.5.23+commit.3251a03b
v0.8.27-nightly.2024.5.22+commit.f162db00
v0.8.26+commit.8a97fa7a
v0.8.26-nightly.2024.5.21+commit.8a97fa7a
v0.8.26-nightly.2024.5.20+commit.cd6f2e75
v0.8.26-nightly.2024.5.17+commit.028cf330
v0.8.26-nightly.2024.5.16+commit.de002446
v0.8.26-nightly.2024.5.15+commit.f9c8ab03
v0.8.26-nightly.2024.5.14+commit.59cc93e5
v0.8.26-nightly.2024.5.13+commit.ff81bac4
v0.8.26-nightly.2024.5.10+commit.e202d30d
v0.8.26-nightly.2024.5.8+commit.cfff77cf
v0.8.26-nightly.2024.5.7+commit.5091b5bb
v0.8.26-nightly.2024.5.2+commit.ae79d13a
v0.8.26-nightly.2024.4.30+commit.5afa25ea
v0.8.26-nightly.2024.4.29+commit.d74b8af7
v0.8.26-nightly.2024.4.26+commit.2c3fc909
v0.8.26-nightly.2024.4.25+commit.24e3c308
v0.8.26-nightly.2024.4.23+commit.ebdce269
v0.8.26-nightly.2024.4.22+commit.272892e9
v0.8.26-nightly.2024.4.19+commit.2e8cd3f5
v0.8.26-nightly.2024.4.18+commit.00b2e54e
v0.8.26-nightly.2024.4.17+commit.39af4492
v0.8.26-nightly.2024.4.16+commit.30d78788
v0.8.26-nightly.2024.4.15+commit.7f0a083a
v0.8.26-nightly.2024.4.12+commit.6c7e686d
v0.8.26-nightly.2024.4.9+commit.ae9bcabd
v0.8.26-nightly.2024.4.8+commit.2bc657ac
v0.8.26-nightly.2024.4.4+commit.3d7b3d94
v0.8.26-nightly.2024.4.3+commit.6040a521
v0.8.26-nightly.2024.4.2+commit.28e91a49
v0.8.26-nightly.2024.3.26+commit.06fc835c
v0.8.26-nightly.2024.3.15+commit.afda6984
v0.8.25+commit.b61c2a91
v0.8.25-nightly.2024.1.30+commit.4577aebf
v0.8.25-nightly.2024.1.26+commit.4edbaf1e
v0.8.24+commit.e11b9ed9
v0.8.24-nightly.2024.1.25+commit.7e7c45cb
v0.8.24-nightly.2024.1.22+commit.01cb85fd
v0.8.24-nightly.2024.1.20+commit.43c39103
v0.8.24-nightly.2024.1.18+commit.ec563a12
v0.8.24-nightly.2024.1.17+commit.9c9eddb5
v0.8.24-nightly.2024.1.15+commit.5aace4f3
v0.8.24-nightly.2024.1.12+commit.1473ce21
v0.8.24-nightly.2024.1.10+commit.fbbac9c1
v0.8.24-nightly.2024.1.9+commit.814225e6
v0.8.24-nightly.2024.1.6+commit.c78f9652
v0.8.24-nightly.2024.1.5+commit.2348552a
v0.8.24-nightly.2023.12.18+commit.92f383d8
v0.8.24-nightly.2023.12.13+commit.1b5c6f66
v0.8.24-nightly.2023.12.12+commit.cdf2f5ec
v0.8.24-nightly.2023.12.5+commit.557d567a
v0.8.24-nightly.2023.12.4+commit.7e90ad00
v0.8.24-nightly.2023.12.1+commit.c3af02c2
v0.8.24-nightly.2023.11.30+commit.3d7d8aad
v0.8.24-nightly.2023.11.29+commit.e658eebc
v0.8.24-nightly.2023.11.28+commit.4b293808
v0.8.24-nightly.2023.11.27+commit.7b269d16
v0.8.24-nightly.2023.11.23+commit.efed3b23
v0.8.24-nightly.2023.11.10+commit.58811f13
v0.8.24-nightly.2023.11.8+commit.90b046aa
v0.8.23+commit.f704f362
v0.8.23-nightly.2023.11.7+commit.cb93e6e9
v0.8.23-nightly.2023.11.6+commit.f70bd949
v0.8.23-nightly.2023.10.31+commit.7df949ed
v0.8.23-nightly.2023.10.30+commit.ad3caa7f
v0.8.23-nightly.2023.10.26+commit.d8de97d0
v0.8.23-nightly.2023.10.25+commit.28ee5d3f
v0.8.22+commit.4fc1097e
v0.8.22-nightly.2023.10.24+commit.c7e52122
v0.8.22-nightly.2023.10.23+commit.1b5775ac
v0.8.22-nightly.2023.10.22+commit.b187d065
v0.8.22-nightly.2023.10.19+commit.ddb0d895
v0.8.22-nightly.2023.10.17+commit.766efb3a
v0.8.22-nightly.2023.10.16+commit.e98f174d
v0.8.22-nightly.2023.10.13+commit.3dab116e
v0.8.22-nightly.2023.10.10+commit.88208707
v0.8.22-nightly.2023.10.9+commit.b12d8fa1
v0.8.22-nightly.2023.10.4+commit.b54e7207
v0.8.22-nightly.2023.10.2+commit.72671d6c
v0.8.22-nightly.2023.9.29+commit.fe1f9c64
v0.8.22-nightly.2023.9.19+commit.cc7a14a6
v0.8.22-nightly.2023.9.18+commit.dc44f8ad
v0.8.22-nightly.2023.9.14+commit.020b5968
v0.8.22-nightly.2023.9.13+commit.9bce5f91
v0.8.22-nightly.2023.9.11+commit.64a0f627
v0.8.22-nightly.2023.9.5+commit.16ae76ca
v0.8.22-nightly.2023.9.4+commit.e4396859
v0.8.22-nightly.2023.8.29+commit.df03f141
v0.8.22-nightly.2023.8.28+commit.26912e0e
v0.8.22-nightly.2023.8.23+commit.37e18612
v0.8.22-nightly.2023.8.22+commit.60b18a13
v0.8.22-nightly.2023.8.21+commit.c96db510
v0.8.22-nightly.2023.8.18+commit.d2f86ffb
v0.8.22-nightly.2023.8.17+commit.ef5f1318
v0.8.22-nightly.2023.8.16+commit.69e5b634
v0.8.22-nightly.2023.8.15+commit.579259d6
v0.8.22-nightly.2023.8.14+commit.d8cc2c62
v0.8.22-nightly.2023.8.11+commit.c50c9b2c
v0.8.22-nightly.2023.8.9+commit.3edf91ad
v0.8.22-nightly.2023.8.7+commit.e357b8bc
v0.8.22-nightly.2023.8.3+commit.51171257
v0.8.22-nightly.2023.8.2+commit.ead0615c
v0.8.22-nightly.2023.8.1+commit.2c702556
v0.8.22-nightly.2023.7.27+commit.45123298
v0.8.22-nightly.2023.7.26+commit.80d0a979
v0.8.22-nightly.2023.7.25+commit.95beef40
v0.8.22-nightly.2023.7.24+commit.83fe3d40
v0.8.22-nightly.2023.7.21+commit.89407d25
v0.8.22-nightly.2023.7.20+commit.f466e1ef
v0.8.22-nightly.2023.7.19+commit.ceb65876
v0.8.21+commit.d9974bed
v0.8.21-nightly.2023.7.18+commit.1acebf78
v0.8.21-nightly.2023.7.17+commit.4c4410e0
v0.8.21-nightly.2023.7.14+commit.11be2f48
v0.8.21-nightly.2023.7.13+commit.ebc2bc9e
v0.8.21-nightly.2023.7.12+commit.69c034b1
v0.8.21-nightly.2023.7.11+commit.b29d8a42
v0.8.21-nightly.2023.7.10+commit.b583e9e6
v0.8.21-nightly.2023.7.3+commit.5d7533b5
v0.8.21-nightly.2023.6.30+commit.2f451a18
v0.8.21-nightly.2023.6.28+commit.30cd1a0f
v0.8.21-nightly.2023.6.27+commit.3bb492a2
v0.8.21-nightly.2023.6.26+commit.34d2383f
v0.8.21-nightly.2023.6.23+commit.aca4c86a
v0.8.21-nightly.2023.6.21+commit.b26090c2
v0.8.21-nightly.2023.6.20+commit.dc7cda18
v0.8.21-nightly.2023.6.19+commit.3ecf9680
v0.8.21-nightly.2023.6.14+commit.374a6fd5
v0.8.21-nightly.2023.6.12+commit.53c305ea
v0.8.21-nightly.2023.6.7+commit.facc3809
v0.8.21-nightly.2023.6.6+commit.09038ce4
v0.8.21-nightly.2023.6.5+commit.f1d2eda7
v0.8.21-nightly.2023.6.2+commit.dcecf00e
v0.8.21-nightly.2023.5.31+commit.14d2ae2d
v0.8.21-nightly.2023.5.30+commit.3eedc635
v0.8.21-nightly.2023.5.29+commit.4449f07d
v0.8.21-nightly.2023.5.26+commit.38468d03
v0.8.21-nightly.2023.5.25+commit.8c7404f6
v0.8.21-nightly.2023.5.24+commit.6db4f182
v0.8.21-nightly.2023.5.22+commit.02a07fdf
v0.8.21-nightly.2023.5.18+commit.9eaa5ceb
v0.8.21-nightly.2023.5.17+commit.574d454b
v0.8.21-nightly.2023.5.16+commit.aa9e2502
v0.8.21-nightly.2023.5.15+commit.1250ee77
v0.8.21-nightly.2023.5.12+commit.3f2cde9b
v0.8.21-nightly.2023.5.11+commit.0a0c3895
v0.8.21-nightly.2023.5.10+commit.f07c8b1f
v0.8.20+commit.a1b79de6
v0.8.20-nightly.2023.5.9+commit.44a30e47
v0.8.20-nightly.2023.5.8+commit.2da0a861
v0.8.20-nightly.2023.5.6+commit.29751849
v0.8.20-nightly.2023.5.5+commit.102f18b2
v0.8.20-nightly.2023.5.4+commit.0f40bcc0
v0.8.20-nightly.2023.5.3+commit.385c48df
v0.8.20-nightly.2023.5.2+commit.1af6ca77
v0.8.20-nightly.2023.4.28+commit.0cb27949
v0.8.20-nightly.2023.4.27+commit.7c870c95
v0.8.20-nightly.2023.4.26+commit.302d46c1
v0.8.20-nightly.2023.4.25+commit.14c25c38
v0.8.20-nightly.2023.4.24+commit.4a8d6618
v0.8.20-nightly.2023.4.23+commit.cd5ae26e
v0.8.20-nightly.2023.4.21+commit.b75bddbd
v0.8.20-nightly.2023.4.20+commit.a297a687
v0.8.20-nightly.2023.4.18+commit.a77d4e28
v0.8.20-nightly.2023.4.17+commit.02e936ad
v0.8.20-nightly.2023.4.14+commit.e1a9446f
v0.8.20-nightly.2023.4.13+commit.5d42bb5e
v0.8.20-nightly.2023.4.12+commit.f0c0df2d
v0.8.20-nightly.2023.4.11+commit.8b4c1d33
v0.8.20-nightly.2023.4.6+commit.e29a68d3
v0.8.20-nightly.2023.4.5+commit.9e0a0af7
v0.8.20-nightly.2023.4.4+commit.7b634152
v0.8.20-nightly.2023.4.3+commit.0037693c
v0.8.19+commit.7dd6d404
v0.8.19-nightly.2023.2.16+commit.23eb9c59
v0.8.19-nightly.2023.2.15+commit.e147654f
v0.8.19-nightly.2023.2.14+commit.1b0f7af7
v0.8.19-nightly.2023.2.13+commit.7cd589ee
v0.8.19-nightly.2023.2.11+commit.e50d5651
v0.8.19-nightly.2023.2.9+commit.59f9ab4d
v0.8.19-nightly.2023.2.8+commit.d33f2734
v0.8.19-nightly.2023.2.7+commit.665bf29a
v0.8.19-nightly.2023.2.6+commit.88e44ed5
v0.8.19-nightly.2023.2.5+commit.f2bf23a0
v0.8.19-nightly.2023.2.3+commit.77640a57
v0.8.19-nightly.2023.2.1+commit.ddbef8f6
v0.8.18+commit.87f61d96
v0.8.18-nightly.2023.1.31+commit.fa4892e6
v0.8.18-nightly.2023.1.26+commit.206e7cf5
v0.8.18-nightly.2023.1.25+commit.fd9ac9ab
v0.8.18-nightly.2023.1.20+commit.d70d79af
v0.8.18-nightly.2023.1.18+commit.c195782f
v0.8.18-nightly.2023.1.17+commit.96ddc54f
v0.8.18-nightly.2023.1.16+commit.7b2f8a2e
v0.8.18-nightly.2023.1.12+commit.609f1522
v0.8.18-nightly.2023.1.9+commit.f441e132
v0.8.18-nightly.2023.1.5+commit.2e221022
v0.8.18-nightly.2023.1.4+commit.2ec6a04b
v0.8.18-nightly.2022.12.27+commit.f1d42724
v0.8.18-nightly.2022.12.21+commit.71ce291c
v0.8.18-nightly.2022.12.20+commit.32f94d45
v0.8.18-nightly.2022.12.17+commit.73fcf691
v0.8.18-nightly.2022.12.16+commit.b053359b
v0.8.18-nightly.2022.12.15+commit.c1040815
v0.8.18-nightly.2022.12.14+commit.37e935f0
v0.8.18-nightly.2022.12.9+commit.a9fe05e8
v0.8.18-nightly.2022.12.8+commit.b49dac7a
v0.8.18-nightly.2022.12.7+commit.1c8745c5
v0.8.18-nightly.2022.12.2+commit.591df042
v0.8.18-nightly.2022.12.1+commit.056c4593
v0.8.18-nightly.2022.11.30+commit.c6ee18a5
v0.8.18-nightly.2022.11.29+commit.40b24850
v0.8.18-nightly.2022.11.28+commit.7070a172
v0.8.18-nightly.2022.11.23+commit.eb2f874e
v0.8.18-nightly.2022.11.22+commit.1dd05e29
v0.8.18-nightly.2022.11.21+commit.5211d3da
v0.8.18-nightly.2022.11.17+commit.0b4b1045
v0.8.18-nightly.2022.11.16+commit.75a74cd4
v0.8.18-nightly.2022.11.14+commit.4100a59c
v0.8.18-nightly.2022.11.10+commit.310a58dd
v0.8.18-nightly.2022.11.9+commit.73e7b844
v0.8.18-nightly.2022.11.8+commit.9db2da03
v0.8.18-nightly.2022.11.7+commit.ce18dddd
v0.8.18-nightly.2022.11.3+commit.2cc6610e
v0.8.18-nightly.2022.11.2+commit.46a7ebd5
v0.8.18-nightly.2022.11.1+commit.7ac4c70c
v0.8.18-nightly.2022.10.28+commit.ff14e408
v0.8.18-nightly.2022.10.27+commit.0816b15e
v0.8.18-nightly.2022.10.26+commit.12f5612c
v0.8.18-nightly.2022.10.25+commit.799ef0ab
v0.8.18-nightly.2022.10.24+commit.84cdcec2
v0.8.17+commit.8df45f5f
v0.8.17-nightly.2022.8.24+commit.22a0c46e
v0.8.17-nightly.2022.8.22+commit.a3de6cd6
v0.8.17-nightly.2022.8.19+commit.f01a09f8
v0.8.17-nightly.2022.8.18+commit.3497e2b2
v0.8.17-nightly.2022.8.16+commit.bb41ddd7
v0.8.17-nightly.2022.8.15+commit.a0ee14f7
v0.8.17-nightly.2022.8.13+commit.a78a2bcf
v0.8.17-nightly.2022.8.12+commit.e27cb025
v0.8.17-nightly.2022.8.10+commit.3c0a7355
v0.8.17-nightly.2022.8.9+commit.6b60524c
v0.8.17-nightly.2022.8.8+commit.6a42da8d
v0.8.16+commit.07a7930e
v0.8.16-nightly.2022.8.5+commit.49a2db99
v0.8.16-nightly.2022.8.4+commit.19ad8b11
v0.8.16-nightly.2022.8.3+commit.82e5a110
v0.8.16-nightly.2022.7.28+commit.d5a78b18
v0.8.16-nightly.2022.7.27+commit.72f19072
v0.8.16-nightly.2022.7.26+commit.ce5da7db
v0.8.16-nightly.2022.7.25+commit.9f34322f
v0.8.16-nightly.2022.7.14+commit.800088e3
v0.8.16-nightly.2022.7.13+commit.454603e1
v0.8.16-nightly.2022.7.12+commit.d003400c
v0.8.16-nightly.2022.7.11+commit.e7c5f044
v0.8.16-nightly.2022.7.8+commit.8d6b20f7
v0.8.16-nightly.2022.7.6+commit.b6f11b33
v0.8.16-nightly.2022.7.5+commit.c8aed8c1
v0.8.16-nightly.2022.7.4+commit.a53f15f4
v0.8.16-nightly.2022.7.1+commit.5de51204
v0.8.16-nightly.2022.6.30+commit.48669b4b
v0.8.16-nightly.2022.6.29+commit.05496064
v0.8.16-nightly.2022.6.27+commit.b70e064e
v0.8.16-nightly.2022.6.23+commit.3ed9a38a
v0.8.16-nightly.2022.6.22+commit.a2a88afd
v0.8.16-nightly.2022.6.21+commit.75300c32
v0.8.16-nightly.2022.6.20+commit.c3ea8661
v0.8.16-nightly.2022.6.17+commit.be470c16
v0.8.16-nightly.2022.6.16+commit.b80f4baa
v0.8.16-nightly.2022.6.15+commit.f904bb06
v0.8.15+commit.e14f2714
v0.8.15-nightly.2022.6.14+commit.dccc06cc
v0.8.15-nightly.2022.6.13+commit.82e5339d
v0.8.15-nightly.2022.6.10+commit.efcbc79b
v0.8.15-nightly.2022.6.9+commit.80f6a13d
v0.8.15-nightly.2022.6.8+commit.9b220a20
v0.8.15-nightly.2022.6.7+commit.8c87f58f
v0.8.15-nightly.2022.6.6+commit.3948391c
v0.8.15-nightly.2022.6.2+commit.035f6abb
v0.8.15-nightly.2022.6.1+commit.3f84837e
v0.8.15-nightly.2022.5.31+commit.baf56aff
v0.8.15-nightly.2022.5.27+commit.095cc647
v0.8.15-nightly.2022.5.25+commit.fdc3c8ee
v0.8.15-nightly.2022.5.23+commit.21591531
v0.8.15-nightly.2022.5.20+commit.02567fd3
v0.8.15-nightly.2022.5.19+commit.0cb95902
v0.8.15-nightly.2022.5.18+commit.de7daaa2
v0.8.14+commit.80d49f37
v0.8.14-nightly.2022.5.17+commit.80d49f37
v0.8.14-nightly.2022.5.13+commit.a3bd01d9
v0.8.14-nightly.2022.5.12+commit.aafda389
v0.8.14-nightly.2022.5.11+commit.0c0ff4fc
v0.8.14-nightly.2022.5.10+commit.9f6d3dea
v0.8.14-nightly.2022.5.9+commit.463e4175
v0.8.14-nightly.2022.5.5+commit.1dba6aaf
v0.8.14-nightly.2022.5.4+commit.84c64edf
v0.8.14-nightly.2022.5.2+commit.3e3e73e3
v0.8.14-nightly.2022.4.28+commit.d55b84ff
v0.8.14-nightly.2022.4.25+commit.fbecdbe7
v0.8.14-nightly.2022.4.14+commit.55917405
v0.8.14-nightly.2022.4.13+commit.25923c1f
v0.8.14-nightly.2022.4.11+commit.9e92c7a4
v0.8.14-nightly.2022.4.10+commit.0b811943
v0.8.14-nightly.2022.4.8+commit.d9c6ceca
v0.8.14-nightly.2022.4.7+commit.15c2a33e
v0.8.14-nightly.2022.4.6+commit.31b54857
v0.8.14-nightly.2022.4.5+commit.34dd30d7
v0.8.14-nightly.2022.4.4+commit.fd763fa6
v0.8.14-nightly.2022.3.24+commit.c4909e99
v0.8.14-nightly.2022.3.23+commit.b35cda59
v0.8.14-nightly.2022.3.21+commit.43f29c00
v0.8.14-nightly.2022.3.17+commit.430ecb6e
v0.8.14-nightly.2022.3.16+commit.10b581b8
v0.8.13+commit.abaa5c0e
v0.8.13-nightly.2022.3.15+commit.724af73f
v0.8.13-nightly.2022.3.14+commit.353759c1
v0.8.13-nightly.2022.3.11+commit.26963775
v0.8.13-nightly.2022.3.10+commit.4263b893
v0.8.13-nightly.2022.3.9+commit.bebdccca
v0.8.13-nightly.2022.3.7+commit.145186f6
v0.8.13-nightly.2022.3.4+commit.198b7053
v0.8.13-nightly.2022.3.3+commit.999a53c9
v0.8.13-nightly.2022.3.2+commit.ebefb5d9
v0.8.13-nightly.2022.3.1+commit.2bcb0275
v0.8.13-nightly.2022.2.28+commit.466251b5
v0.8.13-nightly.2022.2.24+commit.1aacb67a
v0.8.13-nightly.2022.2.23+commit.e7d93f83
v0.8.13-nightly.2022.2.22+commit.47d77931
v0.8.13-nightly.2022.2.21+commit.5db29076
v0.8.13-nightly.2022.2.17+commit.daad9a42
v0.8.13-nightly.2022.2.16+commit.da50176b
v0.8.12+commit.f00d7308
v0.8.12-nightly.2022.2.15+commit.16983848
v0.8.12-nightly.2022.2.14+commit.b3ccc013
v0.8.12-nightly.2022.2.10+commit.1210c3e6
v0.8.12-nightly.2022.2.9+commit.5539a745
v0.8.12-nightly.2022.2.8+commit.5c3bcb6c
v0.8.12-nightly.2022.2.7+commit.0e93456e
v0.8.12-nightly.2022.2.4+commit.32d64ce6
v0.8.12-nightly.2022.2.3+commit.2b141c23
v0.8.12-nightly.2022.2.1+commit.a05d2b35
v0.8.12-nightly.2022.1.31+commit.d839624f
v0.8.12-nightly.2022.1.29+commit.ef8911a6
v0.8.12-nightly.2022.1.27+commit.7a40785b
v0.8.12-nightly.2022.1.26+commit.597426bd
v0.8.12-nightly.2022.1.25+commit.2725788c
v0.8.12-nightly.2022.1.21+commit.3f401ebd
v0.8.12-nightly.2022.1.20+commit.40d3223b
v0.8.12-nightly.2022.1.19+commit.0b9ab33f
v0.8.12-nightly.2022.1.18+commit.a07b3ec7
v0.8.12-nightly.2022.1.17+commit.79e9d619
v0.8.12-nightly.2022.1.14+commit.756ae673
v0.8.12-nightly.2022.1.13+commit.7c1daa50
v0.8.12-nightly.2022.1.12+commit.bc4436c5
v0.8.12-nightly.2022.1.11+commit.a7119699
v0.8.12-nightly.2022.1.10+commit.10c954fd
v0.8.12-nightly.2022.1.6+commit.c3b4292d
v0.8.12-nightly.2022.1.5+commit.b6a203a9
v0.8.12-nightly.2022.1.4+commit.b892851d
v0.8.12-nightly.2022.1.3+commit.c28f85f1
v0.8.12-nightly.2021.12.30+commit.6849774b
v0.8.12-nightly.2021.12.29+commit.692614df
v0.8.12-nightly.2021.12.22+commit.b28cd00a
v0.8.12-nightly.2021.12.21+commit.15826826
v0.8.12-nightly.2021.12.20+commit.b65e0933
v0.8.11+commit.d7f03943
v0.8.11-nightly.2021.12.16+commit.10289fbc
v0.8.11-nightly.2021.12.15+commit.1822261d
v0.8.11-nightly.2021.12.3+commit.c76a6bdb
v0.8.11-nightly.2021.12.1+commit.dcef56a5
v0.8.11-nightly.2021.11.30+commit.c04fca7c
v0.8.11-nightly.2021.11.29+commit.cb610b50
v0.8.11-nightly.2021.11.25+commit.e0c85c6f
v0.8.11-nightly.2021.11.23+commit.71f8576b
v0.8.11-nightly.2021.11.22+commit.9b6a687a
v0.8.11-nightly.2021.11.18+commit.2aeeef83
v0.8.11-nightly.2021.11.16+commit.e5579526
v0.8.11-nightly.2021.11.11+commit.73344204
v0.8.11-nightly.2021.11.10+commit.9240368e
v0.8.11-nightly.2021.11.9+commit.19159b96
v0.8.10+commit.fc410830
v0.8.10-nightly.2021.11.8+commit.f095442d
v0.8.10-nightly.2021.11.5+commit.2f720f22
v0.8.10-nightly.2021.11.4+commit.dd0ff194
v0.8.10-nightly.2021.11.3+commit.4a49e6e4
v0.8.10-nightly.2021.11.2+commit.a7b13782
v0.8.10-nightly.2021.11.1+commit.5eb97fa6
v0.8.10-nightly.2021.10.29+commit.408bd5fa
v0.8.10-nightly.2021.10.28+commit.558d9d45
v0.8.10-nightly.2021.10.27+commit.cede3693
v0.8.10-nightly.2021.10.26+commit.453f404f
v0.8.10-nightly.2021.10.25+commit.e6e30f82
v0.8.10-nightly.2021.10.22+commit.3774955d
v0.8.10-nightly.2021.10.20+commit.ef21e43f
v0.8.10-nightly.2021.10.19+commit.863a0d3b
v0.8.10-nightly.2021.10.18+commit.6bca1549
v0.8.10-nightly.2021.10.16+commit.fdf3b96e
v0.8.10-nightly.2021.10.15+commit.7f0771f8
v0.8.10-nightly.2021.10.14+commit.1e630fc5
v0.8.10-nightly.2021.10.13+commit.1deda33e
v0.8.10-nightly.2021.10.12+commit.a79120fe
v0.8.10-nightly.2021.10.11+commit.b0a5b92f
v0.8.10-nightly.2021.10.7+commit.b343e132
v0.8.10-nightly.2021.10.6+commit.0549c42c
v0.8.10-nightly.2021.10.5+commit.9c6ca4f4
v0.8.10-nightly.2021.10.4+commit.9d6eaa7a
v0.8.10-nightly.2021.10.1+commit.d10e668f
v0.8.10-nightly.2021.9.30+commit.0e7e936f
v0.8.10-nightly.2021.9.29+commit.7a9f4815
v0.8.9+commit.e5eed63a
v0.8.8+commit.dddeac2f
v0.8.8-nightly.2021.9.28+commit.89d959d7
v0.8.8-nightly.2021.9.27+commit.c3ef27f3
v0.8.8-nightly.2021.9.24+commit.3c8846e6
v0.8.8-nightly.2021.9.23+commit.55467c1c
v0.8.8-nightly.2021.9.22+commit.72fc3449
v0.8.8-nightly.2021.9.21+commit.fc954367
v0.8.8-nightly.2021.9.20+commit.2c3322cb
v0.8.8-nightly.2021.9.17+commit.d7ddfcc6
v0.8.8-nightly.2021.9.16+commit.7877758c
v0.8.8-nightly.2021.9.15+commit.c1070fab
v0.8.8-nightly.2021.9.14+commit.0fa24c78
v0.8.8-nightly.2021.9.13+commit.49cde9d4
v0.8.8-nightly.2021.9.9+commit.dea1b9ec
v0.8.8-nightly.2021.9.8+commit.dae6b53c
v0.8.8-nightly.2021.9.7+commit.6feed460
v0.8.8-nightly.2021.9.6+commit.11a85059
v0.8.8-nightly.2021.9.3+commit.8447b32d
v0.8.8-nightly.2021.9.2+commit.7f137d35
v0.8.8-nightly.2021.9.1+commit.70fe0c65
v0.8.8-nightly.2021.8.31+commit.1e334a89
v0.8.8-nightly.2021.8.30+commit.78afd71a
v0.8.8-nightly.2021.8.27+commit.a3d8da25
v0.8.8-nightly.2021.8.26+commit.7df33f0d
v0.8.8-nightly.2021.8.25+commit.208cf6a3
v0.8.8-nightly.2021.8.24+commit.7a0295ec
v0.8.8-nightly.2021.8.23+commit.a39eb7ae
v0.8.8-nightly.2021.8.20+commit.6b7857d5
v0.8.8-nightly.2021.8.19+commit.26207968
v0.8.8-nightly.2021.8.18+commit.cef0f1b9
v0.8.8-nightly.2021.8.17+commit.729db521
v0.8.8-nightly.2021.8.16+commit.97b4ff15
v0.8.8-nightly.2021.8.12+commit.4fdf7db0
v0.8.8-nightly.2021.8.11+commit.cc4e24c2
v0.8.7+commit.e28d00a7
v0.8.7-nightly.2021.8.10+commit.13b26949
v0.8.7-nightly.2021.8.9+commit.74c804d8
v0.8.7-nightly.2021.8.6+commit.ce0e0c48
v0.8.7-nightly.2021.8.5+commit.a532df20
v0.8.7-nightly.2021.8.4+commit.2d5b9036
v0.8.7-nightly.2021.8.3+commit.ae519c12
v0.8.7-nightly.2021.8.2+commit.e9cab0ff
v0.8.7-nightly.2021.7.29+commit.5ff0811b
v0.8.7-nightly.2021.7.28+commit.1794e1c8
v0.8.7-nightly.2021.7.27+commit.c018cdf4
v0.8.7-nightly.2021.7.26+commit.f97fe813
v0.8.7-nightly.2021.7.25+commit.a2ce4616
v0.8.7-nightly.2021.7.21+commit.6d6c9e6e
v0.8.7-nightly.2021.7.20+commit.d655a3c9
v0.8.7-nightly.2021.7.15+commit.3d26d47d
v0.8.7-nightly.2021.7.14+commit.90f77f8c
v0.8.7-nightly.2021.7.13+commit.57d32ca2
v0.8.7-nightly.2021.7.12+commit.ef6ad57c
v0.8.7-nightly.2021.7.8+commit.c3fa520c
v0.8.7-nightly.2021.7.7+commit.46514ffa
v0.8.7-nightly.2021.7.6+commit.69233c37
v0.8.7-nightly.2021.7.5+commit.19b217dc
v0.8.7-nightly.2021.7.2+commit.f6cb933f
v0.8.7-nightly.2021.7.1+commit.98e1dee4
v0.8.7-nightly.2021.6.30+commit.8a6a330d
v0.8.7-nightly.2021.6.29+commit.eaac16c7
v0.8.7-nightly.2021.6.28+commit.d91dc995
v0.8.7-nightly.2021.6.23+commit.cbf1c3ae
v0.8.7-nightly.2021.6.22+commit.9cf6021d
v0.8.6+commit.11564f7e
v0.8.6-nightly.2021.6.21+commit.a96114b3
v0.8.6-nightly.2021.6.17+commit.11281586
v0.8.6-nightly.2021.6.16+commit.61468301
v0.8.6-nightly.2021.6.15+commit.e7bf1cc7
v0.8.6-nightly.2021.6.14+commit.b2ffa910
v0.8.5+commit.a4f2e591
v0.8.5-nightly.2021.6.10+commit.a4f2e591
v0.8.5-nightly.2021.6.9+commit.98e7b61a
v0.8.5-nightly.2021.6.8+commit.e77e9e44
v0.8.5-nightly.2021.6.7+commit.7d8a4e63
v0.8.5-nightly.2021.6.4+commit.1f8f1a3d
v0.8.5-nightly.2021.6.3+commit.1638b210
v0.8.5-nightly.2021.6.1+commit.4cbf9ff7
v0.8.5-nightly.2021.5.31+commit.7d1df951
v0.8.5-nightly.2021.5.27+commit.2f0df8f0
v0.8.5-nightly.2021.5.26+commit.a3634934
v0.8.5-nightly.2021.5.25+commit.6640fb8c
v0.8.5-nightly.2021.5.24+commit.c5031799
v0.8.5-nightly.2021.5.21+commit.29c8f282
v0.8.5-nightly.2021.5.20+commit.13388e28
v0.8.5-nightly.2021.5.19+commit.d07c85db
v0.8.5-nightly.2021.5.18+commit.dac24294
v0.8.5-nightly.2021.5.17+commit.21af5408
v0.8.5-nightly.2021.5.14+commit.f58d5873
v0.8.5-nightly.2021.5.13+commit.324caef5
v0.8.5-nightly.2021.5.12+commit.98e2b4e5
v0.8.5-nightly.2021.5.11+commit.eb991775
v0.8.5-nightly.2021.5.10+commit.643140e2
v0.8.5-nightly.2021.5.7+commit.5d070c5b
v0.8.5-nightly.2021.5.6+commit.518629a8
v0.8.5-nightly.2021.5.5+commit.4c7b61d8
v0.8.5-nightly.2021.5.4+commit.1d1175c2
v0.8.5-nightly.2021.5.3+commit.fe4822a1
v0.8.5-nightly.2021.4.29+commit.f1d58c54
v0.8.5-nightly.2021.4.28+commit.850c25bf
v0.8.5-nightly.2021.4.27+commit.c7944637
v0.8.5-nightly.2021.4.26+commit.2e99a56b
v0.8.5-nightly.2021.4.24+commit.eed0bf58
v0.8.5-nightly.2021.4.23+commit.173a5118
v0.8.5-nightly.2021.4.22+commit.f162c484
v0.8.5-nightly.2021.4.21+commit.85274304
v0.8.4+commit.c7e474f2
v0.8.4-nightly.2021.4.20+commit.cf7f814a
v0.8.4-nightly.2021.4.19+commit.159d6f9e
v0.8.4-nightly.2021.4.16+commit.f9b23ca8
v0.8.4-nightly.2021.4.14+commit.69411436
v0.8.4-nightly.2021.4.13+commit.f188f3d9
v0.8.4-nightly.2021.4.12+commit.0289994d
v0.8.4-nightly.2021.4.8+commit.124db22f
v0.8.4-nightly.2021.4.6+commit.a5cae64a
v0.8.4-nightly.2021.4.1+commit.5433a640
v0.8.4-nightly.2021.3.31+commit.b2555eac
v0.8.4-nightly.2021.3.30+commit.851051c6
v0.8.4-nightly.2021.3.29+commit.2346ec1c
v0.8.4-nightly.2021.3.26+commit.c37bf893
v0.8.4-nightly.2021.3.25+commit.d75a132f
v0.8.4-nightly.2021.3.24+commit.6eac77ae
v0.8.3+commit.8d00100c
v0.8.3-nightly.2021.3.22+commit.54cea090
v0.8.3-nightly.2021.3.17+commit.e179d0aa
v0.8.3-nightly.2021.3.16+commit.35da404c
v0.8.3-nightly.2021.3.15+commit.ae1b321a
v0.8.3-nightly.2021.3.12+commit.ccd9de13
v0.8.3-nightly.2021.3.11+commit.0e22d0bd
v0.8.3-nightly.2021.3.10+commit.23f03e1b
v0.8.3-nightly.2021.3.9+commit.ad5d34df
v0.8.3-nightly.2021.3.5+commit.093ea461
v0.8.3-nightly.2021.3.4+commit.08df163a
v0.8.3-nightly.2021.3.3+commit.be564773
v0.8.2+commit.661d1103
v0.8.2-nightly.2021.3.2+commit.661d1103
v0.8.2-nightly.2021.3.1+commit.ad48b713
v0.8.2-nightly.2021.2.25+commit.44493ad4
v0.8.2-nightly.2021.2.24+commit.eacf7c1c
v0.8.2-nightly.2021.2.23+commit.1220d8df
v0.8.2-nightly.2021.2.22+commit.e75e3fc2
v0.8.2-nightly.2021.2.19+commit.6fd5ea01
v0.8.2-nightly.2021.2.18+commit.5c6633f9
v0.8.2-nightly.2021.2.12+commit.b385b41f
v0.8.2-nightly.2021.2.11+commit.003701f6
v0.8.2-nightly.2021.2.10+commit.215233d5
v0.8.2-nightly.2021.2.9+commit.9b20c984
v0.8.2-nightly.2021.2.8+commit.ec62d123
v0.8.2-nightly.2021.2.4+commit.2fb27884
v0.8.2-nightly.2021.2.3+commit.1a949e53
v0.8.2-nightly.2021.2.2+commit.358324ed
v0.8.2-nightly.2021.2.1+commit.dde6353c
v0.8.2-nightly.2021.1.28+commit.70882cc4
v0.8.2-nightly.2021.1.27+commit.49dbcba3
v0.8.1+commit.df193b15
v0.8.1-nightly.2021.1.27+commit.34fa756f
v0.8.1-nightly.2021.1.25+commit.ccdf57c9
v0.8.1-nightly.2021.1.22+commit.8a844237
v0.8.1-nightly.2021.1.21+commit.3045770a
v0.8.1-nightly.2021.1.20+commit.a75b87c8
v0.8.1-nightly.2021.1.19+commit.1df28473
v0.8.1-nightly.2021.1.18+commit.957e9995
v0.8.1-nightly.2021.1.15+commit.055c4b4d
v0.8.1-nightly.2021.1.14+commit.eaf7d7da
v0.8.1-nightly.2021.1.13+commit.50146114
v0.8.1-nightly.2021.1.12+commit.e9dcd4f8
v0.8.1-nightly.2021.1.11+commit.67d21a87
v0.8.1-nightly.2021.1.8+commit.f03245d4
v0.8.1-nightly.2021.1.7+commit.d11cf15d
v0.8.1-nightly.2021.1.6+commit.5241b7b7
v0.8.1-nightly.2021.1.4+commit.fce6d999
v0.8.1-nightly.2020.12.30+commit.0e32fa82
v0.8.1-nightly.2020.12.29+commit.86c30b4c
v0.8.1-nightly.2020.12.28+commit.8e9a5a02
v0.8.1-nightly.2020.12.22+commit.e299d8ba
v0.8.1-nightly.2020.12.21+commit.b78443ac
v0.8.1-nightly.2020.12.20+commit.67712d50
v0.8.1-nightly.2020.12.18+commit.158154ba
v0.8.1-nightly.2020.12.17+commit.8194cbb4
v0.8.1-nightly.2020.12.16+commit.2be078b4
v0.8.0+commit.c7dfd78e
v0.7.6+commit.7338295f
v0.7.6-nightly.2020.12.15+commit.17293858
v0.7.6-nightly.2020.12.14+commit.d83ce0bc
v0.7.6-nightly.2020.12.11+commit.db9aa36d
v0.7.6-nightly.2020.12.10+commit.9e4f3bad
v0.7.6-nightly.2020.12.9+commit.7e930f7b
v0.7.6-nightly.2020.12.8+commit.0d7f9ae1
v0.7.6-nightly.2020.12.7+commit.b23d9230
v0.7.6-nightly.2020.12.4+commit.3619a0a0
v0.7.6-nightly.2020.12.3+commit.a27d7707
v0.7.6-nightly.2020.12.2+commit.3cd0b252
v0.7.6-nightly.2020.12.1+commit.e10712c1
v0.7.6-nightly.2020.11.30+commit.91e67472
v0.7.6-nightly.2020.11.27+commit.887569ef
v0.7.6-nightly.2020.11.26+commit.e8843fe1
v0.7.6-nightly.2020.11.25+commit.7eb5fc31
v0.7.6-nightly.2020.11.24+commit.ae34fba4
v0.7.6-nightly.2020.11.23+commit.61425e35
v0.7.6-nightly.2020.11.21+commit.8bf455bb
v0.7.6-nightly.2020.11.20+commit.3a3303f2
v0.7.6-nightly.2020.11.19+commit.8d315ee1
v0.7.6-nightly.2020.11.18+commit.bfe87378
v0.7.5+commit.eb77ed08
v0.7.5-nightly.2020.11.17+commit.e1292380
v0.7.5-nightly.2020.11.16+commit.a97521bf
v0.7.5-nightly.2020.11.13+commit.f1846b57
v0.7.5-nightly.2020.11.12+commit.c69c7f32
v0.7.5-nightly.2020.11.11+commit.44eb63fa
v0.7.5-nightly.2020.11.10+commit.d3a016b5
v0.7.5-nightly.2020.11.9+commit.41f50365
v0.7.5-nightly.2020.11.6+commit.6fa42b5e
v0.7.5-nightly.2020.11.5+commit.f55f5c24
v0.7.5-nightly.2020.11.4+commit.5b412544
v0.7.5-nightly.2020.11.3+commit.a8045ba5
v0.7.5-nightly.2020.11.2+commit.c83d8fae
v0.7.5-nightly.2020.10.29+commit.be02db49
v0.7.5-nightly.2020.10.28+commit.f42280f5
v0.7.5-nightly.2020.10.27+commit.f1ed5100
v0.7.5-nightly.2020.10.26+commit.96c188be
v0.7.5-nightly.2020.10.23+commit.08a27b9c
v0.7.5-nightly.2020.10.22+commit.95c521a3
v0.7.5-nightly.2020.10.21+commit.38d58a45
v0.7.5-nightly.2020.10.20+commit.06394672
v0.7.5-nightly.2020.10.19+commit.58579332
v0.7.4+commit.3f05b770
v0.7.4-nightly.2020.10.18+commit.6aae7cae
v0.7.4-nightly.2020.10.16+commit.eedd12ad
v0.7.4-nightly.2020.10.15+commit.9aafb62e
v0.7.4-nightly.2020.10.14+commit.36a36caf
v0.7.4-nightly.2020.10.13+commit.8d241fec
v0.7.4-nightly.2020.10.12+commit.abfa136a
v0.7.4-nightly.2020.10.9+commit.d9215cf9
v0.7.4-nightly.2020.10.8+commit.3739b03a
v0.7.3+commit.9bfce1f6
v0.7.3-nightly.2020.10.6+commit.25d40805
v0.7.3-nightly.2020.10.2+commit.756e21a8
v0.7.3-nightly.2020.9.30+commit.3af21c92
v0.7.3-nightly.2020.9.29+commit.343c13f9
v0.7.3-nightly.2020.9.28+commit.dd5b0a71
v0.7.2+commit.51b20bc0
v0.7.2-nightly.2020.9.25+commit.b34465c5
v0.7.2-nightly.2020.9.24+commit.5711d664
v0.7.2-nightly.2020.9.23+commit.35a7d5d3
v0.7.2-nightly.2020.9.22+commit.700cc4c9
v0.7.2-nightly.2020.9.21+commit.d80a81b0
v0.7.2-nightly.2020.9.17+commit.b571fd05
v0.7.2-nightly.2020.9.16+commit.90506528
v0.7.2-nightly.2020.9.15+commit.3399570d
v0.7.2-nightly.2020.9.12+commit.38175150
v0.7.2-nightly.2020.9.11+commit.31b5102a
v0.7.2-nightly.2020.9.10+commit.0db79dbc
v0.7.2-nightly.2020.9.9+commit.95a284e5
v0.7.2-nightly.2020.9.8+commit.20233240
v0.7.2-nightly.2020.9.7+commit.38e6f272
v0.7.2-nightly.2020.9.3+commit.f9649660
v0.7.2-nightly.2020.9.2+commit.cde65224
v0.7.1+commit.f4a555be
v0.7.1-nightly.2020.9.1+commit.0d83977d
v0.7.1-nightly.2020.8.31+commit.34543e5e
v0.7.1-nightly.2020.8.28+commit.98cc1d99
v0.7.1-nightly.2020.8.27+commit.e872b1b5
v0.7.1-nightly.2020.8.26+commit.fdc4142b
v0.7.1-nightly.2020.8.25+commit.29b6c172
v0.7.1-nightly.2020.8.24+commit.21489d81
v0.7.1-nightly.2020.8.22+commit.bff0f9bd
v0.7.1-nightly.2020.8.21+commit.4dd25f73
v0.7.1-nightly.2020.8.20+commit.4a720a65
v0.7.1-nightly.2020.8.19+commit.9e488f12
v0.7.1-nightly.2020.8.18+commit.3c27d36e
v0.7.1-nightly.2020.8.17+commit.660ef792
v0.7.1-nightly.2020.8.13+commit.b1fb9da6
v0.7.1-nightly.2020.8.12+commit.acdaff63
v0.7.1-nightly.2020.8.11+commit.e68d16d8
v0.7.1-nightly.2020.8.10+commit.05901f5b
v0.7.1-nightly.2020.8.6+commit.241a564f
v0.7.1-nightly.2020.8.5+commit.3a409c39
v0.7.1-nightly.2020.8.4+commit.b8fd409f
v0.7.1-nightly.2020.8.3+commit.d31f05fc
v0.7.1-nightly.2020.7.31+commit.08791ab0
v0.7.1-nightly.2020.7.29+commit.f2fa5b5f
v0.7.1-nightly.2020.7.28+commit.cd2ce283
v0.7.0+commit.9e61f92b
v0.7.0-nightly.2020.7.27+commit.4e4b3ee6
v0.7.0-nightly.2020.7.23+commit.7ad27188
v0.6.12+commit.27d51765
v0.6.11+commit.5ef660b1
v0.6.11-nightly.2020.6.25+commit.48dd3634
v0.6.10+commit.00c0fcaf
v0.6.10-nightly.2020.6.10+commit.0a5d9927
v0.6.10-nightly.2020.6.9+commit.1e8e0ebd
v0.6.10-nightly.2020.6.8+commit.3d241eed
v0.6.10-nightly.2020.6.5+commit.d4552678
v0.6.10-nightly.2020.6.4+commit.0ec96337
v0.6.9+commit.3e3065ac
v0.6.9-nightly.2020.6.4+commit.70e62524
v0.6.9-nightly.2020.6.3+commit.de5e2835
v0.6.9-nightly.2020.6.2+commit.22f7a9f0
v0.6.9-nightly.2020.5.29+commit.b01a1a36
v0.6.9-nightly.2020.5.28+commit.ee8307ce
v0.6.9-nightly.2020.5.27+commit.57ac8628
v0.6.9-nightly.2020.5.14+commit.33d8d838
v0.6.8+commit.0bbfe453
v0.6.8-nightly.2020.5.14+commit.a6d0067b
v0.6.8-nightly.2020.5.13+commit.aca70049
v0.6.8-nightly.2020.5.12+commit.b014b89e
v0.6.8-nightly.2020.5.11+commit.39249bc6
v0.6.8-nightly.2020.5.8+commit.4e58c672
v0.6.8-nightly.2020.5.7+commit.741c41a1
v0.6.8-nightly.2020.5.6+commit.3a93080c
v0.6.8-nightly.2020.5.5+commit.1de73a16
v0.6.8-nightly.2020.5.4+commit.1bb07e26
v0.6.7+commit.b8d736ae
v0.6.7-nightly.2020.5.4+commit.94f7ffcf
v0.6.7-nightly.2020.5.1+commit.5163c09e
v0.6.7-nightly.2020.4.29+commit.602b29cb
v0.6.7-nightly.2020.4.28+commit.75a25d53
v0.6.7-nightly.2020.4.27+commit.61b1369f
v0.6.7-nightly.2020.4.25+commit.ed6c6b31
v0.6.7-nightly.2020.4.24+commit.2b39f3b9
v0.6.7-nightly.2020.4.23+commit.aaa434da
v0.6.7-nightly.2020.4.22+commit.d0fcd468
v0.6.7-nightly.2020.4.20+commit.7eff836a
v0.6.7-nightly.2020.4.17+commit.ccc06c49
v0.6.7-nightly.2020.4.16+commit.0f7a5e80
v0.6.7-nightly.2020.4.15+commit.cbd90f8d
v0.6.7-nightly.2020.4.14+commit.accd8d76
v0.6.7-nightly.2020.4.9+commit.f8aaa83e
v0.6.6+commit.6c089d02
v0.6.6-nightly.2020.4.9+commit.605e176f
v0.6.6-nightly.2020.4.8+commit.9fab9df1
v0.6.6-nightly.2020.4.7+commit.582c7545
v0.6.6-nightly.2020.4.6+commit.e349f4b7
v0.6.5+commit.f956cc89
v0.6.5-nightly.2020.4.6+commit.8451639f
v0.6.5-nightly.2020.4.3+commit.00acaadd
v0.6.5-nightly.2020.4.2+commit.c8f0629e
v0.6.5-nightly.2020.4.1+commit.c11d5b8d
v0.6.5-nightly.2020.3.31+commit.b83d82ab
v0.6.5-nightly.2020.3.30+commit.469316f8
v0.6.5-nightly.2020.3.26+commit.994591b8
v0.6.5-nightly.2020.3.25+commit.18971389
v0.6.5-nightly.2020.3.24+commit.d584b2d1
v0.6.5-nightly.2020.3.23+commit.848f405f
v0.6.5-nightly.2020.3.19+commit.8834b1ac
v0.6.5-nightly.2020.3.18+commit.cfd315e1
v0.6.5-nightly.2020.3.17+commit.435c9dae
v0.6.5-nightly.2020.3.16+commit.e21567c1
v0.6.5-nightly.2020.3.13+commit.362c2175
v0.6.5-nightly.2020.3.12+commit.bdd8045d
v0.6.5-nightly.2020.3.11+commit.1167af1d
v0.6.5-nightly.2020.3.10+commit.59071f60
v0.6.4+commit.1dca32f3
v0.6.4-nightly.2020.3.10+commit.683ebc8e
v0.6.4-nightly.2020.3.9+commit.dbe2a5f4
v0.6.4-nightly.2020.3.8+commit.a328e940
v0.6.4-nightly.2020.3.6+commit.78ce4b96
v0.6.4-nightly.2020.3.4+commit.27a4670a
v0.6.4-nightly.2020.3.3+commit.20679d63
v0.6.4-nightly.2020.2.27+commit.b65a165d
v0.6.4-nightly.2020.2.26+commit.6930e0c2
v0.6.4-nightly.2020.2.25+commit.af81d4b6
v0.6.4-nightly.2020.2.24+commit.aa6a2b47
v0.6.4-nightly.2020.2.20+commit.525fe384
v0.6.4-nightly.2020.2.19+commit.8f2c5fc0
v0.6.4-nightly.2020.2.18+commit.ba9f740a
v0.6.3+commit.8dda9521
v0.6.3-nightly.2020.2.18+commit.64f9dc35
v0.6.3-nightly.2020.2.17+commit.50421e8b
v0.6.3-nightly.2020.2.14+commit.96709b32
v0.6.3-nightly.2020.2.13+commit.7af581df
v0.6.3-nightly.2020.2.12+commit.0e100e7e
v0.6.3-nightly.2020.2.11+commit.5214cb0e
v0.6.3-nightly.2020.2.10+commit.64bb0d55
v0.6.3-nightly.2020.2.7+commit.462cd432
v0.6.3-nightly.2020.2.6+commit.93191ceb
v0.6.3-nightly.2020.2.5+commit.913d5f32
v0.6.3-nightly.2020.2.4+commit.836938c1
v0.6.3-nightly.2020.2.3+commit.93a41f7a
v0.6.3-nightly.2020.1.31+commit.b6190e06
v0.6.3-nightly.2020.1.30+commit.ad98bf0f
v0.6.3-nightly.2020.1.29+commit.01eb9a5b
v0.6.3-nightly.2020.1.28+commit.2d3bd91d
v0.6.3-nightly.2020.1.27+commit.8809d4bb
v0.6.2+commit.bacdbe57
v0.6.2-nightly.2020.1.27+commit.1bdb409b
v0.6.2-nightly.2020.1.23+commit.3add37a2
v0.6.2-nightly.2020.1.22+commit.641bb815
v0.6.2-nightly.2020.1.20+commit.470c19eb
v0.6.2-nightly.2020.1.17+commit.92908f52
v0.6.2-nightly.2020.1.16+commit.3d4a2219
v0.6.2-nightly.2020.1.15+commit.9d9a7ebe
v0.6.2-nightly.2020.1.14+commit.6dbadf69
v0.6.2-nightly.2020.1.13+commit.408458b7
v0.6.2-nightly.2020.1.10+commit.d577a768
v0.6.2-nightly.2020.1.9+commit.17158995
v0.6.2-nightly.2020.1.8+commit.12b52ae6
v0.6.1+commit.e6f7d5a4
v0.6.1-nightly.2020.1.7+commit.8385256b
v0.6.1-nightly.2020.1.6+commit.20cf9d9f
v0.6.1-nightly.2020.1.3+commit.943af71d
v0.6.1-nightly.2020.1.2+commit.d082b9b8
v0.6.1-nightly.2019.12.20+commit.ece6463f
v0.6.1-nightly.2019.12.19+commit.d420fe37
v0.6.1-nightly.2019.12.18+commit.9a1cc027
v0.6.0+commit.26b70077
v0.6.0-nightly.2019.12.17+commit.d13438ee
v0.6.0-nightly.2019.12.16+commit.7390b5b5
v0.6.0-nightly.2019.12.14+commit.1c01c69e
v0.6.0-nightly.2019.12.13+commit.9ddd5042
v0.6.0-nightly.2019.12.12+commit.104a8c59
v0.6.0-nightly.2019.12.11+commit.7247e72d
v0.6.0-nightly.2019.12.10+commit.7244aa01
v0.5.17+commit.d19bba13
v0.5.16+commit.9c3226ce
v0.5.15+commit.6a57276f
v0.5.14+commit.01f1aaa4
v0.5.14-nightly.2019.12.10+commit.45aa7a88
v0.5.14-nightly.2019.12.9+commit.d6667560
v0.5.14-nightly.2019.12.5+commit.d2e3933d
v0.5.14-nightly.2019.12.4+commit.2a1b6f55
v0.5.14-nightly.2019.11.30+commit.4775af73
v0.5.14-nightly.2019.11.29+commit.7b038dbd
v0.5.14-nightly.2019.11.28+commit.40d9744b
v0.5.14-nightly.2019.11.27+commit.87943bf4
v0.5.14-nightly.2019.11.26+commit.200a92b4
v0.5.14-nightly.2019.11.25+commit.c4622774
v0.5.14-nightly.2019.11.21+commit.9eac460c
v0.5.14-nightly.2019.11.20+commit.7535039f
v0.5.14-nightly.2019.11.19+commit.e383b2bb
v0.5.14-nightly.2019.11.18+commit.79af19db
v0.5.14-nightly.2019.11.15+commit.6a993152
v0.5.14-nightly.2019.11.14+commit.3e04fd6e
v0.5.13+commit.5b0b510c
v0.5.13-nightly.2019.11.14+commit.d1c6ab8a
v0.5.13-nightly.2019.11.13+commit.6bef3071
v0.5.13-nightly.2019.11.12+commit.52a9de83
v0.5.13-nightly.2019.11.11+commit.7c7cca5f
v0.5.13-nightly.2019.11.10+commit.a5f0422d
v0.5.13-nightly.2019.11.8+commit.78be9385
v0.5.13-nightly.2019.11.7+commit.37c6ab4c
v0.5.13-nightly.2019.11.6+commit.56a3abcd
v0.5.13-nightly.2019.11.5+commit.9bec5334
v0.5.13-nightly.2019.11.4+commit.26c6a1fc
v0.5.13-nightly.2019.11.1+commit.73954f16
v0.5.13-nightly.2019.10.31+commit.d932f2d0
v0.5.13-nightly.2019.10.29+commit.5d906cd5
v0.5.13-nightly.2019.10.28+commit.9eb08c0c
v0.5.13-nightly.2019.10.25+commit.302a51a5
v0.5.13-nightly.2019.10.24+commit.15e39f7d
v0.5.13-nightly.2019.10.23+commit.e56d1aa5
v0.5.13-nightly.2019.10.22+commit.eca2b9bd
v0.5.13-nightly.2019.10.18+commit.d5b2f347
v0.5.13-nightly.2019.10.17+commit.5ea1d90f
v0.5.13-nightly.2019.10.16+commit.9ec8bcda
v0.5.13-nightly.2019.10.15+commit.83bb1515
v0.5.13-nightly.2019.10.4+commit.6cbcc379
v0.5.13-nightly.2019.10.2+commit.2d150b65
v0.5.13-nightly.2019.10.1+commit.74d2b228
v0.5.12+commit.7709ece9
v0.5.12-nightly.2019.10.1+commit.cbdc3bc1
v0.5.12-nightly.2019.9.30+commit.88476475
v0.5.12-nightly.2019.9.24+commit.973e4ca9
v0.5.12-nightly.2019.9.23+commit.c4208a6a
v0.5.12-nightly.2019.9.19+commit.0478eb1e
v0.5.12-nightly.2019.9.17+commit.58f0f9db
v0.5.12-nightly.2019.9.16+commit.34a84f3a
v0.5.12-nightly.2019.9.13+commit.5d58c43a
v0.5.12-nightly.2019.9.12+commit.b747c267
v0.5.12-nightly.2019.9.11+commit.5063e537
v0.5.12-nightly.2019.9.10+commit.4452a9b6
v0.5.12-nightly.2019.9.9+commit.f5e976ce
v0.5.12-nightly.2019.9.6+commit.7e80fceb
v0.5.12-nightly.2019.9.5+commit.96980d0b
v0.5.12-nightly.2019.9.4+commit.c5fbf23f
v0.5.12-nightly.2019.9.3+commit.d1831b15
v0.5.12-nightly.2019.9.2+commit.3c963eb0
v0.5.12-nightly.2019.8.29+commit.459aed90
v0.5.12-nightly.2019.8.28+commit.e74b63b6
v0.5.12-nightly.2019.8.26+commit.e1bb4b9f
v0.5.12-nightly.2019.8.24+commit.bb104546
v0.5.12-nightly.2019.8.23+commit.b5048bd6
v0.5.12-nightly.2019.8.19+commit.a39d26f3
v0.5.12-nightly.2019.8.16+commit.058bbd39
v0.5.12-nightly.2019.8.15+commit.2508cbc1
v0.5.12-nightly.2019.8.14+commit.fb8137df
v0.5.12-nightly.2019.8.13+commit.a6cbc3b8
v0.5.11+commit.22be8592
v0.5.11+commit.c082d0b4
v0.5.11-nightly.2019.8.12+commit.b285e086
v0.5.11-nightly.2019.8.10+commit.f5f2bbb2
v0.5.11-nightly.2019.8.9+commit.682a3ece
v0.5.11-nightly.2019.8.8+commit.16efcfdb
v0.5.11-nightly.2019.8.7+commit.6166dc8e
v0.5.11-nightly.2019.8.6+commit.cd563e52
v0.5.11-nightly.2019.8.5+commit.29d47d5c
v0.5.11-nightly.2019.8.2+commit.967ee944
v0.5.11-nightly.2019.8.1+commit.aa87a607
v0.5.11-nightly.2019.7.31+commit.32e6e356
v0.5.11-nightly.2019.7.30+commit.092e62f1
v0.5.11-nightly.2019.7.29+commit.2fdc07c5
v0.5.11-nightly.2019.7.25+commit.4f7fec69
v0.5.11-nightly.2019.7.23+commit.14699340
v0.5.11-nightly.2019.7.22+commit.535553b5
v0.5.11-nightly.2019.7.19+commit.508cf66d
v0.5.11-nightly.2019.7.18+commit.1d673a3b
v0.5.11-nightly.2019.7.17+commit.4fa78004
v0.5.11-nightly.2019.7.16+commit.a5a7983a
v0.5.11-nightly.2019.7.11+commit.88477bdb
v0.5.11-nightly.2019.7.10+commit.ba922e76
v0.5.11-nightly.2019.7.9+commit.8d006d20
v0.5.11-nightly.2019.7.8+commit.25928767
v0.5.11-nightly.2019.7.4+commit.3b2ebba4
v0.5.11-nightly.2019.7.3+commit.c3c8bc09
v0.5.11-nightly.2019.7.2+commit.06d01d15
v0.5.11-nightly.2019.7.1+commit.b8dbf7d2
v0.5.11-nightly.2019.6.27+commit.3597de35
v0.5.11-nightly.2019.6.26+commit.b4a0a793
v0.5.11-nightly.2019.6.25+commit.1cc84753
v0.5.10+commit.5a6ea5b1
v0.5.10-nightly.2019.6.25+commit.92529068
v0.5.10-nightly.2019.6.24+commit.eb5b8298
v0.5.10-nightly.2019.6.20+commit.096e3fcd
v0.5.10-nightly.2019.6.19+commit.53f26d97
v0.5.10-nightly.2019.6.18+commit.b6695071
v0.5.10-nightly.2019.6.17+commit.9c5dc63e
v0.5.10-nightly.2019.6.14+commit.4aa0c9e0
v0.5.10-nightly.2019.6.13+commit.62bd7032
v0.5.10-nightly.2019.6.12+commit.502d22a2
v0.5.10-nightly.2019.6.11+commit.bd1f65d6
v0.5.10-nightly.2019.6.7+commit.dc085bb8
v0.5.10-nightly.2019.6.6+commit.fc35c139
v0.5.10-nightly.2019.6.5+commit.3a331639
v0.5.10-nightly.2019.6.4+commit.95e6b2e4
v0.5.10-nightly.2019.5.30+commit.dd04a35c
v0.5.10-nightly.2019.5.29+commit.c9e2d388
v0.5.10-nightly.2019.5.28+commit.ff8898b8
v0.5.9+commit.c68bc34e
v0.5.9+commit.e560f70d
v0.5.9-nightly.2019.5.28+commit.01b6b680
v0.5.9-nightly.2019.5.27+commit.c14279fc
v0.5.9-nightly.2019.5.24+commit.2a2cea08
v0.5.9-nightly.2019.5.23+commit.7cf51876
v0.5.9-nightly.2019.5.22+commit.f06582f9
v0.5.9-nightly.2019.5.21+commit.0e132d07
v0.5.9-nightly.2019.5.20+commit.0731abd3
v0.5.9-nightly.2019.5.17+commit.88e9fbe6
v0.5.9-nightly.2019.5.16+commit.46d6f395
v0.5.9-nightly.2019.5.15+commit.a10501bb
v0.5.9-nightly.2019.5.14+commit.563aec1d
v0.5.9-nightly.2019.5.13+commit.a28b6224
v0.5.9-nightly.2019.5.10+commit.661b08e1
v0.5.9-nightly.2019.5.9+commit.8f2c8daf
v0.5.9-nightly.2019.5.8+commit.97f16421
v0.5.9-nightly.2019.5.7+commit.a21f8a0b
v0.5.9-nightly.2019.5.6+commit.dee1c110
v0.5.9-nightly.2019.5.2+commit.90f2fe6f
v0.5.9-nightly.2019.4.30+commit.b6bcd8a1
v0.5.8+commit.23d335f2
v0.5.8-nightly.2019.4.30+commit.0dc461b9
v0.5.8-nightly.2019.4.29+commit.578d6180
v0.5.8-nightly.2019.4.25+commit.eea425a3
v0.5.8-nightly.2019.4.24+commit.f124bace
v0.5.8-nightly.2019.4.23+commit.13518820
v0.5.8-nightly.2019.4.18+commit.fce19bde
v0.5.8-nightly.2019.4.17+commit.1feefa1c
v0.5.8-nightly.2019.4.16+commit.a61931c5
v0.5.8-nightly.2019.4.15+commit.e4e786a9
v0.5.8-nightly.2019.4.14+commit.6c68904f
v0.5.8-nightly.2019.4.12+commit.31abeb99
v0.5.8-nightly.2019.4.11+commit.e97d4b4a
v0.5.8-nightly.2019.4.10+commit.9eaaf42c
v0.5.8-nightly.2019.4.5+commit.9ef84df4
v0.5.8-nightly.2019.4.4+commit.ee2f5662
v0.5.8-nightly.2019.4.3+commit.1b7878cf
v0.5.8-nightly.2019.4.2+commit.7b0f7eb1
v0.5.8-nightly.2019.4.1+commit.a3a60b8e
v0.5.8-nightly.2019.3.29+commit.91a54f9b
v0.5.8-nightly.2019.3.28+commit.2bbc41ad
v0.5.8-nightly.2019.3.27+commit.97818f65
v0.5.8-nightly.2019.3.26+commit.b85fc1a6
v0.5.7+commit.6da8b019
v0.5.7-nightly.2019.3.26+commit.d079cdbf
v0.5.7-nightly.2019.3.25+commit.99ed3a64
v0.5.7-nightly.2019.3.22+commit.0af47da1
v0.5.7-nightly.2019.3.21+commit.ebb8c175
v0.5.7-nightly.2019.3.20+commit.5245a66d
v0.5.7-nightly.2019.3.19+commit.c7824932
v0.5.7-nightly.2019.3.18+commit.5b5c9aa2
v0.5.7-nightly.2019.3.14+commit.d1d6d59c
v0.5.7-nightly.2019.3.13+commit.2da906d9
v0.5.6+commit.b259423e
v0.5.6-nightly.2019.3.13+commit.9ccd5dfe
v0.5.6-nightly.2019.3.12+commit.2f37cd09
v0.5.6-nightly.2019.3.11+commit.189983a1
v0.5.5+commit.47a71e8f
v0.5.5-nightly.2019.3.5+commit.c283f6d8
v0.5.5-nightly.2019.3.4+commit.5490a5cd
v0.5.5-nightly.2019.2.28+commit.e9543d83
v0.5.5-nightly.2019.2.27+commit.a0dcb36f
v0.5.5-nightly.2019.2.26+commit.472a6445
v0.5.5-nightly.2019.2.25+commit.52ee955f
v0.5.5-nightly.2019.2.21+commit.e7a8fed0
v0.5.5-nightly.2019.2.20+commit.c8fb2c1b
v0.5.5-nightly.2019.2.19+commit.d9e4a10d
v0.5.5-nightly.2019.2.18+commit.db7b38e3
v0.5.5-nightly.2019.2.16+commit.2f0926c3
v0.5.5-nightly.2019.2.15+commit.04081303
v0.5.5-nightly.2019.2.14+commit.33318249
v0.5.5-nightly.2019.2.13+commit.b1a5ffb9
v0.5.5-nightly.2019.2.12+commit.828255fa
v0.5.4+commit.9549d8ff
v0.5.4-nightly.2019.2.12+commit.f0f34984
v0.5.4-nightly.2019.2.11+commit.49cd55d3
v0.5.4-nightly.2019.2.7+commit.caecdfab
v0.5.4-nightly.2019.2.6+commit.e5bf1f1d
v0.5.4-nightly.2019.2.5+commit.f3c9b41f
v0.5.4-nightly.2019.2.4+commit.82b69963
v0.5.4-nightly.2019.1.31+commit.ddab3f06
v0.5.4-nightly.2019.1.30+commit.bf3968d6
v0.5.4-nightly.2019.1.29+commit.ebf503a6
v0.5.4-nightly.2019.1.28+commit.e6d102f2
v0.5.4-nightly.2019.1.26+commit.0ef45b28
v0.5.4-nightly.2019.1.24+commit.2e7274b4
v0.5.4-nightly.2019.1.23+commit.ea292393
v0.5.4-nightly.2019.1.22+commit.26c06550
v0.5.3+commit.10d17f24
v0.5.3-nightly.2019.1.22+commit.d87d9a26
v0.5.3-nightly.2019.1.21+commit.606c2b99
v0.5.3-nightly.2019.1.19+commit.d3270bc3
v0.5.3-nightly.2019.1.18+commit.7b759866
v0.5.3-nightly.2019.1.17+commit.49f74a7b
v0.5.3-nightly.2019.1.16+commit.82453a76
v0.5.3-nightly.2019.1.15+commit.6146c59a
v0.5.3-nightly.2019.1.14+commit.051df319
v0.5.3-nightly.2019.1.11+commit.94688d2f
v0.5.3-nightly.2019.1.10+commit.31033fb4
v0.5.3-nightly.2019.1.9+commit.63319cfd
v0.5.3-nightly.2019.1.8+commit.a0ca746c
v0.5.3-nightly.2019.1.7+commit.f3799034
v0.5.3-nightly.2019.1.3+commit.d597b1db
v0.5.3-nightly.2018.12.20+commit.245ec29c
v0.5.2+commit.1df8f40c
v0.5.2-nightly.2018.12.19+commit.88750920
v0.5.2-nightly.2018.12.18+commit.4b43aeca
v0.5.2-nightly.2018.12.17+commit.12874029
v0.5.2-nightly.2018.12.13+commit.b3e2ba15
v0.5.2-nightly.2018.12.12+commit.85291bcb
v0.5.2-nightly.2018.12.11+commit.599760b6
v0.5.2-nightly.2018.12.10+commit.6240d9e7
v0.5.2-nightly.2018.12.7+commit.52ff3c94
v0.5.2-nightly.2018.12.6+commit.5a08ae5e
v0.5.2-nightly.2018.12.5+commit.6efe2a52
v0.5.2-nightly.2018.12.4+commit.e49f37be
v0.5.2-nightly.2018.12.3+commit.e6a01d26
v0.5.1+commit.c8a2cb62
v0.5.1-nightly.2018.12.3+commit.a73df9bc
v0.5.1-nightly.2018.11.30+commit.a7ca4991
v0.5.1-nightly.2018.11.29+commit.f6d01323
v0.5.1-nightly.2018.11.28+commit.7cbf0468
v0.5.1-nightly.2018.11.27+commit.bc7cb301
v0.5.1-nightly.2018.11.26+commit.f9378967
v0.5.1-nightly.2018.11.25+commit.1e03c160
v0.5.1-nightly.2018.11.23+commit.616ef8bc
v0.5.1-nightly.2018.11.22+commit.dc748bc7
v0.5.1-nightly.2018.11.21+commit.2c6e1888
v0.5.1-nightly.2018.11.19+commit.d3f66ca0
v0.5.1-nightly.2018.11.17+commit.5be45e73
v0.5.1-nightly.2018.11.15+commit.9db76403
v0.5.1-nightly.2018.11.14+commit.10d99fc3
v0.5.1-nightly.2018.11.13+commit.74ede87a
v0.5.0+commit.1d4f565a
v0.5.0-nightly.2018.11.13+commit.ac980fb8
v0.5.0-nightly.2018.11.12+commit.09f8ff27
v0.5.0-nightly.2018.11.11+commit.405565db
v0.5.0-nightly.2018.11.9+commit.9709dfe0
v0.5.0-nightly.2018.11.8+commit.cc2de07b
v0.5.0-nightly.2018.11.7+commit.a459b8c8
v0.5.0-nightly.2018.11.5+commit.88aee34c
v0.5.0-nightly.2018.11.4+commit.e4da724f
v0.5.0-nightly.2018.10.30+commit.cbbbc0d5
v0.5.0-nightly.2018.10.29+commit.0b4f6ab7
v0.5.0-nightly.2018.10.28+commit.c338b422
v0.5.0-nightly.2018.10.26+commit.c8400353
v0.5.0-nightly.2018.10.25+commit.f714b0dd
v0.5.0-nightly.2018.10.24+commit.01566c2e
v0.5.0-nightly.2018.10.23+commit.f5f977ea
v0.5.0-nightly.2018.10.22+commit.a2f5087d
v0.5.0-nightly.2018.10.19+commit.c13b5280
v0.5.0-nightly.2018.10.18+commit.99dc869e
v0.5.0-nightly.2018.10.17+commit.ba158882
v0.5.0-nightly.2018.10.16+commit.b723893a
v0.5.0-nightly.2018.10.15+commit.b965fd6e
v0.5.0-nightly.2018.10.12+commit.1d312c8e
v0.5.0-nightly.2018.10.11+commit.6b5d041e
v0.5.0-nightly.2018.10.10+commit.06200b4b
v0.5.0-nightly.2018.10.9+commit.4ab2e03b
v0.5.0-nightly.2018.10.8+commit.7d2dc143
v0.5.0-nightly.2018.10.6+commit.363b527b
v0.5.0-nightly.2018.10.5+commit.44c1293a
v0.5.0-nightly.2018.10.4+commit.68dfe8b6
v0.5.0-nightly.2018.10.3+commit.b8b31eb3
v0.5.0-nightly.2018.10.2+commit.b77b79c4
v0.5.0-nightly.2018.10.1+commit.80012e69
v0.5.0-nightly.2018.9.30+commit.8ef47cb6
v0.5.0-nightly.2018.9.27+commit.963ae540
v0.5.0-nightly.2018.9.26+commit.d72498b3
v0.5.0-nightly.2018.9.25+commit.608f36d7
v0.4.26+commit.4563c3fc
v0.4.26-nightly.2018.9.25+commit.1b8334e5
v0.4.26-nightly.2018.9.24+commit.dce1ed5a
v0.4.26-nightly.2018.9.21+commit.8f96fe69
v0.4.26-nightly.2018.9.20+commit.2150aea3
v0.4.26-nightly.2018.9.19+commit.7c15f6b1
v0.4.26-nightly.2018.9.18+commit.fcb48bce
v0.4.26-nightly.2018.9.17+commit.2409986c
v0.4.26-nightly.2018.9.13+commit.8b089cc8
v0.4.25+commit.59dbf8f1
v0.4.25-nightly.2018.9.13+commit.15c8c0d2
v0.4.25-nightly.2018.9.12+commit.9214c7c3
v0.4.25-nightly.2018.9.11+commit.d66e956a
v0.4.25-nightly.2018.9.10+commit.86d85025
v0.4.25-nightly.2018.9.6+commit.f19cddd5
v0.4.25-nightly.2018.9.5+commit.a996ea26
v0.4.25-nightly.2018.9.4+commit.f27d7edf
v0.4.25-nightly.2018.9.3+commit.0b9cc80b
v0.4.25-nightly.2018.8.16+commit.a9e7ae29
v0.4.25-nightly.2018.8.15+commit.2946b7cd
v0.4.25-nightly.2018.8.14+commit.6ca39739
v0.4.25-nightly.2018.8.13+commit.a2c754b3
v0.4.25-nightly.2018.8.9+commit.63d071d6
v0.4.25-nightly.2018.8.8+commit.d2ca9c82
v0.4.25-nightly.2018.8.7+commit.cda3fbda
v0.4.25-nightly.2018.8.6+commit.3684151e
v0.4.25-nightly.2018.8.3+commit.04efbc9e
v0.4.25-nightly.2018.8.2+commit.6003ed2a
v0.4.25-nightly.2018.8.1+commit.21888e24
v0.4.25-nightly.2018.7.31+commit.75c1a9bd
v0.4.25-nightly.2018.7.30+commit.9d09e21b
v0.4.25-nightly.2018.7.27+commit.bc51b0f6
v0.4.25-nightly.2018.7.25+commit.ff8e9300
v0.4.25-nightly.2018.7.24+commit.fc68d22b
v0.4.25-nightly.2018.7.23+commit.79ddcc76
v0.4.25-nightly.2018.7.20+commit.d3000e70
v0.4.25-nightly.2018.7.19+commit.e3c2f20f
v0.4.25-nightly.2018.7.18+commit.b909df45
v0.4.25-nightly.2018.7.17+commit.56096e9c
v0.4.25-nightly.2018.7.16+commit.98656423
v0.4.25-nightly.2018.7.12+commit.ff9974e9
v0.4.25-nightly.2018.7.11+commit.07910c80
v0.4.25-nightly.2018.7.10+commit.5c404fcf
v0.4.25-nightly.2018.7.9+commit.c42583d2
v0.4.25-nightly.2018.7.5+commit.b1ab81ef
v0.4.25-nightly.2018.7.4+commit.47637224
v0.4.25-nightly.2018.7.3+commit.09f3532e
v0.4.25-nightly.2018.7.2+commit.a5608b31
v0.4.25-nightly.2018.6.29+commit.c9cab803
v0.4.25-nightly.2018.6.28+commit.42680629
v0.4.25-nightly.2018.6.27+commit.b67dfa15
v0.4.25-nightly.2018.6.26+commit.24f124f8
v0.4.25-nightly.2018.6.25+commit.b7003505
v0.4.25-nightly.2018.6.22+commit.9b67bdb3
v0.4.25-nightly.2018.6.21+commit.0d104718
v0.4.25-nightly.2018.6.20+commit.ba7fbf11
v0.4.25-nightly.2018.6.19+commit.c72e04c3
v0.4.25-nightly.2018.6.18+commit.4247b004
v0.4.25-nightly.2018.6.17+commit.1692f78b
v0.4.25-nightly.2018.6.14+commit.baeabe1c
v0.4.25-nightly.2018.6.13+commit.3055e4ca
v0.4.25-nightly.2018.6.12+commit.56a965ea
v0.4.25-nightly.2018.6.11+commit.d0355619
v0.4.25-nightly.2018.6.8+commit.81c5a6e4
v0.4.25-nightly.2018.6.7+commit.ddd256a6
v0.4.25-nightly.2018.6.6+commit.59b35fa5
v0.4.25-nightly.2018.6.5+commit.7422cd73
v0.4.25-nightly.2018.6.4+commit.0a074d84
v0.4.25-nightly.2018.6.3+commit.ef8fb63b
v0.4.25-nightly.2018.5.30+commit.3f3d6df2
v0.4.25-nightly.2018.5.28+commit.0c223b03
v0.4.25-nightly.2018.5.23+commit.18c651b7
v0.4.25-nightly.2018.5.22+commit.849b1bd5
v0.4.25-nightly.2018.5.21+commit.e97f9b6b
v0.4.25-nightly.2018.5.18+commit.4d7b092c
v0.4.25-nightly.2018.5.17+commit.4aa2f036
v0.4.25-nightly.2018.5.16+commit.3897c367
v0.4.24+commit.e67f0147
v0.4.24-nightly.2018.5.16+commit.7f965c86
v0.4.24-nightly.2018.5.15+commit.b8b46099
v0.4.24-nightly.2018.5.14+commit.7a669b39
v0.4.24-nightly.2018.5.11+commit.43803b1a
v0.4.24-nightly.2018.5.10+commit.85d417a8
v0.4.24-nightly.2018.5.9+commit.1e953355
v0.4.24-nightly.2018.5.8+commit.0a63bc17
v0.4.24-nightly.2018.5.7+commit.6db7e09a
v0.4.24-nightly.2018.5.4+commit.81d61ca0
v0.4.24-nightly.2018.5.3+commit.72c3b3a2
v0.4.24-nightly.2018.5.2+commit.dc18cde6
v0.4.24-nightly.2018.4.30+commit.9e61b25d
v0.4.24-nightly.2018.4.27+commit.1604a996
v0.4.24-nightly.2018.4.26+commit.ef2111a2
v0.4.24-nightly.2018.4.25+commit.81cca26f
v0.4.24-nightly.2018.4.24+commit.258ae892
v0.4.24-nightly.2018.4.23+commit.c7ee2ca0
v0.4.24-nightly.2018.4.22+commit.2fae248d
v0.4.24-nightly.2018.4.20+commit.0f328431
v0.4.24-nightly.2018.4.19+commit.27d79906
v0.4.23+commit.124ca40d
v0.4.23-nightly.2018.4.19+commit.ae834e3d
v0.4.23-nightly.2018.4.18+commit.85687a37
v0.4.23-nightly.2018.4.17+commit.5499db01
v0.4.22+commit.4cb486ee
v0.4.22-nightly.2018.4.16+commit.d8030c9b
v0.4.22-nightly.2018.4.14+commit.73ca3e8a
v0.4.22-nightly.2018.4.13+commit.2001cc6b
v0.4.22-nightly.2018.4.12+commit.c3dc67d0
v0.4.22-nightly.2018.4.11+commit.b7b6d0ce
v0.4.22-nightly.2018.4.10+commit.27385d6d
v0.4.22-nightly.2018.4.6+commit.9bd49516
v0.4.22-nightly.2018.4.5+commit.c6adad93
v0.4.22-nightly.2018.4.4+commit.920de496
v0.4.22-nightly.2018.4.3+commit.3fbdd655
v0.4.22-nightly.2018.3.30+commit.326d656a
v0.4.22-nightly.2018.3.29+commit.c2ae33f8
v0.4.22-nightly.2018.3.27+commit.af262281
v0.4.22-nightly.2018.3.21+commit.8fd53c1c
v0.4.22-nightly.2018.3.16+commit.2b2527f3
v0.4.22-nightly.2018.3.15+commit.3f1e0d84
v0.4.22-nightly.2018.3.14+commit.c3f07b52
v0.4.22-nightly.2018.3.13+commit.f2614be9
v0.4.22-nightly.2018.3.12+commit.c6e9dd13
v0.4.22-nightly.2018.3.8+commit.fbc29f6d
v0.4.22-nightly.2018.3.7+commit.b5e804b8
v0.4.21+commit.dfe3193c
v0.4.21-nightly.2018.3.7+commit.bd7bc7c4
v0.4.21-nightly.2018.3.6+commit.a9e02acc
v0.4.21-nightly.2018.3.5+commit.cd6ffbdf
v0.4.21-nightly.2018.3.1+commit.cf6720ea
v0.4.21-nightly.2018.2.28+commit.ac5485a2
v0.4.21-nightly.2018.2.27+commit.415ac2ae
v0.4.21-nightly.2018.2.26+commit.cd2d8936
v0.4.21-nightly.2018.2.23+commit.cae6cc2c
v0.4.21-nightly.2018.2.22+commit.71a34abd
v0.4.21-nightly.2018.2.21+commit.16c7eabc
v0.4.21-nightly.2018.2.20+commit.dcc4083b
v0.4.21-nightly.2018.2.19+commit.839acafb
v0.4.21-nightly.2018.2.16+commit.3f7e82d0
v0.4.21-nightly.2018.2.15+commit.f4aa05f3
v0.4.21-nightly.2018.2.14+commit.bb3b327c
v0.4.20+commit.3155dd80
v0.4.20-nightly.2018.2.13+commit.27ef9794
v0.4.20-nightly.2018.2.12+commit.954903b5
v0.4.20-nightly.2018.1.29+commit.a668b9de
v0.4.20-nightly.2018.1.26+commit.bbad48bb
v0.4.20-nightly.2018.1.25+commit.e7afde95
v0.4.20-nightly.2018.1.24+commit.b177352a
v0.4.20-nightly.2018.1.23+commit.31aaf433
v0.4.20-nightly.2018.1.22+commit.e5def2da
v0.4.20-nightly.2018.1.19+commit.eba46a65
v0.4.20-nightly.2018.1.18+commit.33723c45
v0.4.20-nightly.2018.1.17+commit.4715167e
v0.4.20-nightly.2018.1.15+commit.14fcbd65
v0.4.20-nightly.2018.1.11+commit.0c20b6da
v0.4.20-nightly.2018.1.10+commit.a75d5333
v0.4.20-nightly.2018.1.6+commit.2548228b
v0.4.20-nightly.2018.1.5+commit.bca01f8f
v0.4.20-nightly.2018.1.4+commit.a0771691
v0.4.20-nightly.2017.12.20+commit.efc198d5
v0.4.20-nightly.2017.12.19+commit.2d800e67
v0.4.20-nightly.2017.12.18+commit.37b70e8e
v0.4.20-nightly.2017.12.14+commit.3d1830f3
v0.4.20-nightly.2017.12.13+commit.bfc54463
v0.4.20-nightly.2017.12.12+commit.1ddd4e2b
v0.4.20-nightly.2017.12.11+commit.4a1f18c9
v0.4.20-nightly.2017.12.8+commit.226bfe5b
v0.4.20-nightly.2017.12.6+commit.c2109436
v0.4.20-nightly.2017.12.5+commit.b47e023d
v0.4.20-nightly.2017.12.4+commit.240c79e6
v0.4.20-nightly.2017.12.1+commit.6d8d0393
v0.4.20-nightly.2017.11.30+commit.cb16a5d3
v0.4.19+commit.c4cbbb05
v0.4.19-nightly.2017.11.30+commit.f5a2508e
v0.4.19-nightly.2017.11.29+commit.7c69d88f
v0.4.19-nightly.2017.11.22+commit.f22ac8fc
v0.4.19-nightly.2017.11.21+commit.5c9e273d
v0.4.19-nightly.2017.11.17+commit.2b5ef806
v0.4.19-nightly.2017.11.16+commit.58e452d1
v0.4.19-nightly.2017.11.15+commit.e3206d8e
v0.4.19-nightly.2017.11.14+commit.bc39e730
v0.4.19-nightly.2017.11.13+commit.060b2c2b
v0.4.19-nightly.2017.11.11+commit.284c3839
v0.4.19-nightly.2017.10.29+commit.eb140bc6
v0.4.19-nightly.2017.10.28+commit.f9b24009
v0.4.19-nightly.2017.10.27+commit.1e085f85
v0.4.19-nightly.2017.10.26+commit.59d4dfbd
v0.4.19-nightly.2017.10.24+commit.1313e9d8
v0.4.19-nightly.2017.10.23+commit.dc6b1f02
v0.4.19-nightly.2017.10.20+commit.bdd2858b
v0.4.19-nightly.2017.10.19+commit.c58d9d2c
v0.4.19-nightly.2017.10.18+commit.f7ca2421
v0.4.18+commit.9cf6e910
v0.4.18-nightly.2017.10.18+commit.e854da1a
v0.4.18-nightly.2017.10.17+commit.8fbfd62d
v0.4.18-nightly.2017.10.16+commit.dbc8655b
v0.4.18-nightly.2017.10.15+commit.a74c9aef
v0.4.18-nightly.2017.10.10+commit.c35496bf
v0.4.18-nightly.2017.10.9+commit.6f832cac
v0.4.18-nightly.2017.10.6+commit.961f8746
v0.4.18-nightly.2017.10.5+commit.995b5525
v0.4.18-nightly.2017.10.4+commit.0c3888ab
v0.4.18-nightly.2017.10.3+commit.5c284589
v0.4.18-nightly.2017.10.2+commit.c6161030
v0.4.18-nightly.2017.9.29+commit.b9218468
v0.4.18-nightly.2017.9.28+commit.4d01d086
v0.4.18-nightly.2017.9.27+commit.809d5ce1
v0.4.18-nightly.2017.9.26+commit.eb5a6aac
v0.4.18-nightly.2017.9.25+commit.a72237f2
v0.4.18-nightly.2017.9.22+commit.a2a58789
v0.4.17+commit.bdeb9e52
v0.4.17-nightly.2017.9.21+commit.725b4fc2
v0.4.17-nightly.2017.9.20+commit.c0b3e5b0
v0.4.17-nightly.2017.9.19+commit.1fc71bd7
v0.4.17-nightly.2017.9.18+commit.c289fd3d
v0.4.17-nightly.2017.9.16+commit.a0d17172
v0.4.17-nightly.2017.9.14+commit.7dd372ce
v0.4.17-nightly.2017.9.12+commit.4770f8f4
v0.4.17-nightly.2017.9.11+commit.fbe24da1
v0.4.17-nightly.2017.9.6+commit.59223061
v0.4.17-nightly.2017.9.5+commit.f242331c
v0.4.17-nightly.2017.9.4+commit.8283f836
v0.4.17-nightly.2017.8.31+commit.402d6e71
v0.4.17-nightly.2017.8.29+commit.2d39a42d
v0.4.17-nightly.2017.8.28+commit.d15cde2a
v0.4.17-nightly.2017.8.25+commit.e945f458
v0.4.17-nightly.2017.8.24+commit.012d9f79
v0.4.16+commit.d7661dd9
v0.4.16-nightly.2017.8.24+commit.78c2dcac
v0.4.16-nightly.2017.8.23+commit.c5f11d93
v0.4.16-nightly.2017.8.22+commit.f874fc28
v0.4.16-nightly.2017.8.21+commit.0cf60488
v0.4.16-nightly.2017.8.16+commit.83561e13
v0.4.16-nightly.2017.8.15+commit.dca1f45c
v0.4.16-nightly.2017.8.14+commit.4d9790b6
v0.4.16-nightly.2017.8.11+commit.c84de7fa
v0.4.16-nightly.2017.8.10+commit.41e3cbe0
v0.4.16-nightly.2017.8.9+commit.81887bc7
v0.4.15+commit.8b45bddb
v0.4.15+commit.bbb8e64f
v0.4.15-nightly.2017.8.8+commit.41e72436
v0.4.15-nightly.2017.8.7+commit.212454a9
v0.4.15-nightly.2017.8.4+commit.e48730fe
v0.4.15-nightly.2017.8.2+commit.04166ce1
v0.4.15-nightly.2017.8.1+commit.7e07eb6e
v0.4.15-nightly.2017.7.31+commit.93f90eb2
v0.4.14+commit.c2215d46
v0.4.14-nightly.2017.7.31+commit.22326189
v0.4.14-nightly.2017.7.28+commit.7e40def6
v0.4.14-nightly.2017.7.27+commit.1298a8df
v0.4.14-nightly.2017.7.26+commit.0d701c94
v0.4.14-nightly.2017.7.25+commit.3c2b710b
v0.4.14-nightly.2017.7.24+commit.cfb11ff7
v0.4.14-nightly.2017.7.21+commit.75b48616
v0.4.14-nightly.2017.7.20+commit.d70974ea
v0.4.14-nightly.2017.7.19+commit.3ad326be
v0.4.14-nightly.2017.7.18+commit.c167a31b
v0.4.14-nightly.2017.7.14+commit.7c97546f
v0.4.14-nightly.2017.7.13+commit.2b33e0bc
v0.4.14-nightly.2017.7.12+commit.b981ef20
v0.4.14-nightly.2017.7.11+commit.0b17ff1b
v0.4.14-nightly.2017.7.10+commit.6fa5d47f
v0.4.14-nightly.2017.7.9+commit.aafcc360
v0.4.14-nightly.2017.7.8+commit.7d1ddfc6
v0.4.14-nightly.2017.7.6+commit.08dade9f
v0.4.13+commit.0fb4cb1a
v0.4.13-nightly.2017.7.6+commit.40d4ee49
v0.4.13-nightly.2017.7.5+commit.2b505e7a
v0.4.13-nightly.2017.7.4+commit.331b0b1c
v0.4.13-nightly.2017.7.3+commit.6e4e627b
v0.4.12+commit.194ff033
v0.4.12-nightly.2017.7.3+commit.0c7530a8
v0.4.12-nightly.2017.7.1+commit.06f8949f
v0.4.12-nightly.2017.6.30+commit.568e7520
v0.4.12-nightly.2017.6.29+commit.f5372cda
v0.4.12-nightly.2017.6.28+commit.e19c4125
v0.4.12-nightly.2017.6.27+commit.bc31d496
v0.4.12-nightly.2017.6.26+commit.f8794892
v0.4.12-nightly.2017.6.25+commit.29b8cdb5
v0.4.12-nightly.2017.6.23+commit.793f05fa
v0.4.12-nightly.2017.6.22+commit.1c54ce2a
v0.4.12-nightly.2017.6.21+commit.ac977cdf
v0.4.12-nightly.2017.6.20+commit.cb5f2f90
v0.4.12-nightly.2017.6.19+commit.0c75afb2
v0.4.12-nightly.2017.6.16+commit.17de4a07
v0.4.12-nightly.2017.6.15+commit.71fea1e3
v0.4.12-nightly.2017.6.14+commit.43cfab70
v0.4.12-nightly.2017.6.13+commit.0c8c2091
v0.4.12-nightly.2017.6.12+commit.496c2a20
v0.4.12-nightly.2017.6.9+commit.76667fed
v0.4.12-nightly.2017.6.8+commit.51fcfbcf
v0.4.12-nightly.2017.6.6+commit.243e389f
v0.4.12-nightly.2017.6.1+commit.96de7a83
v0.4.12-nightly.2017.5.30+commit.254b5572
v0.4.12-nightly.2017.5.29+commit.4a5dc6a4
v0.4.12-nightly.2017.5.26+commit.e43ff797
v0.4.12-nightly.2017.5.24+commit.cf639f48
v0.4.12-nightly.2017.5.23+commit.14b22150
v0.4.12-nightly.2017.5.22+commit.e3af0640
v0.4.12-nightly.2017.5.19+commit.982f6613
v0.4.12-nightly.2017.5.18+commit.6f9428e9
v0.4.12-nightly.2017.5.17+commit.b4c6877a
v0.4.12-nightly.2017.5.16+commit.2ba87fe8
v0.4.12-nightly.2017.5.11+commit.242e4318
v0.4.12-nightly.2017.5.10+commit.a6586f75
v0.4.12-nightly.2017.5.6+commit.822c9057
v0.4.12-nightly.2017.5.5+commit.0582fcb9
v0.4.12-nightly.2017.5.4+commit.025b32d9
v0.4.11+commit.68ef5810
v0.4.11-nightly.2017.5.3+commit.1aa0f77a
v0.4.11-nightly.2017.5.2+commit.5aeb6352
v0.4.11-nightly.2017.4.28+commit.f33614e1
v0.4.11-nightly.2017.4.27+commit.abe77f48
v0.4.11-nightly.2017.4.26+commit.3cbdf6d4
v0.4.11-nightly.2017.4.25+commit.c3b839ca
v0.4.11-nightly.2017.4.24+commit.a9f42157
v0.4.11-nightly.2017.4.22+commit.aa441668
v0.4.11-nightly.2017.4.21+commit.e3eea9fc
v0.4.11-nightly.2017.4.20+commit.6468955f
v0.4.11-nightly.2017.4.18+commit.82628a80
v0.4.11-nightly.2017.4.13+commit.138c952a
v0.4.11-nightly.2017.4.10+commit.9fe20650
v0.4.11-nightly.2017.3.29+commit.fefb3fad
v0.4.11-nightly.2017.3.28+commit.215184ef
v0.4.11-nightly.2017.3.27+commit.9d769a56
v0.4.11-nightly.2017.3.22+commit.74d7c513
v0.4.11-nightly.2017.3.21+commit.6fb27dee
v0.4.11-nightly.2017.3.20+commit.57bc763e
v0.4.11-nightly.2017.3.17+commit.2f2ad42c
v0.4.11-nightly.2017.3.16+commit.a2eb2c0a
v0.4.11-nightly.2017.3.15+commit.0157b86c
v0.4.10+commit.f0d539ae
v0.4.10-nightly.2017.3.15+commit.d134fda0
v0.4.10-nightly.2017.3.14+commit.409eb9e3
v0.4.10-nightly.2017.3.13+commit.9aab3b86
v0.4.10-nightly.2017.3.10+commit.f1dd79c7
v0.4.10-nightly.2017.3.9+commit.b22369d5
v0.4.10-nightly.2017.3.8+commit.a1e350a4
v0.4.10-nightly.2017.3.6+commit.2dac39b9
v0.4.10-nightly.2017.3.3+commit.6bfd894f
v0.4.10-nightly.2017.3.2+commit.5c411b47
v0.4.10-nightly.2017.3.1+commit.6ac2c15c
v0.4.10-nightly.2017.2.24+commit.6bbba106
v0.4.10-nightly.2017.2.22+commit.0b67fee3
v0.4.10-nightly.2017.2.20+commit.32b7d174
v0.4.10-nightly.2017.2.17+commit.419ab926
v0.4.10-nightly.2017.2.16+commit.0ad8e534
v0.4.10-nightly.2017.2.15+commit.ad751bd3
v0.4.10-nightly.2017.2.14+commit.91d5515c
v0.4.10-nightly.2017.2.13+commit.8357bdad
v0.4.10-nightly.2017.2.3+commit.5ce79609
v0.4.10-nightly.2017.2.2+commit.8f9839c6
v0.4.10-nightly.2017.2.1+commit.c1a675da
v0.4.10-nightly.2017.1.31+commit.747db75a
v0.4.9+commit.364da425
v0.4.9-nightly.2017.1.31+commit.f9af2de0
v0.4.9-nightly.2017.1.30+commit.edd3696d
v0.4.9-nightly.2017.1.27+commit.1774e087
v0.4.9-nightly.2017.1.26+commit.2122d2d7
v0.4.9-nightly.2017.1.24+commit.b52a6040
v0.4.9-nightly.2017.1.23+commit.6946902c
v0.4.9-nightly.2017.1.20+commit.12b002b3
v0.4.9-nightly.2017.1.19+commit.09403dd5
v0.4.9-nightly.2017.1.18+commit.005e1908
v0.4.9-nightly.2017.1.17+commit.6ecb4aa3
v0.4.9-nightly.2017.1.16+commit.79e5772b
v0.4.9-nightly.2017.1.13+commit.392ef5f4
v0.4.8+commit.60cc1668
v0.4.8-nightly.2017.1.13+commit.bde0b406
v0.4.8-nightly.2017.1.12+commit.b983c749
v0.4.8-nightly.2017.1.11+commit.4f5da2ea
v0.4.8-nightly.2017.1.10+commit.26a90af4
v0.4.8-nightly.2017.1.9+commit.354a10be
v0.4.8-nightly.2017.1.6+commit.a4d7a590
v0.4.8-nightly.2017.1.5+commit.0031e6a5
v0.4.8-nightly.2017.1.3+commit.43a5d11f
v0.4.8-nightly.2017.1.2+commit.75a596ab
v0.4.8-nightly.2016.12.16+commit.af8bc1c9
v0.4.7+commit.822622cf
v0.4.7-nightly.2016.12.15+commit.688841ae
v0.4.7-nightly.2016.12.14+commit.e53d1255
v0.4.7-nightly.2016.12.13+commit.9d607345
v0.4.7-nightly.2016.12.12+commit.e53fdb49
v0.4.7-nightly.2016.12.11+commit.84d4f3da
v0.4.7-nightly.2016.12.8+commit.89771a44
v0.4.7-nightly.2016.12.7+commit.fd7561ed
v0.4.7-nightly.2016.12.6+commit.b201e148
v0.4.7-nightly.2016.12.5+commit.34327c5d
v0.4.7-nightly.2016.12.3+commit.9be2fb12
v0.4.7-nightly.2016.12.2+commit.3a01a87a
v0.4.7-nightly.2016.12.1+commit.67f274f6
v0.4.7-nightly.2016.11.30+commit.e43a8ebc
v0.4.7-nightly.2016.11.29+commit.071cbc4a
v0.4.7-nightly.2016.11.28+commit.dadb4818
v0.4.7-nightly.2016.11.26+commit.4a67a286
v0.4.7-nightly.2016.11.25+commit.ba94b0ae
v0.4.7-nightly.2016.11.24+commit.851f8576
v0.4.7-nightly.2016.11.23+commit.475009b9
v0.4.7-nightly.2016.11.22+commit.1a205ebf
v0.4.6+commit.2dabbdf0
v0.4.6-nightly.2016.11.22+commit.3d9a180c
v0.4.6-nightly.2016.11.21+commit.aa48008c
v0.4.5+commit.b318366e
v0.4.5-nightly.2016.11.21+commit.afda210a
v0.4.5-nightly.2016.11.17+commit.b46a14f4
v0.4.5-nightly.2016.11.16+commit.c8116918
v0.4.5-nightly.2016.11.15+commit.c1b1efaf
v0.4.5-nightly.2016.11.14+commit.4f546e65
v0.4.5-nightly.2016.11.11+commit.6248e92d
v0.4.5-nightly.2016.11.10+commit.a40dcfef
v0.4.5-nightly.2016.11.9+commit.c82acfd3
v0.4.5-nightly.2016.11.8+commit.7a30e8cf
v0.4.5-nightly.2016.11.4+commit.d97d267a
v0.4.5-nightly.2016.11.3+commit.90a4acc3
v0.4.5-nightly.2016.11.1+commit.9cb1d30e
v0.4.4+commit.4633f3de
v0.4.4-nightly.2016.10.31+commit.1d3460c4
v0.4.4-nightly.2016.10.28+commit.e85390cc
v0.4.4-nightly.2016.10.27+commit.76e958f6
v0.4.4-nightly.2016.10.26+commit.34e2209b
v0.4.4-nightly.2016.10.25+commit.f99a418b
v0.4.3+commit.2353da71
v0.4.3-nightly.2016.10.25+commit.d190f016
v0.4.3-nightly.2016.10.24+commit.84b43b91
v0.4.3-nightly.2016.10.21+commit.984b8ac1
v0.4.3-nightly.2016.10.20+commit.9d304501
v0.4.3-nightly.2016.10.19+commit.0fd6f2b5
v0.4.3-nightly.2016.10.18+commit.0a9eb645
v0.4.3-nightly.2016.10.17+commit.07d32937
v0.4.3-nightly.2016.10.15+commit.482807f6
v0.4.3-nightly.2016.10.14+commit.0635b6e0
v0.4.3-nightly.2016.10.13+commit.2951c1eb
v0.4.3-nightly.2016.10.12+commit.def3f3ea
v0.4.3-nightly.2016.10.11+commit.aa18a6bd
v0.4.3-nightly.2016.10.10+commit.119bd4ad
v0.4.3-nightly.2016.9.30+commit.d5cfb17b
v0.4.2+commit.af6afb04
v0.4.2-nightly.2016.9.17+commit.212e0160
v0.4.2-nightly.2016.9.15+commit.6a80511f
v0.4.2-nightly.2016.9.13+commit.2bee7e91
v0.4.2-nightly.2016.9.12+commit.149dba9b
v0.4.2-nightly.2016.9.9+commit.51a98ab8
v0.4.1+commit.4fc6fc2c
v0.4.1-nightly.2016.9.9+commit.79867f49
v0.4.0+commit.acd334c9
v0.3.6+commit.3fc68da5
v0.3.6-nightly.2016.9.8+commit.f5a513a0
v0.3.6-nightly.2016.9.7+commit.24524d62
v0.3.6-nightly.2016.9.6+commit.114502f8
v0.3.6-nightly.2016.9.5+commit.873d8bb3
v0.3.6-nightly.2016.9.2+commit.341c9436
v0.3.6-nightly.2016.9.1+commit.b5d941d3
v0.3.6-nightly.2016.8.31+commit.3ccd1986
v0.3.6-nightly.2016.8.30+commit.cf974fd1
v0.3.6-nightly.2016.8.29+commit.b8060c55
v0.3.6-nightly.2016.8.27+commit.91d4fa47
v0.3.6-nightly.2016.8.26+commit.3eeefb5c
v0.3.6-nightly.2016.8.24+commit.e20afc71
v0.3.6-nightly.2016.8.23+commit.de535a74
v0.3.6-nightly.2016.8.22+commit.7183658c
v0.3.6-nightly.2016.8.20+commit.0d736fde
v0.3.6-nightly.2016.8.19+commit.32c93cf9
v0.3.6-nightly.2016.8.17+commit.c499470b
v0.3.6-nightly.2016.8.16+commit.970260bf
v0.3.6-nightly.2016.8.15+commit.868a1675
v0.3.6-nightly.2016.8.12+commit.9e03bda9
v0.3.6-nightly.2016.8.11+commit.7c15fa66
v0.3.6-nightly.2016.8.10+commit.55858de1
v0.3.6-nightly.2016.8.10+commit.5a374037
v0.3.6-nightly.2016.8.10+commit.b7c26f46
v0.3.6-nightly.2016.8.10+commit.e2a46b6a
v0.3.5+commit.5f97274a
v0.3.5-nightly.2016.8.10+commit.cacc3b6b
v0.3.5-nightly.2016.8.10+commit.e6a031d4
v0.3.5-nightly.2016.8.10+commit.fc608391
v0.3.5-nightly.2016.8.8+commit.2fcc6ec3
v0.3.5-nightly.2016.8.8+commit.539afbee
v0.3.5-nightly.2016.8.8+commit.b13e5813
v0.3.5-nightly.2016.8.8+commit.c3ed550e
v0.3.5-nightly.2016.8.7+commit.f7af7de1
v0.3.5-nightly.2016.8.6+commit.e3c1bf73
v0.3.5-nightly.2016.8.5+commit.3c93a22d
v0.3.5-nightly.2016.8.5+commit.4542b7f8
v0.3.5-nightly.2016.8.5+commit.ff60ce98
v0.3.5-nightly.2016.8.4+commit.b83acfae
v0.3.5-nightly.2016.8.3+commit.3b21d980
v0.3.5-nightly.2016.7.21+commit.6610add6
v0.3.5-nightly.2016.7.19+commit.427deb43
v0.3.5-nightly.2016.7.1+commit.48238c9f
v0.3.5-nightly.2016.6.27+commit.2ccfea8b
v0.3.5-nightly.2016.6.21+commit.b23c3007
v0.3.5-nightly.2016.6.20+commit.9da08ac3
v0.3.5-nightly.2016.6.19+commit.5917c8e7
v0.3.5-nightly.2016.6.14+commit.371690f0
v0.3.4+commit.7dab8902
v0.3.4-nightly.2016.6.8+commit.093790d7
v0.3.4-nightly.2016.6.8+commit.ccddd6fd
v0.3.4-nightly.2016.6.8+commit.d593166d
v0.3.4-nightly.2016.6.6+commit.e97ac4fb
v0.3.4-nightly.2016.6.5+commit.0a0fc046
v0.3.4-nightly.2016.6.4+commit.602bcd38
v0.3.3+commit.4dc1cb14
v0.3.3-nightly.2016.5.31+commit.7dab8902
v0.3.3-nightly.2016.5.30+commit.4be92c0c
v0.3.3-nightly.2016.5.28+commit.eb57a0c3
v0.3.2+commit.81ae2a78
v0.3.2-nightly.2016.5.27+commit.4dc1cb14
v0.3.2-nightly.2016.5.25+commit.3c2056c6
v0.3.2-nightly.2016.5.24+commit.86c65c93
v0.3.2-nightly.2016.5.20+commit.e3c54185
v0.3.2-nightly.2016.5.19+commit.7a51852a
v0.3.2-nightly.2016.5.18+commit.cb865fb2
v0.3.2-nightly.2016.5.17+commit.0a37072e
v0.3.2-nightly.2016.5.13+commit.4b445b89
v0.3.2-nightly.2016.5.12+commit.73ede5bb
v0.3.2-nightly.2016.5.12+commit.c06051d3
v0.3.2-nightly.2016.5.6+commit.9e36bdda
v0.3.2-nightly.2016.5.5+commit.1b7e2d30
v0.3.2-nightly.2016.5.3+commit.aa4dcbb8
v0.3.2-nightly.2016.5.1+commit.bee80f1d
v0.3.2-nightly.2016.4.22+commit.dd4300d5
v0.3.1+commit.c492d9be
v0.3.1-nightly.2016.4.18+commit.81ae2a78
v0.3.1-nightly.2016.4.15+commit.7ba6c98e
v0.3.1-nightly.2016.4.13+commit.9137506a
v0.3.1-nightly.2016.4.12+commit.3ad5e821
v0.3.1-nightly.2016.4.7+commit.054bc2a6
v0.3.1-nightly.2016.4.5+commit.12797ed6
v0.3.1-nightly.2016.3.31+commit.c67926cf
v0.3.0+commit.11d67369
v0.3.0-nightly.2016.3.30+commit.2acdfc52
v0.3.0-nightly.2016.3.30+commit.c2cf8069
v0.3.0-nightly.2016.3.18+commit.e759a248
v0.3.0-nightly.2016.3.11+commit.1f9578ce
v0.2.2+commit.ef92f566
v0.2.2-nightly.2016.3.10+commit.34d714f7
v0.2.2-nightly.2016.3.2+commit.32f3a653
v0.2.2-nightly.2016.3.1+commit.02bb315d
v0.2.2-nightly.2016.2.22+commit.8339330b
v0.2.2-nightly.2016.2.19+commit.37381072
v0.2.2-nightly.2016.2.18+commit.565d7174
v0.2.1+commit.91a6b35f
v0.2.1-nightly.2016.2.13+commit.a14185a5
v0.2.1-nightly.2016.2.11+commit.c6c3c783
v0.2.1-nightly.2016.2.10+commit.7b5d96c4
v0.2.1-nightly.2016.2.3+commit.fad2d4df
v0.2.0+commit.4dc2445e
v0.2.0-nightly.2016.1.28+commit.bdbb7d8a
v0.2.0-nightly.2016.1.26+commit.9b9d10b4
v0.2.0-nightly.2016.1.24+commit.194679f7
v0.2.0-nightly.2016.1.20+commit.67c855c5
v0.2.0-nightly.2016.1.19+commit.d21c4276
v0.2.0-nightly.2016.1.18+commit.02340e84
v0.2.0-nightly.2016.1.15+commit.cc4b4f50
v0.2.0-nightly.2016.1.14+commit.ca45cfee
v0.2.0-nightly.2016.1.13+commit.d2f18c73
v0.2.0-nightly.2016.1.12+commit.02c1aacd
v0.2.0-nightly.2016.1.11+commit.aa645d11
v0.2.0-nightly.2016.1.5+commit.b158e48c
v0.2.0-nightly.2016.1.4+commit.252bd142
v0.2.0-nightly.2015.12.21+commit.6b711d05
v0.2.0-nightly.2015.12.18+commit.6c6295b7
v0.2.0-nightly.2015.12.17+commit.fe23cc82
v0.2.0-nightly.2015.12.15+commit.591a4f1f
v0.2.0-nightly.2015.12.14+commit.98684cca
v0.2.0-nightly.2015.12.10+commit.e7098958
v0.2.0-nightly.2015.12.7+commit.15a1468c
v0.2.0-nightly.2015.12.6+commit.ba8bc456
v0.2.0-nightly.2015.12.4+commit.02e4aa94
v0.1.7+commit.b4e666cc
v0.1.7-nightly.2015.11.26+commit.f86451cd
v0.1.7-nightly.2015.11.24+commit.8d16c6e9
v0.1.7-nightly.2015.11.23+commit.2554d610
v0.1.7-nightly.2015.11.19+commit.58110b27
v0.1.6+commit.d41f8b7c
v0.1.6-nightly.2015.11.16+commit.c881d103
v0.1.6-nightly.2015.11.12+commit.321b1ed2
v0.1.6-nightly.2015.11.7+commit.94ea61cb
v0.1.6-nightly.2015.11.3+commit.48ffa087
v0.1.6-nightly.2015.11.2+commit.665344ee
v0.1.6-nightly.2015.10.27+commit.22723da1
v0.1.6-nightly.2015.10.26+commit.e77deccf
v0.1.6-nightly.2015.10.23+commit.7a9f8d9f
v0.1.6-nightly.2015.10.22+commit.cb8f6633
v0.1.5+commit.23865e39
v0.1.5-nightly.2015.10.16+commit.52eaa477
v0.1.5-nightly.2015.10.15+commit.984ab6ab
v0.1.5-nightly.2015.10.13+commit.e11e10f8
v0.1.4+commit.5f6c3cdf
v0.1.4-nightly.2015.10.6+commit.d35a4b84
v0.1.4-nightly.2015.10.5+commit.7ff67629
v0.1.4-nightly.2015.10.5+commit.a33d173a
v0.1.4-nightly.2015.10.2+commit.795c894a
v0.1.3+commit.028f561d
v0.1.3-nightly.2015.9.29+commit.3ff932c8
v0.1.3-nightly.2015.9.28+commit.4457170b
v0.1.3-nightly.2015.9.25+commit.4457170b
The compiler version is specified in
pragma solidity X.X.X
. Use the compiler version rather than the nightly build. If using the Solidity compiler, run
solc —version
to check.
EVM Version
homestead
tangerineWhistle
spuriousDragon
byzantium
constantinople
petersburg
istanbul
berlin
london
default
The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version.
EVM version details
.
Optimization
No
Yes
If you enabled optimization during compilation, select yes.
Optimization runs
Enter the Solidity Contract Code
// File: contracts/compound/ComptrollerInterface.sol pragma solidity ^0.5.16; contract ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) bool public constant isComptroller = true; /*** Assets You Are In ***/ function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); function exitMarket(address cToken) external returns (uint); /*** Policy Hooks ***/ function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); function repayBorrowAllowed( address cToken, address payer, address borrower, uint repayAmount) external returns (uint); function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount) external returns (uint); function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) external returns (uint); function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint repayAmount) external view returns (uint, uint); function getLiquidationIncentive(address cTokenCollateral) public view returns (uint); function safetyGuardian() external view returns (address); } // File: contracts/compound/InterestRateModel.sol pragma solidity ^0.5.16; /** * @title Compound's InterestRateModel Interface * @author Compound */ contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** * @notice Calculates the current borrow interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @return The borrow rate per block (as a percentage, and scaled by 1e18) */ function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); /** * @notice Calculates the current supply interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per block (as a percentage, and scaled by 1e18) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); } // File: contracts/compound/CTokenInterfaces.sol pragma solidity ^0.5.16; contract CTokenStorage { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ uint internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-cToken operations */ ComptrollerInterface public comptroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; /** * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) */ uint internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint public totalReserves; /** * @notice Total number of tokens in circulation */ uint public totalSupply; /** * @notice Official record of token balances for each account */ mapping (address => uint) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ mapping (address => mapping (address => uint)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint principal; uint interestIndex; } /** * @notice Mapping of account addresses to outstanding borrow balances */ mapping(address => BorrowSnapshot) internal accountBorrows; } contract CTokenInterface is CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint mintAmount, uint mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when comptroller is changed */ event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); /** * @notice Event emitted when the reserves are added */ event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint amount); /** * @notice Failure event */ event Failure(uint error, uint info, uint detail); /*** User Interface ***/ function transfer(address dst, uint amount) external returns (bool); function transferFrom(address src, address dst, uint amount) external returns (bool); function approve(address spender, uint amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint); function balanceOf(address owner) external view returns (uint); function balanceOfUnderlying(address owner) external returns (uint); function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); function borrowRatePerBlock() external view returns (uint); function supplyRatePerBlock() external view returns (uint); function totalBorrowsCurrent() external returns (uint); function borrowBalanceCurrent(address account) external returns (uint); function borrowBalanceStored(address account) public view returns (uint); function exchangeRateCurrent() public returns (uint); function exchangeRateStored() public view returns (uint); function getCash() external view returns (uint); function accrueInterest() public returns (uint); function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); /*** Admin Functions ***/ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); function _acceptAdmin() external returns (uint); function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); function _reduceReserves(uint reduceAmount) external returns (uint); function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); } contract CErc20Storage { /** * @notice Underlying asset for this CToken */ address public underlying; } contract CErc20Interface is CErc20Storage { /*** User Interface ***/ function mint(uint mintAmount) external returns (uint); function redeem(uint redeemTokens) external returns (uint); function redeemUnderlying(uint redeemAmount) external returns (uint); function borrow(uint borrowAmount) external returns (uint); function repayBorrow(uint repayAmount) external returns (uint); function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint); /*** Admin Functions ***/ function _addReserves(uint addAmount) external returns (uint); } contract CDelegationStorage { /** * @notice Implementation address for this contract */ address public implementation; } contract CDelegatorInterface is CDelegationStorage { /** * @notice Emitted when implementation is changed */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public; } contract CDelegateInterface is CDelegationStorage { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) public; /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() public; } // File: contracts/compound/ErrorReporter.sol pragma solidity ^0.5.16; contract ComptrollerErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, COMPTROLLER_MISMATCH, INSUFFICIENT_SHORTFALL, INSUFFICIENT_LIQUIDITY, INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, MARKET_NOT_ENTERED, // no longer possible MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, NONZERO_BORROW_BALANCE, PRICE_ERROR, REJECTION, SNAPSHOT_ERROR, TOO_MANY_ASSETS, TOO_MUCH_REPAY } enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, EXIT_MARKET_BALANCE_OWED, EXIT_MARKET_REJECTION, SET_CLOSE_FACTOR_OWNER_CHECK, SET_CLOSE_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_NO_EXISTS, SET_COLLATERAL_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_WITHOUT_PRICE, SET_IMPLEMENTATION_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_VALIDATION, SET_MAX_ASSETS_OWNER_CHECK, SET_PENDING_ADMIN_OWNER_CHECK, SET_PENDING_IMPLEMENTATION_OWNER_CHECK, SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, SET_PAUSE_GUARDIAN_OWNER_CHECK } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. **/ event Failure(uint error, uint info, uint detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint) { emit Failure(uint(err), uint(info), 0); return uint(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { emit Failure(uint(err), uint(info), opaqueError); return uint(err); } } contract TokenErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, BAD_INPUT, COMPTROLLER_REJECTION, COMPTROLLER_CALCULATION_ERROR, INTEREST_RATE_MODEL_ERROR, INVALID_ACCOUNT_PAIR, INVALID_CLOSE_AMOUNT_REQUESTED, INVALID_COLLATERAL_FACTOR, MATH_ERROR, MARKET_NOT_FRESH, MARKET_NOT_LISTED, TOKEN_INSUFFICIENT_ALLOWANCE, TOKEN_INSUFFICIENT_BALANCE, TOKEN_INSUFFICIENT_CASH, TOKEN_TRANSFER_IN_FAILED, TOKEN_TRANSFER_OUT_FAILED } /* * Note: FailureInfo (but not Error) is kept in alphabetical order * This is because FailureInfo grows significantly faster, and * the order of Error has some meaning, while the order of FailureInfo * is entirely arbitrary. */ enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED, ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, BORROW_ACCRUE_INTEREST_FAILED, BORROW_CASH_NOT_AVAILABLE, BORROW_FRESHNESS_CHECK, BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, BORROW_MARKET_NOT_LISTED, BORROW_COMPTROLLER_REJECTION, LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED, LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED, LIQUIDATE_COLLATERAL_FRESHNESS_CHECK, LIQUIDATE_COMPTROLLER_REJECTION, LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED, LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX, LIQUIDATE_CLOSE_AMOUNT_IS_ZERO, LIQUIDATE_FRESHNESS_CHECK, LIQUIDATE_LIQUIDATOR_IS_BORROWER, LIQUIDATE_REPAY_BORROW_FRESH_FAILED, LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER, LIQUIDATE_SEIZE_TOO_MUCH, MINT_ACCRUE_INTEREST_FAILED, MINT_COMPTROLLER_REJECTION, MINT_EXCHANGE_CALCULATION_FAILED, MINT_EXCHANGE_RATE_READ_FAILED, MINT_FRESHNESS_CHECK, MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, MINT_TRANSFER_IN_FAILED, MINT_TRANSFER_IN_NOT_POSSIBLE, REDEEM_ACCRUE_INTEREST_FAILED, REDEEM_COMPTROLLER_REJECTION, REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, REDEEM_EXCHANGE_RATE_READ_FAILED, REDEEM_FRESHNESS_CHECK, REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, REDEEM_TRANSFER_OUT_NOT_POSSIBLE, REDUCE_RESERVES_ACCRUE_INTEREST_FAILED, REDUCE_RESERVES_ADMIN_CHECK, REDUCE_RESERVES_CASH_NOT_AVAILABLE, REDUCE_RESERVES_FRESH_CHECK, REDUCE_RESERVES_VALIDATION, REPAY_BEHALF_ACCRUE_INTEREST_FAILED, REPAY_BORROW_ACCRUE_INTEREST_FAILED, REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, REPAY_BORROW_COMPTROLLER_REJECTION, REPAY_BORROW_FRESHNESS_CHECK, REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_VALIDATION, SET_COMPTROLLER_OWNER_CHECK, SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED, SET_INTEREST_RATE_MODEL_FRESH_CHECK, SET_INTEREST_RATE_MODEL_OWNER_CHECK, SET_MAX_ASSETS_OWNER_CHECK, SET_ORACLE_MARKET_NOT_LISTED, SET_PENDING_ADMIN_OWNER_CHECK, SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED, SET_RESERVE_FACTOR_ADMIN_CHECK, SET_RESERVE_FACTOR_FRESH_CHECK, SET_RESERVE_FACTOR_BOUNDS_CHECK, TRANSFER_COMPTROLLER_REJECTION, TRANSFER_NOT_ALLOWED, TRANSFER_NOT_ENOUGH, TRANSFER_TOO_MUCH, ADD_RESERVES_ACCRUE_INTEREST_FAILED, ADD_RESERVES_FRESH_CHECK, ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. **/ event Failure(uint error, uint info, uint detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint) { emit Failure(uint(err), uint(info), 0); return uint(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { emit Failure(uint(err), uint(info), opaqueError); return uint(err); } } // File: contracts/compound/CarefulMath.sol pragma solidity ^0.5.16; /** * @title Careful Math * @author Compound * @notice Derived from OpenZeppelin's SafeMath library * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol */ contract CarefulMath { /** * @dev Possible error codes that we can return */ enum MathError { NO_ERROR, DIVISION_BY_ZERO, INTEGER_OVERFLOW, INTEGER_UNDERFLOW } /** * @dev Multiplies two numbers, returns an error on overflow. */ function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { if (a == 0) { return (MathError.NO_ERROR, 0); } uint c = a * b; if (c / a != b) { return (MathError.INTEGER_OVERFLOW, 0); } else { return (MathError.NO_ERROR, c); } } /** * @dev Integer division of two numbers, truncating the quotient. */ function divUInt(uint a, uint b) internal pure returns (MathError, uint) { if (b == 0) { return (MathError.DIVISION_BY_ZERO, 0); } return (MathError.NO_ERROR, a / b); } /** * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). */ function subUInt(uint a, uint b) internal pure returns (MathError, uint) { if (b <= a) { return (MathError.NO_ERROR, a - b); } else { return (MathError.INTEGER_UNDERFLOW, 0); } } /** * @dev Adds two numbers, returns an error on overflow. */ function addUInt(uint a, uint b) internal pure returns (MathError, uint) { uint c = a + b; if (c >= a) { return (MathError.NO_ERROR, c); } else { return (MathError.INTEGER_OVERFLOW, 0); } } /** * @dev add a and b and then subtract c */ function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { (MathError err0, uint sum) = addUInt(a, b); if (err0 != MathError.NO_ERROR) { return (err0, 0); } return subUInt(sum, c); } } // File: contracts/compound/Exponential.sol pragma solidity ^0.5.16; /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract Exponential is CarefulMath { uint constant expScale = 1e18; uint constant doubleScale = 1e36; uint constant halfExpScale = expScale/2; uint constant mantissaOne = expScale; struct Exp { uint mantissa; } struct Double { uint mantissa; } /** * @dev Creates an exponential from numerator and denominator values. * Note: Returns an error if (`num` * 10e18) > MAX_INT, * or if `denom` is zero. */ function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) { (MathError err0, uint scaledNumerator) = mulUInt(num, expScale); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } (MathError err1, uint rational) = divUInt(scaledNumerator, denom); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: rational})); } /** * @dev Adds two exponentials, returning a new exponential. */ function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { (MathError error, uint result) = addUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** * @dev Subtracts two exponentials, returning a new exponential. */ function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { (MathError error, uint result) = subUInt(a.mantissa, b.mantissa); return (error, Exp({mantissa: result})); } /** * @dev Multiply an Exp by a scalar, returning a new Exp. */ function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa})); } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); } return (MathError.NO_ERROR, truncate(product)); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) { (MathError err, Exp memory product) = mulScalar(a, scalar); if (err != MathError.NO_ERROR) { return (err, 0); } return addUInt(truncate(product), addend); } /** * @dev Divide an Exp by a scalar, returning a new Exp. */ function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa})); } /** * @dev Divide a scalar by an Exp, returning a new Exp. */ function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) { /* We are doing this as: getExp(mulUInt(expScale, scalar), divisor.mantissa) How it works: Exp = a / b; Scalar = s; `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` */ (MathError err0, uint numerator) = mulUInt(expScale, scalar); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } return getExp(numerator, divisor.mantissa); } /** * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. */ function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) { (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor); if (err != MathError.NO_ERROR) { return (err, 0); } return (MathError.NO_ERROR, truncate(fraction)); } /** * @dev Multiplies two exponentials, returning a new exponential. */ function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); if (err0 != MathError.NO_ERROR) { return (err0, Exp({mantissa: 0})); } // We add half the scale before dividing so that we get rounding instead of truncation. // See "Listing 6" and text above it at https://accu.org/index.php/journals/1717 // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18. (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); if (err1 != MathError.NO_ERROR) { return (err1, Exp({mantissa: 0})); } (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale); // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero. assert(err2 == MathError.NO_ERROR); return (MathError.NO_ERROR, Exp({mantissa: product})); } /** * @dev Multiplies two exponentials given their mantissas, returning a new exponential. */ function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) { return mulExp(Exp({mantissa: a}), Exp({mantissa: b})); } /** * @dev Multiplies three exponentials, returning a new exponential. */ function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) { (MathError err, Exp memory ab) = mulExp(a, b); if (err != MathError.NO_ERROR) { return (err, ab); } return mulExp(ab, c); } /** * @dev Divides two exponentials, returning a new exponential. * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) */ function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { return getExp(a.mantissa, b.mantissa); } /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ function truncate(Exp memory exp) pure internal returns (uint) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / expScale; } /** * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa < right.mantissa; } /** * @dev Checks if left Exp <= right Exp. */ function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa <= right.mantissa; } /** * @dev Checks if left Exp > right Exp. */ function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa > right.mantissa; } /** * @dev returns true if Exp is exactly zero */ function isZeroExp(Exp memory value) pure internal returns (bool) { return value.mantissa == 0; } function safe224(uint n, string memory errorMessage) pure internal returns (uint224) { require(n < 2**224, errorMessage); return uint224(n); } function safe32(uint n, string memory errorMessage) pure internal returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(uint a, uint b) pure internal returns (uint) { return add_(a, b, "addition overflow"); } function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { uint c = a + b; require(c >= a, errorMessage); return c; } function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(uint a, uint b) pure internal returns (uint) { return sub_(a, b, "subtraction underflow"); } function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { require(b <= a, errorMessage); return a - b; } function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); } function mul_(Exp memory a, uint b) pure internal returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Exp memory b) pure internal returns (uint) { return mul_(a, b.mantissa) / expScale; } function mul_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); } function mul_(Double memory a, uint b) pure internal returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Double memory b) pure internal returns (uint) { return mul_(a, b.mantissa) / doubleScale; } function mul_(uint a, uint b) pure internal returns (uint) { return mul_(a, b, "multiplication overflow"); } function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { if (a == 0 || b == 0) { return 0; } uint c = a * b; require(c / a == b, errorMessage); return c; } function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); } function div_(Exp memory a, uint b) pure internal returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Exp memory b) pure internal returns (uint) { return div_(mul_(a, expScale), b.mantissa); } function div_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); } function div_(Double memory a, uint b) pure internal returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Double memory b) pure internal returns (uint) { return div_(mul_(a, doubleScale), b.mantissa); } function div_(uint a, uint b) pure internal returns (uint) { return div_(a, b, "divide by zero"); } function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { require(b > 0, errorMessage); return a / b; } function fraction(uint a, uint b) pure internal returns (Double memory) { return Double({mantissa: div_(mul_(a, doubleScale), b)}); } } // File: contracts/compound/EIP20Interface.sol pragma solidity ^0.5.16; /** * @title ERC 20 Token Standard Interface * https://eips.ethereum.org/EIPS/eip-20 */ interface EIP20Interface { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return The balance */ function balanceOf(address owner) external view returns (uint256 balance); /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external returns (bool success); /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) external returns (bool success); /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); } // File: contracts/compound/EIP20NonStandardInterface.sol pragma solidity ^0.5.16; /** * @title EIP20NonStandardInterface * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ interface EIP20NonStandardInterface { /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return The balance */ function balanceOf(address owner) external view returns (uint256 balance); /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transfer(address dst, uint256 amount) external; /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transferFrom(address src, address dst, uint256 amount) external; /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); } // File: contracts/compound/CToken.sol pragma solidity ^0.5.16; /** * @title Compound's CToken Contract * @notice Abstract base for CTokens * @author Compound */ contract CToken is CTokenInterface, Exponential, TokenErrorReporter { /** * @notice Initialize the money market * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ EIP-20 name of this token * @param symbol_ EIP-20 symbol of this token * @param decimals_ EIP-20 decimal precision of this token */ function initialize(ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_) public { require(msg.sender == admin, "!admin"); require(accrualBlockNumber == 0 && borrowIndex == 0, "!once"); // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; require(initialExchangeRateMantissa > 0, "zero"); // Set the comptroller uint err = _setComptroller(comptroller_); require(err == uint(Error.NO_ERROR), "comptroller failed"); // Initialize block number and borrow index (block number mocks depend on comptroller being set) accrualBlockNumber = getBlockNumber(); borrowIndex = mantissaOne; // Set the interest rate model (depends on block number / borrow index) err = _setInterestRateModelFresh(interestRateModel_); require(err == uint(Error.NO_ERROR), "interest rate model failed"); name = name_; symbol = symbol_; decimals = decimals_; // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund) _notEntered = true; } /** * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` * @dev Called by both `transfer` and `transferFrom` internally * @param spender The address of the account performing the transfer * @param src The address of the source account * @param dst The address of the destination account * @param tokens The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { /* Fail if transfer not allowed */ uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens); if (allowed != 0) { return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed); } /* Do not allow self-transfers */ if (src == dst) { return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED); } /* Get the allowance, infinite for the account owner */ uint startingAllowance = 0; if (spender == src) { startingAllowance = uint(-1); } else { startingAllowance = transferAllowances[src][spender]; } /* Do the calculations, checking for {under,over}flow */ MathError mathErr; uint allowanceNew; uint srcTokensNew; uint dstTokensNew; (mathErr, allowanceNew) = subUInt(startingAllowance, tokens); if (mathErr != MathError.NO_ERROR) { return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ALLOWED); } (mathErr, srcTokensNew) = subUInt(accountTokens[src], tokens); if (mathErr != MathError.NO_ERROR) { return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH); } (mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens); if (mathErr != MathError.NO_ERROR) { return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accountTokens[src] = srcTokensNew; accountTokens[dst] = dstTokensNew; /* Eat some of the allowance (if necessary) */ if (startingAllowance != uint(-1)) { transferAllowances[src][spender] = allowanceNew; } /* We emit a Transfer event */ emit Transfer(src, dst, tokens); return uint(Error.NO_ERROR); } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external nonReentrant returns (bool) { return transferTokens(msg.sender, msg.sender, dst, amount) == uint(Error.NO_ERROR); } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) external nonReentrant returns (bool) { return transferTokens(msg.sender, src, dst, amount) == uint(Error.NO_ERROR); } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool) { address src = msg.sender; transferAllowances[src][spender] = amount; emit Approval(src, spender, amount); return true; } /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256) { return transferAllowances[owner][spender]; } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) external view returns (uint256) { return accountTokens[owner]; } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) external returns (uint) { Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); (MathError mErr, uint balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]); require(mErr == MathError.NO_ERROR); return balance; } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by comptroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) { uint cTokenBalance = accountTokens[account]; uint borrowBalance; uint exchangeRateMantissa; MathError mErr; (mErr, borrowBalance) = borrowBalanceStoredInternal(account); if (mErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0, 0, 0); } (mErr, exchangeRateMantissa) = exchangeRateStoredInternal(); if (mErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0, 0, 0); } return (uint(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa); } /** * @dev Function to simply retrieve block number * This exists mainly for inheriting test contracts to stub this result. */ function getBlockNumber() internal view returns (uint) { return block.number; } /** * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ function borrowRatePerBlock() external view returns (uint) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } /** * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ function supplyRatePerBlock() external view returns (uint) { return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() external nonReentrant returns (uint) { require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); return totalBorrows; } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) external nonReentrant returns (uint) { require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); return borrowBalanceStored(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) public view returns (uint) { (MathError err, uint result) = borrowBalanceStoredInternal(account); require(err == MathError.NO_ERROR, "borrowBalanceStoredInternal failed"); return result; } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return (error code, the calculated balance or 0 if error code is non-zero) */ function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint) { /* Note: we do not assert that the market is up to date */ MathError mathErr; uint principalTimesIndex; uint result; /* Get borrowBalance and borrowIndex */ BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; /* If borrowBalance = 0 then borrowIndex is likely also 0. * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. */ if (borrowSnapshot.principal == 0) { return (MathError.NO_ERROR, 0); } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ (mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } (mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } return (MathError.NO_ERROR, result); } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public nonReentrant returns (uint) { require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); return exchangeRateStored(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() public view returns (uint) { (MathError err, uint result) = exchangeRateStoredInternal(); require(err == MathError.NO_ERROR, "exchangeRateStoredInternal failed"); return result; } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return (error code, calculated exchange rate scaled by 1e18) */ function exchangeRateStoredInternal() internal view returns (MathError, uint) { uint _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return (MathError.NO_ERROR, initialExchangeRateMantissa); } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint totalCash = getCashPrior(); uint cashPlusBorrowsMinusReserves; Exp memory exchangeRate; MathError mathErr; (mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } return (MathError.NO_ERROR, exchangeRate.mantissa); } } /** * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() external view returns (uint) { return getCashPrior(); } /** * @notice Applies accrued interest to total borrows and reserves * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() public returns (uint) { /* Remember the initial block number */ uint currentBlockNumber = getBlockNumber(); uint accrualBlockNumberPrior = accrualBlockNumber; /* Short-circuit accumulating 0 interest */ if (accrualBlockNumberPrior == currentBlockNumber) { return uint(Error.NO_ERROR); } /* Read the previous values out of storage */ uint cashPrior = getCashPrior(); uint borrowsPrior = totalBorrows; uint reservesPrior = totalReserves; uint borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of blocks elapsed since the last accrual */ (MathError mathErr, uint blockDelta) = subUInt(currentBlockNumber, accrualBlockNumberPrior); require(mathErr == MathError.NO_ERROR, "could not calculate block delta"); /* * Calculate the interest accumulated into borrows and reserves and the new index: * simpleInterestFactor = borrowRate * blockDelta * interestAccumulated = simpleInterestFactor * totalBorrows * totalBorrowsNew = interestAccumulated + totalBorrows * totalReservesNew = interestAccumulated * reserveFactor + totalReserves * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex */ Exp memory simpleInterestFactor; uint interestAccumulated; uint totalBorrowsNew; uint totalReservesNew; uint borrowIndexNew; (mathErr, simpleInterestFactor) = mulScalar(Exp({mantissa: borrowRateMantissa}), blockDelta); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, uint(mathErr)); } (mathErr, interestAccumulated) = mulScalarTruncate(simpleInterestFactor, borrowsPrior); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint(mathErr)); } (mathErr, totalBorrowsNew) = addUInt(interestAccumulated, borrowsPrior); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint(mathErr)); } (mathErr, totalReservesNew) = mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint(mathErr)); } (mathErr, borrowIndexNew) = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint(mathErr)); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accrualBlockNumber = currentBlockNumber; borrowIndex = borrowIndexNew; totalBorrows = totalBorrowsNew; totalReserves = totalReservesNew; /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); return uint(Error.NO_ERROR); } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. */ function mintInternal(uint mintAmount) internal nonReentrant returns (uint, uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0); } // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to return mintFresh(msg.sender, mintAmount); } struct MintLocalVars { Error err; MathError mathErr; uint exchangeRateMantissa; uint mintTokens; uint totalSupplyNew; uint accountTokensNew; uint actualMintAmount; } /** * @notice User supplies assets into the market and receives cTokens in exchange * @dev Assumes interest has already been accrued up to the current block * @param minter The address of the account which is supplying the assets * @param mintAmount The amount of the underlying asset to supply * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount. */ function mintFresh(address minter, uint mintAmount) internal returns (uint, uint) { /* Fail if mint not allowed */ uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount); if (allowed != 0) { return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0); } MintLocalVars memory vars; (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal(); if (vars.mathErr != MathError.NO_ERROR) { return (failOpaque(Error.MATH_ERROR, FailureInfo.MINT_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr)), 0); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call `doTransferIn` for the minter and the mintAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * `doTransferIn` reverts if anything goes wrong, since we can't be sure if * side-effects occurred. The function returns the amount actually transferred, * in case of a fee. On success, the cToken holds an additional `actualMintAmount` * of cash. */ vars.actualMintAmount = doTransferIn(minter, mintAmount); /* * We get the current exchange rate and calculate the number of cTokens to be minted: * mintTokens = actualMintAmount / exchangeRate */ (vars.mathErr, vars.mintTokens) = divScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa})); require(vars.mathErr == MathError.NO_ERROR); /* * We calculate the new total supply of cTokens and minter token balance, checking for overflow: * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens */ (vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens); require(vars.mathErr == MathError.NO_ERROR); (vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens); require(vars.mathErr == MathError.NO_ERROR); /* We write previously calculated values into storage */ totalSupply = vars.totalSupplyNew; accountTokens[minter] = vars.accountTokensNew; /* We emit a Mint event, and a Transfer event */ emit Mint(minter, vars.actualMintAmount, vars.mintTokens); emit Transfer(address(this), minter, vars.mintTokens); return (uint(Error.NO_ERROR), vars.actualMintAmount); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemInternal(uint redeemTokens) internal nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to return redeemFresh(msg.sender, redeemTokens, 0); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to receive from redeeming cTokens * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED); } // redeemFresh emits redeem-specific logs on errors, so we don't need to return redeemFresh(msg.sender, 0, redeemAmount); } struct RedeemLocalVars { Error err; MathError mathErr; uint exchangeRateMantissa; uint redeemTokens; uint redeemAmount; uint totalSupplyNew; uint accountTokensNew; } /** * @notice User redeems cTokens in exchange for the underlying asset * @dev Assumes interest has already been accrued up to the current block * @param redeemer The address of the account which is redeeming the tokens * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) { require(redeemTokensIn == 0 || redeemAmountIn == 0, "!zero"); RedeemLocalVars memory vars; /* exchangeRate = invoke Exchange Rate Stored() */ (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal(); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr)); } /* If redeemTokensIn > 0: */ if (redeemTokensIn > 0) { /* * We calculate the exchange rate and the amount of underlying to be redeemed: * redeemTokens = redeemTokensIn * redeemAmount = redeemTokensIn x exchangeRateCurrent */ vars.redeemTokens = redeemTokensIn; (vars.mathErr, vars.redeemAmount) = mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint(vars.mathErr)); } } else { /* * We get the current exchange rate and calculate the amount to be redeemed: * redeemTokens = redeemAmountIn / exchangeRate * redeemAmount = redeemAmountIn */ (vars.mathErr, vars.redeemTokens) = divScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa})); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, uint(vars.mathErr)); } vars.redeemAmount = redeemAmountIn; } /* Fail if redeem not allowed */ uint allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens); if (allowed != 0) { return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK); } /* * We calculate the new total supply and redeemer balance, checking for underflow: * totalSupplyNew = totalSupply - redeemTokens * accountTokensNew = accountTokens[redeemer] - redeemTokens */ (vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, uint(vars.mathErr)); } (vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)); } /* Fail gracefully if protocol has insufficient cash */ if (getCashPrior() < vars.redeemAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write previously calculated values into storage */ totalSupply = vars.totalSupplyNew; accountTokens[redeemer] = vars.accountTokensNew; /* * We invoke doTransferOut for the redeemer and the redeemAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken has redeemAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(redeemer, vars.redeemAmount); /* We emit a Transfer event, and a Redeem event */ emit Transfer(redeemer, address(this), vars.redeemTokens); emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens); /* We call the defense hook */ comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens); return uint(Error.NO_ERROR); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrowInternal(uint borrowAmount) internal nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED); } // borrowFresh emits borrow-specific logs on errors, so we don't need to return borrowFresh(msg.sender, borrowAmount); } struct BorrowLocalVars { MathError mathErr; uint accountBorrows; uint accountBorrowsNew; uint totalBorrowsNew; } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrowFresh(address payable borrower, uint borrowAmount) internal returns (uint) { /* Fail if borrow not allowed */ uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE); } BorrowLocalVars memory vars; /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowsNew = accountBorrows + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)); } (vars.mathErr, vars.accountBorrowsNew) = addUInt(vars.accountBorrows, borrowAmount); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)); } (vars.mathErr, vars.totalBorrowsNew) = addUInt(totalBorrows, borrowAmount); if (vars.mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = vars.totalBorrowsNew; /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(borrower, borrowAmount); /* We emit a Borrow event */ emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); return uint(Error.NO_ERROR); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint, uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to return repayBorrowFresh(msg.sender, msg.sender, repayAmount); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint, uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0); } // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to return repayBorrowFresh(msg.sender, borrower, repayAmount); } struct RepayBorrowLocalVars { Error err; MathError mathErr; uint repayAmount; uint borrowerIndex; uint accountBorrows; uint accountBorrowsNew; uint totalBorrowsNew; uint actualRepayAmount; } /** * @notice Borrows are repaid by another user (possibly the borrower). * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of undelrying tokens being returned * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint, uint) { /* Fail if repayBorrow not allowed */ uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); if (allowed != 0) { return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0); } RepayBorrowLocalVars memory vars; /* We remember the original borrowerIndex for verification purposes */ vars.borrowerIndex = accountBorrows[borrower].interestIndex; /* We fetch the amount the borrower owes, with accumulated interest */ (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower); if (vars.mathErr != MathError.NO_ERROR) { return (failOpaque(Error.MATH_ERROR, FailureInfo.REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)), 0); } /* If repayAmount == -1, repayAmount = accountBorrows */ if (repayAmount == uint(-1)) { vars.repayAmount = vars.accountBorrows; } else { vars.repayAmount = repayAmount; } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the payer and the repayAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ (vars.mathErr, vars.accountBorrowsNew) = subUInt(vars.accountBorrows, vars.actualRepayAmount); require(vars.mathErr == MathError.NO_ERROR); (vars.mathErr, vars.totalBorrowsNew) = subUInt(totalBorrows, vars.actualRepayAmount); require(vars.mathErr == MathError.NO_ERROR, "!totalBorrowsNew"); /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = vars.accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = vars.totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew); return (uint(Error.NO_ERROR), vars.actualRepayAmount); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function liquidateBorrowInternal(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal nonReentrant returns (uint, uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0); } error = cTokenCollateral.accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to return liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral); } /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param liquidator The address repaying the borrow and seizing collateral * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal returns (uint, uint) { /* Fail if liquidate not allowed */ uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount); if (allowed != 0) { return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0); } /* Verify cTokenCollateral market's block number equals current block number */ if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0); } /* Fail if repayAmount = -1 */ if (repayAmount == uint(-1)) { return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0); } /* Fail if repayBorrow fails */ (uint repayBorrowError, uint actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount); if (repayBorrowError != uint(Error.NO_ERROR)) { return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(cTokenCollateral), actualRepayAmount); require(amountSeizeError == uint(Error.NO_ERROR), "!seizeTokens"); /* Revert if borrower collateral token balance < seizeTokens */ require(cTokenCollateral.balanceOf(borrower) >= seizeTokens); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call uint seizeError; if (address(cTokenCollateral) == address(this)) { seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { seizeError = cTokenCollateral.seize(liquidator, borrower, seizeTokens); } /* Revert if seize tokens fails (since we cannot be sure of side effects) */ require(seizeError == uint(Error.NO_ERROR), "token seizure failed"); /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens); return (uint(Error.NO_ERROR), actualRepayAmount); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another cToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize(address liquidator, address borrower, uint seizeTokens) external nonReentrant returns (uint) { return seizeInternal(msg.sender, liquidator, borrower, seizeTokens); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint); /*** Admin Functions ***/ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return uint(Error.NO_ERROR); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() external returns (uint) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return uint(Error.NO_ERROR); } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK); } ComptrollerInterface oldComptroller = comptroller; // Ensure invoke comptroller.isComptroller() returns true require(newComptroller.isComptroller(), "!comptroller"); // Set market's comptroller to newComptroller comptroller = newComptroller; // Emit NewComptroller(oldComptroller, newComptroller) emit NewComptroller(oldComptroller, newComptroller); return uint(Error.NO_ERROR); } /** * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint newReserveFactorMantissa) external nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed. return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED); } // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to. return _setReserveFactorFresh(newReserveFactorMantissa); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK); } // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK); } uint oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); return uint(Error.NO_ERROR); } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED); } // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. (error, ) = _addReservesFresh(addAmount); return error; } /** * @notice Add reserves by transferring from caller * @dev Requires fresh interest accrual * @param addAmount Amount of addition to reserves * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees */ function _addReservesFresh(uint addAmount) internal returns (uint, uint) { // totalReserves + actualAddAmount uint totalReservesNew; uint actualAddAmount; // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the caller and the addAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional addAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ actualAddAmount = doTransferIn(msg.sender, addAmount); totalReservesNew = totalReserves + actualAddAmount; /* Revert on overflow */ require(totalReservesNew >= totalReserves, "add reserves unexpected overflow"); // Store reserves[n+1] = reserves[n] + actualAddAmount totalReserves = totalReservesNew; /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); /* Return (NO_ERROR, actualAddAmount) */ return (uint(Error.NO_ERROR), actualAddAmount); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint reduceAmount) external nonReentrant returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed. return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED); } // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to. return _reduceReservesFresh(reduceAmount); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReservesFresh(uint reduceAmount) internal returns (uint) { // totalReserves - reduceAmount uint totalReservesNew; // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = totalReserves - reduceAmount; // We checked reduceAmount <= totalReserves above, so this should never revert. require(totalReservesNew <= totalReserves, "reduce reserves unexpected underflow"); // Store reserves[n+1] = reserves[n] - reduceAmount totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. doTransferOut(admin, reduceAmount); emit ReservesReduced(admin, reduceAmount, totalReservesNew); return uint(Error.NO_ERROR); } /** * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) { uint error = accrueInterest(); if (error != uint(Error.NO_ERROR)) { // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED); } // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to. return _setInterestRateModelFresh(newInterestRateModel); } /** * @notice updates the interest rate model (*requires fresh interest accrual) * @dev Admin function to update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK); } // Track the market's current interest rate model oldInterestRateModel = interestRateModel; // Ensure invoke newInterestRateModel.isInterestRateModel() returns true require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); // Set the interest rate model to newInterestRateModel interestRateModel = newInterestRateModel; // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); return uint(Error.NO_ERROR); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying owned by this contract */ function getCashPrior() internal view returns (uint); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ function doTransferIn(address from, uint amount) internal returns (uint); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ function doTransferOut(address payable to, uint amount) internal; /*** Reentrancy Guard ***/ /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() { require(_notEntered, "re-entered"); _notEntered = false; _; _notEntered = true; // get a gas-refund post-Istanbul } } // File: contracts/compound/PriceOracle.sol pragma solidity ^0.5.16; contract PriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /** * @notice Get the underlying price of a cToken asset * @param cToken The cToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getUnderlyingPrice(CToken cToken) external view returns (uint); } // File: contracts/compound/ComptrollerStorage.sol pragma solidity ^0.5.16; contract UnitrollerAdminStorage { /** * @notice Administrator for this contract */ address public admin; /** * @notice Pending administrator for this contract */ address public pendingAdmin; /** * @notice Active brains of Unitroller */ address public comptrollerImplementation; /** * @notice Pending brains of Unitroller */ address public pendingComptrollerImplementation; } contract ComptrollerV1Storage is UnitrollerAdminStorage { /** * @notice Oracle which gives the price of any given asset */ PriceOracle public oracle; /** * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow */ uint public closeFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ uint public liquidationIncentiveMantissa; /** * @notice Max number of assets a single account can participate in (borrow or use as collateral) */ uint public maxAssets; /** * @notice Per-account mapping of "assets you are in", capped by maxAssets */ mapping(address => CToken[]) public accountAssets; } contract ComptrollerV2Storage is ComptrollerV1Storage { struct Market { /// @notice Whether or not this market is listed bool isListed; /** * @notice Multiplier representing the most one can borrow against their collateral in this market. * For instance, 0.9 to allow borrowing 90% of collateral value. * Must be between 0 and 1, and stored as a mantissa. */ uint collateralFactorMantissa; /// @notice Per-market mapping of "accounts in this asset" mapping(address => bool) accountMembership; /// @notice Whether or not this market receives COMP bool isComped; /** * @notice Multiplier representing the most one can borrow the asset. * For instance, 0.5 to allow borrowing this asset 50% * collateral value * collateralFactor. * When calculating equity, 0.5 with 100 borrow balance will produce 200 borrow value * Must be between (0, 1], and stored as a mantissa. */ uint borrowFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ uint liquidationIncentiveMantissa; } /** * @notice Official mapping of cTokens -> Market metadata * @dev Used e.g. to determine if a market is supported */ mapping(address => Market) public markets; /** * @notice The Pause Guardian can pause certain actions as a safety mechanism. * Actions which allow users to remove their own assets cannot be paused. * Liquidation / seizing / transfer can only be paused globally, not by market. */ address public pauseGuardian; bool public _mintGuardianPaused; bool public _borrowGuardianPaused; bool public transferGuardianPaused; bool public seizeGuardianPaused; mapping(address => bool) public mintGuardianPaused; mapping(address => bool) public borrowGuardianPaused; } contract ComptrollerV3Storage is ComptrollerV2Storage { struct CompMarketState { /// @notice The market's last updated compBorrowIndex or compSupplyIndex uint224 index; /// @notice The block number the index was last updated at uint32 block; } /// @notice A list of all markets CToken[] public allMarkets; /// @notice The rate at which the flywheel distributes COMP, per block uint public compRate; /// @notice The portion of compRate that each market currently receives mapping(address => uint) public compSpeeds; /// @notice The COMP market supply state for each market mapping(address => CompMarketState) public compSupplyState; /// @notice The COMP market borrow state for each market mapping(address => CompMarketState) public compBorrowState; /// @notice The COMP borrow index for each market for each supplier as of the last time they accrued COMP mapping(address => mapping(address => uint)) public compSupplierIndex; /// @notice The COMP borrow index for each market for each borrower as of the last time they accrued COMP mapping(address => mapping(address => uint)) public compBorrowerIndex; /// @notice The COMP accrued but not yet transferred to each user mapping(address => uint) public compAccrued; } // File: contracts/compound/Unitroller.sol pragma solidity ^0.5.16; /** * @title ComptrollerCore * @dev Storage for the comptroller is at this address, while execution is delegated to the `comptrollerImplementation`. * CTokens should reference this contract as their comptroller. */ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { /** * @notice Emitted when pendingComptrollerImplementation is changed */ event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation); /** * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); constructor() public { // Set admin to caller admin = msg.sender; } /*** Admin Functions ***/ function _setPendingImplementation(address newPendingImplementation) public returns (uint) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK); } address oldPendingImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = newPendingImplementation; emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); return uint(Error.NO_ERROR); } /** * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation * @dev Admin function for new implementation to accept it's role as implementation * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptImplementation() public returns (uint) { // Check caller is pendingImplementation and pendingImplementation ≠ address(0) if (msg.sender != pendingComptrollerImplementation || pendingComptrollerImplementation == address(0)) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK); } // Save current values for inclusion in log address oldImplementation = comptrollerImplementation; address oldPendingImplementation = pendingComptrollerImplementation; comptrollerImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = address(0); emit NewImplementation(oldImplementation, comptrollerImplementation); emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); return uint(Error.NO_ERROR); } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address newPendingAdmin) public returns (uint) { // Check caller = admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return uint(Error.NO_ERROR); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() public returns (uint) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin) { return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return uint(Error.NO_ERROR); } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ function () payable external { // delegate all other functions to current implementation (bool success, ) = comptrollerImplementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize) switch success case 0 { revert(free_mem_ptr, returndatasize) } default { return(free_mem_ptr, returndatasize) } } } } // File: contracts/compound/Comptroller.sol pragma solidity ^0.5.16; /** * @title Compound's Comptroller Contract * @author Compound */ contract Comptroller is ComptrollerV3Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { /// @notice Emitted when an admin supports a market event MarketListed(CToken cToken); /// @notice Emitted when an account enters a market event MarketEntered(CToken cToken, address account); /// @notice Emitted when an account exits a market event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); /// @notice Emitted when a collateral factor is changed by admin event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); /// @notice Emitted when liquidation incentive is changed by admin event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); /// @notice Emitted when pause guardian is changed event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); /// @notice Emitted when an action is paused globally event ActionPaused(string action, bool pauseState); /// @notice Emitted when an action is paused on a market event ActionPaused(CToken cToken, string action, bool pauseState); /// @notice Emitted when market comped status is changed event MarketComped(CToken cToken, bool isComped); /// @notice Emitted when a new COMP speed is calculated for a market event CompSpeedUpdated(CToken indexed cToken, uint newSpeed); /// @notice Emitted when COMP is distributed to a supplier event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); /// @notice Emitted when COMP is distributed to a borrower event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); /// @notice Emitted when borrow factor for a cToken is changed event NewBorrowFactor(CToken indexed cToken, uint newBorrowFactor); /// @notice The threshold above which the flywheel transfers COMP, in wei uint public constant compClaimThreshold = 0.001e18; /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 // liquidationIncentiveMantissa must be no less than this value uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 // liquidationIncentiveMantissa must be no greater than this value uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 // borrowFactorMantissa must not exceed this value uint256 internal constant borrowFactorMaxMantissa = 1e18; // 1.0 constructor() public { admin = msg.sender; } /*** Assets You Are In ***/ /** * @notice Returns the assets an account has entered * @param account The address of the account to pull assets for * @return A dynamic list with the assets the account has entered */ function getAssetsIn(address account) external view returns (CToken[] memory) { CToken[] memory assetsIn = accountAssets[account]; return assetsIn; } /** * @notice Returns whether the given account is entered in the given asset * @param account The address of the account to check * @param cToken The cToken to check * @return True if the account is in the asset, otherwise false. */ function checkMembership(address account, CToken cToken) external view returns (bool) { return markets[address(cToken)].accountMembership[account]; } /** * @notice Add assets to be included in account liquidity calculation * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { uint len = cTokens.length; uint[] memory results = new uint[](len); for (uint i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); results[i] = uint(addToMarketInternal(cToken, msg.sender)); } return results; } /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param cToken The market to enter * @param borrower The address of the account to modify * @return Success indicator for whether the market was entered */ function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { Market storage marketToJoin = markets[address(cToken)]; if (!marketToJoin.isListed) { // market is not listed, cannot join return Error.MARKET_NOT_LISTED; } if (marketToJoin.accountMembership[borrower] == true) { // already joined return Error.NO_ERROR; } // survived the gauntlet, add to list // NOTE: we store these somewhat redundantly as a significant optimization // this avoids having to iterate through the list for the most common use cases // that is, only when we need to perform liquidity checks // and not whenever we want to check if an account is in a particular market marketToJoin.accountMembership[borrower] = true; accountAssets[borrower].push(cToken); emit MarketEntered(cToken, borrower); return Error.NO_ERROR; } /** * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not have an outstanding borrow balance in the asset, * or be providing necessary collateral for an outstanding borrow. * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ function exitMarket(address cTokenAddress) external returns (uint) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ if (amountOwed != 0) { return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); } /* Fail if the sender is not permitted to redeem all of their tokens */ uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); } Market storage marketToExit = markets[address(cToken)]; /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { return uint(Error.NO_ERROR); } /* Set cToken account membership to false */ delete marketToExit.accountMembership[msg.sender]; /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; uint len = userAssetList.length; uint assetIndex = len; for (uint i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; } } // We *must* have found the asset in the list or our redundant data structure is broken assert(assetIndex < len); // copy last item in list to location of item to be removed, reduce length by 1 CToken[] storage storedList = accountAssets[msg.sender]; storedList[assetIndex] = storedList[storedList.length - 1]; storedList.length--; emit MarketExited(cToken, msg.sender); return uint(Error.NO_ERROR); } /*** Policy Hooks ***/ /** * @notice Checks if the account should be allowed to redeem tokens in the given market * @param cToken The market to verify the redeem against * @param redeemer The account which would redeem the tokens * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); if (allowed != uint(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer, false); return uint(Error.NO_ERROR); } function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { return uint(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); if (err != Error.NO_ERROR) { return uint(err); } if (shortfall > 0) { return uint(Error.INSUFFICIENT_LIQUIDITY); } return uint(Error.NO_ERROR); } /** * @notice Validates redeem and reverts on rejection. May emit logs. * @param cToken Asset being redeemed * @param redeemer The address redeeming the tokens * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { // Shh - currently unused cToken; redeemer; // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { revert("redeemTokens zero"); } } /** * @notice Checks if the account should be allowed to repay a borrow in the given market * @param cToken The market to verify the repay against * @param payer The account which would repay the asset * @param borrower The account which would borrowed the asset * @param repayAmount The amount of the underlying asset the account would repay * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function repayBorrowAllowed( address cToken, address payer, address borrower, uint repayAmount) public returns (uint) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); return uint(Error.NO_ERROR); } /** * @notice Checks if the liquidation should be allowed to occur * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param repayAmount The amount of underlying being repaid */ function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount) public returns (uint) { // Shh - currently unused liquidator; if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { return uint(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { return uint(err); } if (shortfall == 0) { return uint(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (mathErr != MathError.NO_ERROR) { return uint(Error.MATH_ERROR); } if (repayAmount > maxClose) { return uint(Error.TOO_MUCH_REPAY); } return uint(Error.NO_ERROR); } /** * @notice Checks if the seizing of assets should be allowed to occur * @param cTokenCollateral Asset which was used as collateral and will be seized * @param cTokenBorrowed Asset which was borrowed by the borrower * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param seizeTokens The number of collateral tokens to seize */ function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) public returns (uint) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "paused"); // Shh - currently unused seizeTokens; if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { return uint(Error.MARKET_NOT_LISTED); } if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { return uint(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving updateCompSupplyIndex(cTokenCollateral); distributeSupplierComp(cTokenCollateral, borrower, false); distributeSupplierComp(cTokenCollateral, liquidator, false); return uint(Error.NO_ERROR); } /** * @notice Checks if the account should be allowed to transfer tokens in the given market * @param cToken The market to verify the transfer against * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens uint allowed = redeemAllowedInternal(cToken, src, transferTokens); if (allowed != uint(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, src, false); distributeSupplierComp(cToken, dst, false); return uint(Error.NO_ERROR); } /*** Liquidity/Liquidation Calculations ***/ /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `cTokenBalance` is the number of cTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint sumCollateral; uint sumBorrowPlusEffects; uint cTokenBalance; uint borrowBalance; uint exchangeRateMantissa; uint oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; Exp tokensToDenom; Exp borrowFactorMantissa; } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code (semi-opaque), account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidity(address account) public view returns (uint, uint, uint) { (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); return (uint(err), liquidity, shortfall); } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code, account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @return (possible error code (semi-opaque), hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidity( address account, address cTokenModify, uint redeemTokens, uint borrowAmount) public view returns (uint, uint, uint) { (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); return (uint(err), liquidity, shortfall); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, * without calculating accumulated interest. * @return (possible error code, hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, uint redeemTokens, uint borrowAmount) internal view returns (Error, uint, uint); /** * @notice Calculate number of tokens of collateral asset to seize given an underlying amount * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) * @param cTokenBorrowed The address of the borrowed cToken * @param cTokenCollateral The address of the collateral cToken * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { /* Read oracle prices for borrowed and collateral markets */ uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { return (uint(Error.PRICE_ERROR), 0); } /* * Get the exchange rate and calculate the number of collateral tokens to seize: * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error uint seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; MathError mathErr; uint liquidationIncentive = getLiquidationIncentive(cTokenCollateral); (mathErr, numerator) = mulExp(liquidationIncentive, priceBorrowedMantissa); if (mathErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0); } (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); if (mathErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0); } (mathErr, ratio) = divExp(numerator, denominator); if (mathErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0); } (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); if (mathErr != MathError.NO_ERROR) { return (uint(Error.MATH_ERROR), 0); } return (uint(Error.NO_ERROR), seizeTokens); } function getLiquidationIncentive(address cToken) public view returns (uint) { uint cTokenLiquidationIncentive = markets[cToken].liquidationIncentiveMantissa; if (cTokenLiquidationIncentive == 0) return liquidationIncentiveMantissa; return cTokenLiquidationIncentive; } /*** Admin Functions ***/ /** * @notice Sets a new price oracle for the comptroller * @dev Admin function to set a new price oracle * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) external returns (uint); /** * @notice Sets the closeFactor used when liquidating borrows * @dev Admin function to set closeFactor * @param newCloseFactorMantissa New close factor, scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); } Exp memory newCloseFactorExp = Exp({mantissa: newCloseFactorMantissa}); Exp memory lowLimit = Exp({mantissa: closeFactorMinMantissa}); if (lessThanOrEqualExp(newCloseFactorExp, lowLimit)) { return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } Exp memory highLimit = Exp({mantissa: closeFactorMaxMantissa}); if (lessThanExp(highLimit, newCloseFactorExp)) { return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); } uint oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); return uint(Error.NO_ERROR); } /** * @notice Sets the collateralFactor for a market * @dev Admin function to set per-market collateralFactor * @param cToken The market to set the factor on * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); } // Verify market is listed Market storage market = markets[address(cToken)]; if (!market.isListed) { return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); } Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); // Check collateral factor <= 0.9 Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); if (lessThanExp(highLimit, newCollateralFactorExp)) { return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); } // If collateral factor != 0, fail if price == 0 if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0) { return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); } // Set market's collateral factor to new collateral factor, remember old value uint oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); return uint(Error.NO_ERROR); } function _setLiquidationIncentive(CToken cToken, uint newLiquidationIncentiveMantissa) external returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); } // Verify market is listed Market storage market = markets[address(cToken)]; if (!market.isListed) { return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); } // Check de-scaled min <= newLiquidationIncentive <= max Exp memory newLiquidationIncentive = Exp({mantissa: newLiquidationIncentiveMantissa}); Exp memory minLiquidationIncentive = Exp({mantissa: liquidationIncentiveMinMantissa}); if (lessThanExp(newLiquidationIncentive, minLiquidationIncentive)) { return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); } Exp memory maxLiquidationIncentive = Exp({mantissa: liquidationIncentiveMaxMantissa}); if (lessThanExp(maxLiquidationIncentive, newLiquidationIncentive)) { return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); } // Save current value for use in log uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive market.liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; // Emit event with old incentive, new incentive emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); return uint(Error.NO_ERROR); } /** * @notice Sets liquidationIncentive * @dev Admin function to set liquidationIncentive * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { // Check caller is admin if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); } // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; return uint(Error.NO_ERROR); } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param cToken The address of the market (token) to list * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _supportMarket(CToken cToken) external returns (uint); function _addMarketInternal(address cToken) internal { for (uint i = 0; i < allMarkets.length; i ++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); } /** * @notice Set the given borrow factors for the given cToken markets. * @dev Admin function to set the borrow factors. * @param cToken The addresses of the markets (tokens) to change the borrow factors for * @param newBorrowFactor The new borrow factor values in underlying to be set. Must be between (0, 1] */ function _setBorrowFactor(CToken cToken, uint newBorrowFactor) external { require(msg.sender == admin, "!admin"); require(newBorrowFactor > 0 && newBorrowFactor <= borrowFactorMaxMantissa, "!borrowFactor"); markets[address(cToken)].borrowFactorMantissa = newBorrowFactor; emit NewBorrowFactor(cToken, newBorrowFactor); } /** * @notice Admin function to change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _setPauseGuardian(address newPauseGuardian) public returns (uint) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); } // Save current value for inclusion in log address oldPauseGuardian = pauseGuardian; // Store pauseGuardian with value newPauseGuardian pauseGuardian = newPauseGuardian; // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); return uint(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) external returns (bool) { require(markets[address(cToken)].isListed, "!listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "!admin"); mintGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Mint", state); return state; } function _setBorrowPaused(CToken cToken, bool state) external returns (bool) { require(markets[address(cToken)].isListed, "!listed"); require(msg.sender == pauseGuardian || msg.sender == admin, "!admin"); borrowGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Borrow", state); return state; } function _setTransferPaused(bool state) public returns (bool) { require(msg.sender == pauseGuardian || msg.sender == admin, "!admin"); transferGuardianPaused = state; emit ActionPaused("Transfer", state); return state; } function _setSeizePaused(bool state) external returns (bool) { require(msg.sender == pauseGuardian || msg.sender == admin, "!admin"); seizeGuardianPaused = state; emit ActionPaused("Seize", state); return state; } function _become(Unitroller unitroller) public { require(msg.sender == unitroller.admin(), "!admin"); require(unitroller._acceptImplementation() == 0, "!authorized"); } /** * @notice Checks caller is admin, or this contract is becoming the new implementation */ function adminOrInitializing() internal view returns (bool) { return msg.sender == admin || msg.sender == comptrollerImplementation; } /*** Comp Distribution ***/ /** * @notice Accrue COMP to the market by updating the supply index * @param cToken The market whose supply index to update */ function updateCompSupplyIndex(address cToken) internal; /** * @notice Accrue COMP to the market by updating the borrow index * @param cToken The market whose borrow index to update */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal; /** * @notice Calculate COMP accrued by a supplier and possibly transfer it to them * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ function distributeSupplierComp(address cToken, address supplier, bool distributeAll) internal { CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); compSupplierIndex[cToken][supplier] = supplyIndex.mantissa; if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { supplierIndex.mantissa = compInitialIndex; } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); uint supplierTokens = CToken(cToken).balanceOf(supplier); uint supplierDelta = mul_(supplierTokens, deltaIndex); uint supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = transferComp(supplier, supplierAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); } /** * @notice Calculate COMP accrued by a borrower and possibly transfer it to them * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); compBorrowerIndex[cToken][borrower] = borrowIndex.mantissa; if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); uint borrowerDelta = mul_(borrowerAmount, deltaIndex); uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = transferComp(borrower, borrowerAccrued, distributeAll ? 0 : compClaimThreshold); emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); } } function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint); /** * @notice Claim all the comp accrued by holder in all markets * @param holder The address to claim COMP for */ function claimComp(address holder) public { return claimComp(holder, allMarkets); } /** * @notice Claim all the comp accrued by holder in the specified markets * @param holder The address to claim COMP for * @param cTokens The list of markets to claim COMP in */ function claimComp(address holder, CToken[] memory cTokens) public { address[] memory holders = new address[](1); holders[0] = holder; claimComp(holders, cTokens, true, true); } /** * @notice Claim all comp accrued by the holders * @param holders The addresses to claim COMP for * @param cTokens The list of markets to claim COMP in * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { for (uint i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "!listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); for (uint j = 0; j < holders.length; j++) { distributeBorrowerComp(address(cToken), holders[j], borrowIndex, true); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); for (uint j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j], true); } } } } /*** Comp Distribution Admin ***/ function _addCompMarketInternal(address cToken) internal { Market storage market = markets[cToken]; require(market.isListed == true, "!listed"); require(market.isComped == false, "already added"); market.isComped = true; emit MarketComped(CToken(cToken), true); if (compSupplyState[cToken].index == 0 && compSupplyState[cToken].block == 0) { compSupplyState[cToken] = CompMarketState({ index: compInitialIndex, block: safe32(getBlockNumber(), "exceeds 32 bits") }); } if (compBorrowState[cToken].index == 0 && compBorrowState[cToken].block == 0) { compBorrowState[cToken] = CompMarketState({ index: compInitialIndex, block: safe32(getBlockNumber(), "exceeds 32 bits") }); } } /** * @notice Return all of the markets * @dev The automatic getter may be used to access an individual market. * @return The list of market addresses */ function getAllMarkets() public view returns (CToken[] memory) { return allMarkets; } function getBlockNumber() public view returns (uint) { return block.number; } /** * @notice Return the address of the COMP token * @return The address of COMP */ function getCompAddress() public view returns (address); } // File: contracts/ILiquidityGauge.sol pragma solidity ^0.5.16; interface ILiquidityGauge { function notifySavingsChange(address addr) external; } // File: contracts/Ownable.sol pragma solidity ^0.5.16; contract Ownable { address public _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } function owner() public view returns (address) { return _owner; } modifier onlyOwner() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/QsConfig.sol pragma solidity ^0.5.16; contract QsConfig is Ownable, Exponential { address public compToken; uint public safetyVaultRatio; address public safetyVault; address public safetyGuardian; address public pendingSafetyGuardian; struct MarketCap { /** * The borrow capacity of the asset, will be checked in borrowAllowed() * 0 means there is no limit on the capacity */ uint borrowCap; /** * The supply capacity of the asset, will be checked in mintAllowed() * 0 means there is no limit on the capacity */ uint supplyCap; /** * The flash loan capacity of the asset, will be checked in flashLoanAllowed() * 0 means there is no limit on the capacity */ uint flashLoanCap; } uint public compRatio = 0.5e18; mapping(address => bool) public whitelist; mapping(address => bool) public blacklist; mapping(address => MarketCap) marketsCap; // creditLimits allowed specific protocols to borrow and repay without collateral mapping(address => uint) public creditLimits; uint public flashLoanFeeRatio = 0.0001e18; ILiquidityGauge public liquidityGauge; event NewCompToken(address oldCompToken, address newCompToken); event NewSafetyVault(address oldSafetyVault, address newSafetyVault); event NewSafetyVaultRatio(uint oldSafetyVaultRatio, uint newSafetyVault); event NewCompRatio(uint oldCompRatio, uint newCompRatio); event WhitelistChange(address user, bool enabled); event BlacklistChange(address user, bool enabled); /// @notice Emitted when protocol's credit limit has changed event CreditLimitChanged(address protocol, uint creditLimit); event FlashLoanFeeRatioChanged(uint oldFeeRatio, uint newFeeRatio); /// @notice Emitted when borrow cap for a cToken is changed event NewBorrowCap(address indexed cToken, uint newBorrowCap); /// @notice Emitted when supply cap for a cToken is changed event NewSupplyCap(address indexed cToken, uint newSupplyCap); /// @notice Emitted when flash loan for a cToken is changed event NewFlashLoanCap(address indexed cToken, uint newFlashLoanCap); event NewPendingSafetyGuardian(address oldPendingSafetyGuardian, address newPendingSafetyGuardian); event NewSafetyGuardian(address oldSafetyGuardian, address newSafetyGuardian); event NewLiquidityGauge(address oldLiquidityGauge, address newLiquidityGauage); modifier onlySafetyGuardian { require(msg.sender == safetyGuardian, "Safety guardian required."); _; } constructor(QsConfig previousQsConfig) public { safetyGuardian = msg.sender; if (address(previousQsConfig) == address(0x0)) return; safetyGuardian = previousQsConfig.safetyGuardian(); compToken = previousQsConfig.compToken(); safetyVaultRatio = previousQsConfig.safetyVaultRatio(); safetyVault = previousQsConfig.safetyVault(); } /** * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. * @dev Admin function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. * @param cTokens The addresses of the markets (tokens) to change the borrow caps for * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing. */ function _setMarketBorrowCaps(address[] calldata cTokens, uint[] calldata newBorrowCaps) external onlySafetyGuardian { uint numMarkets = cTokens.length; uint numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); for(uint i = 0; i < numMarkets; i++) { marketsCap[cTokens[i]].borrowCap = newBorrowCaps[i]; emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); } } /** * @notice Set the given flash loan caps for the given cToken markets. Borrowing that brings total flash cap to or above flash loan cap will revert. * @dev Admin function to set the flash loan caps. A flash loan cap of 0 corresponds to unlimited flash loan. * @param cTokens The addresses of the markets (tokens) to change the flash loan caps for * @param newFlashLoanCaps The new flash loan cap values in underlying to be set. A value of 0 corresponds to unlimited flash loan. */ function _setMarketFlashLoanCaps(address[] calldata cTokens, uint[] calldata newFlashLoanCaps) external onlySafetyGuardian { uint numMarkets = cTokens.length; uint numFlashLoanCaps = newFlashLoanCaps.length; require(numMarkets != 0 && numMarkets == numFlashLoanCaps, "invalid input"); for(uint i = 0; i < numMarkets; i++) { marketsCap[cTokens[i]].flashLoanCap = newFlashLoanCaps[i]; emit NewFlashLoanCap(cTokens[i], newFlashLoanCaps[i]); } } /** * @notice Set the given supply caps for the given cToken markets. Supplying that brings total supply to or above supply cap will revert. * @dev Admin function to set the supply caps. A supply cap of 0 corresponds to unlimited supplying. * @param cTokens The addresses of the markets (tokens) to change the supply caps for * @param newSupplyCaps The new supply cap values in underlying to be set. A value of 0 corresponds to unlimited supplying. */ function _setMarketSupplyCaps(address[] calldata cTokens, uint[] calldata newSupplyCaps) external onlySafetyGuardian { uint numMarkets = cTokens.length; uint numSupplyCaps = newSupplyCaps.length; require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); for(uint i = 0; i < numMarkets; i++) { marketsCap[cTokens[i]].supplyCap = newSupplyCaps[i]; emit NewSupplyCap(cTokens[i], newSupplyCaps[i]); } } /** * @notice Sets whitelisted protocol's credit limit * @param protocol The address of the protocol * @param creditLimit The credit limit */ function _setCreditLimit(address protocol, uint creditLimit) public onlyOwner { require(isContract(protocol), "contract required"); require(creditLimits[protocol] != creditLimit, "no change"); creditLimits[protocol] = creditLimit; emit CreditLimitChanged(protocol, creditLimit); } function _setCompToken(address _compToken) public onlyOwner { address oldCompToken = compToken; compToken = _compToken; emit NewCompToken(oldCompToken, compToken); } function _setSafetyVault(address _safetyVault) public onlyOwner { address oldSafetyVault = safetyVault; safetyVault = _safetyVault; emit NewSafetyVault(oldSafetyVault, safetyVault); } function _setSafetyVaultRatio(uint _safetyVaultRatio) public onlySafetyGuardian { require(_safetyVaultRatio < 1e18, "!safetyVaultRatio"); uint oldSafetyVaultRatio = safetyVaultRatio; safetyVaultRatio = _safetyVaultRatio; emit NewSafetyVaultRatio(oldSafetyVaultRatio, safetyVaultRatio); } function _setPendingSafetyGuardian(address newPendingSafetyGuardian) external onlyOwner { address oldPendingSafetyGuardian = pendingSafetyGuardian; pendingSafetyGuardian = newPendingSafetyGuardian; emit NewPendingSafetyGuardian(oldPendingSafetyGuardian, newPendingSafetyGuardian); } function _acceptSafetyGuardian() external { require(msg.sender == pendingSafetyGuardian, "!pendingSafetyGuardian"); address oldPendingSafetyGuardian = pendingSafetyGuardian; address oldSafetyGuardian = safetyGuardian; safetyGuardian = pendingSafetyGuardian; pendingSafetyGuardian = address(0x0); emit NewSafetyGuardian(oldSafetyGuardian, safetyGuardian); emit NewPendingSafetyGuardian(oldPendingSafetyGuardian, pendingSafetyGuardian); } function getCreditLimit(address protocol) external view returns (uint) { return creditLimits[protocol]; } function getBorrowCap(address cToken) external view returns (uint) { return marketsCap[cToken].borrowCap; } function getSupplyCap(address cToken) external view returns (uint) { return marketsCap[cToken].supplyCap; } function getFlashLoanCap(address cToken) external view returns (uint) { return marketsCap[cToken].flashLoanCap; } function calculateSeizeTokenAllocation(uint _seizeTokenAmount, uint liquidationIncentiveMantissa) public view returns(uint liquidatorAmount, uint safetyVaultAmount) { Exp memory vaultRatio = Exp({mantissa:safetyVaultRatio}); (,Exp memory tmp) = mulScalar(vaultRatio, _seizeTokenAmount); safetyVaultAmount = div_(tmp, liquidationIncentiveMantissa).mantissa; liquidatorAmount = sub_(_seizeTokenAmount, safetyVaultAmount); } function getCompAllocation(address user, uint userAccrued) public view returns(uint userAmount, uint governanceAmount) { if (!isContract(user) || whitelist[user]) { return (userAccrued, 0); } Exp memory compRatioExp = Exp({mantissa:compRatio}); (, userAmount) = mulScalarTruncate(compRatioExp, userAccrued); governanceAmount = sub_(userAccrued, userAmount); } function getFlashFee(address borrower, address cToken, uint256 amount) external view returns (uint flashFee) { if (whitelist[borrower]) { return 0; } Exp memory flashLoanFeeRatioExp = Exp({mantissa:flashLoanFeeRatio}); (, flashFee) = mulScalarTruncate(flashLoanFeeRatioExp, amount); cToken; } function _setCompRatio(uint _compRatio) public onlySafetyGuardian { require(_compRatio < 1e18, "compRatio should be less then 100%"); uint oldCompRatio = compRatio; compRatio = _compRatio; emit NewCompRatio(oldCompRatio, compRatio); } function isBlocked(address user) public view returns (bool) { return blacklist[user]; } function _addToWhitelist(address _member) public onlySafetyGuardian { require(_member != address(0x0), "Zero address is not allowed"); whitelist[_member] = true; emit WhitelistChange(_member, true); } function _removeFromWhitelist(address _member) public onlySafetyGuardian { require(_member != address(0x0), "Zero address is not allowed"); whitelist[_member] = false; emit WhitelistChange(_member, false); } function _addToBlacklist(address _member) public onlySafetyGuardian { require(_member != address(0x0), "Zero address is not allowed"); blacklist[_member] = true; emit BlacklistChange(_member, true); } function _removeFromBlacklist(address _member) public onlySafetyGuardian { require(_member != address(0x0), "Zero address is not allowed"); blacklist[_member] = false; emit BlacklistChange(_member, false); } function _setFlashLoanFeeRatio(uint _feeRatio) public onlySafetyGuardian { require(_feeRatio != flashLoanFeeRatio, "Same fee ratio already set"); require(_feeRatio < 1e18, "Invalid fee ratio"); uint oldFeeRatio = flashLoanFeeRatio; flashLoanFeeRatio = _feeRatio; emit FlashLoanFeeRatioChanged(oldFeeRatio, flashLoanFeeRatio); } function _setliquidityGauge(ILiquidityGauge _liquidityGauge) external onlySafetyGuardian { emit NewLiquidityGauge(address(liquidityGauge), address(_liquidityGauge)); liquidityGauge = _liquidityGauge; } function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: contracts/Qstroller.sol pragma solidity ^0.5.16; contract Qstroller is Comptroller { /// @notice Emitted when an admin delists a market event MarketDelisted(CToken cToken); QsConfig public qsConfig; // /** // * @notice Remove the market from the markets mapping // * @param cToken The address of the market (token) to delist // */ // function _delistMarket(CToken cToken) external { // require(msg.sender == admin, "only admin may delist market"); // // require(markets[address(cToken)].isListed, "market not listed"); // require(cToken.totalSupply() == 0, "market not empty"); // // cToken.isCToken(); // Sanity check to make sure its really a CToken // // delete markets[address(cToken)]; // // for (uint i = 0; i < allMarkets.length; i++) { // if (allMarkets[i] == cToken) { // allMarkets[i] = allMarkets[allMarkets.length - 1]; // delete allMarkets[allMarkets.length - 1]; // allMarkets.length--; // break; // } // } // // emit MarketDelisted(cToken); // } function _setQsConfig(QsConfig _qsConfig) public { require(msg.sender == admin); qsConfig = _qsConfig; } /** * @notice Sets new governance token distribution speed * @dev Admin function to set new token distribution speed */ function _setCompSpeeds(address[] memory _allMarkets, uint[] memory _compSpeeds) public { // Check caller is admin require(msg.sender == admin); require(_allMarkets.length == _compSpeeds.length); for (uint i = 0; i < _allMarkets.length; i++) { _setCompSpeedInternal(_allMarkets[i], _compSpeeds[i]); } } function _setCompSpeedInternal(address _cToken, uint _compSpeed) internal { Market storage market = markets[_cToken]; if (market.isComped == false) { _addCompMarketInternal(_cToken); } uint currentCompSpeed = compSpeeds[_cToken]; uint currentSupplySpeed = currentCompSpeed >> 128; uint currentBorrowSpeed = uint128(currentCompSpeed); uint newSupplySpeed = _compSpeed >> 128; uint newBorrowSpeed = uint128(_compSpeed); if (currentSupplySpeed != newSupplySpeed) { updateCompSupplyIndex(_cToken); } if (currentBorrowSpeed != newBorrowSpeed) { Exp memory borrowIndex = Exp({mantissa: CToken(_cToken).borrowIndex()}); updateCompBorrowIndex(_cToken, borrowIndex); } compSpeeds[_cToken] = _compSpeed; } function getCompAddress() public view returns (address) { return qsConfig.compToken(); } function calculateSeizeTokenAllocation(uint _seizeTokenAmount) public view returns(uint liquidatorAmount, uint safetyVaultAmount) { return qsConfig.calculateSeizeTokenAllocation(_seizeTokenAmount, liquidationIncentiveMantissa); } function transferComp(address user, uint userAccrued, uint threshold) internal returns (uint) { address compAddress = getCompAddress(); if (userAccrued >= threshold && userAccrued > 0 && compAddress != address(0x0)) { EIP20Interface comp = EIP20Interface(compAddress); uint compRemaining = comp.balanceOf(address(this)); if (userAccrued <= compRemaining) { (uint userAmount, uint governanceAmount) = qsConfig.getCompAllocation(user, userAccrued); if (userAmount > 0) comp.transfer(user, userAmount); if (governanceAmount > 0) comp.transfer(qsConfig.safetyVault(), governanceAmount); return 0; } } return userAccrued; } /** * @notice Checks if the account should be allowed to borrow the underlying asset of the given market * @param cToken The market to verify the borrow against * @param borrower The account which would borrow the asset * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "p"); if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { // only cTokens may call borrowAllowed if borrower not in market require(msg.sender == cToken, "!c"); // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { return uint(err); } // it should be impossible to break the important invariant assert(markets[cToken].accountMembership[borrower]); } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { return uint(Error.PRICE_ERROR); } uint borrowCap = qsConfig.getBorrowCap(cToken); // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { uint totalBorrows = CToken(cToken).totalBorrows(); (MathError mathErr, uint nextTotalBorrows) = addUInt(totalBorrows, borrowAmount); require(mathErr == MathError.NO_ERROR, "o"); require(nextTotalBorrows < borrowCap, "c"); } (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); if (err != Error.NO_ERROR) { return uint(err); } if (shortfall > 0) { return uint(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex, false); return uint(Error.NO_ERROR); } function flashLoanAllowed(address cToken, address to, uint256 flashLoanAmount) view public returns (uint) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "p"); require(qsConfig.whitelist(to), "!w"); if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } uint flashLoanCap = qsConfig.getFlashLoanCap(cToken); require(flashLoanAmount <= flashLoanCap, "c"); to; return uint(Error.NO_ERROR); } function getFlashLoanCap(address cToken) view external returns (uint) { return qsConfig.getFlashLoanCap(cToken); } /** * @notice Checks if the account should be allowed to mint tokens in the given market * @param cToken The market to verify the mint against * @param minter The account which would get the minted tokens * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { require(!qsConfig.isBlocked(minter)); // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken]); // Shh - currently unused minter; mintAmount; if (!markets[cToken].isListed) { return uint(Error.MARKET_NOT_LISTED); } uint supplyCap = qsConfig.getSupplyCap(cToken); // Supply cap of 0 corresponds to unlimited borrowing if (supplyCap != 0) { Exp memory exchangeRate = Exp({mantissa: CTokenInterface(cToken).exchangeRateStored()}); (MathError mErr, uint totalSupplyUnderlying) = mulScalarTruncate(exchangeRate, EIP20Interface(cToken).totalSupply()); require(mErr == MathError.NO_ERROR); (MathError mathErr, uint nextTotalSupplyUnderlying) = addUInt(totalSupplyUnderlying, mintAmount); require(mathErr == MathError.NO_ERROR); require(nextTotalSupplyUnderlying <= supplyCap, ">cap"); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter, false); return uint(Error.NO_ERROR); } /** * @notice Accrue COMP to the market by updating the supply index * @param cToken The market whose supply index to update */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; uint compSpeed = compSpeeds[cToken]; // use first 128 bit as supplySpeed uint supplySpeed = compSpeed >> 128; uint blockNumber = getBlockNumber(); uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { uint supplyTokens = CToken(cToken).totalSupply(); uint compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); compSupplyState[cToken] = CompMarketState({ index: safe224(index.mantissa, ">224bits"), block: safe32(blockNumber, ">32bits") }); } else if (deltaBlocks > 0 && supplyState.index > 0) { supplyState.block = safe32(blockNumber, ">32bits"); } } /** * @notice Accrue COMP to the market by updating the borrow index * @param cToken The market whose borrow index to update */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; // use last 128 bit as borrowSpeed uint borrowSpeed = uint128(compSpeeds[cToken]); uint blockNumber = getBlockNumber(); uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); uint compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); compBorrowState[cToken] = CompMarketState({ index: safe224(index.mantissa, ">224bits"), block: safe32(blockNumber, ">32bits") }); } else if (deltaBlocks > 0 && borrowState.index > 0) { borrowState.block = safe32(blockNumber, ">32bits"); } } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, * without calculating accumulated interest. * @return (possible error code, hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, uint redeemTokens, uint borrowAmount) internal view returns (Error, uint, uint) { // If credit limit is set to MAX, no need to check account liquidity. if (qsConfig.getCreditLimit(account) == uint(-1)) { return (Error.NO_ERROR, uint(-1), 0); } AccountLiquidityLocalVars memory vars; // Holds all our calculation results uint oErr; MathError mErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; for (uint i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); vars.borrowFactorMantissa = Exp({mantissa: markets[address(asset)].borrowFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); // Get the normalized price of the asset vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); if (vars.oraclePriceMantissa == 0) { return (Error.PRICE_ERROR, 0, 0); } vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); // Pre-compute a conversion factor from tokens -> ether (normalized price value) (mErr, vars.tokensToDenom) = mulExp3(vars.collateralFactor, vars.exchangeRate, vars.oraclePrice); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // sumCollateral += tokensToDenom * cTokenBalance (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrowValue = borrowBalance / borrowFactor uint borrowValue = div_(vars.borrowBalance, vars.borrowFactorMantissa); // sumBorrowPlusEffects += oraclePrice * borrowValue (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowValue, vars.sumBorrowPlusEffects); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // Calculate effects of interacting with cTokenModify if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } // borrow effect // borrowValue = borrowAmount / borrowFactor borrowValue = div_(borrowAmount, vars.borrowFactorMantissa); // sumBorrowPlusEffects += oraclePrice * borrowValue (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowValue, vars.sumBorrowPlusEffects); if (mErr != MathError.NO_ERROR) { return (Error.MATH_ERROR, 0, 0); } } } // If credit limit is set, no need to consider collateral. if (qsConfig.getCreditLimit(account) > 0) { vars.sumCollateral = qsConfig.getCreditLimit(account); } // These are safe, as the underflow condition is checked first if (vars.sumCollateral > vars.sumBorrowPlusEffects) { return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0); } else { return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral); } } /** * @notice Checks if the liquidation should be allowed to occur * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param repayAmount The amount of underlying being repaid */ function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount) public returns (uint) { require(qsConfig.getCreditLimit(borrower) == 0 , "c"); return super.liquidateBorrowAllowed(cTokenBorrowed, cTokenCollateral, liquidator, borrower, repayAmount); } function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) public returns (uint) { require(qsConfig.getCreditLimit(borrower) == 0 , "c"); return super.seizeAllowed(cTokenCollateral, cTokenBorrowed, liquidator, borrower, seizeTokens); } /** * @notice Checks if the account should be allowed to repay a borrow in the given market * @param cToken The market to verify the repay against * @param payer The account which would repay the asset * @param borrower The account which would borrowed the asset * @param repayAmount The amount of the underlying asset the account would repay * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function repayBorrowAllowed( address cToken, address payer, address borrower, uint repayAmount) public returns (uint) { require(qsConfig.getCreditLimit(borrower) == 0 || payer == borrower); return super.repayBorrowAllowed(cToken, payer, borrower, repayAmount); } function _supportMarket(CToken cToken) external returns (uint) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } if (markets[address(cToken)].isListed) { return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); } cToken.isCToken(); // Sanity check to make sure its really a CToken markets[address(cToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0, borrowFactorMantissa: 1e18, liquidationIncentiveMantissa: 0}); _addMarketInternal(address(cToken)); emit MarketListed(cToken); return uint(Error.NO_ERROR); } function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { require(!qsConfig.isBlocked(redeemer), "b"); } /** * @notice Sets a new price oracle for the comptroller * @dev Admin function to set a new price oracle * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) external returns (uint) { // Check caller is admin require(msg.sender == admin); // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oracle, newOracle); // Set comptroller's oracle to newOracle oracle = newOracle; return uint(Error.NO_ERROR); } function safetyGuardian() external view returns (address) { return qsConfig.safetyGuardian(); } } // File: contracts/IERC3156FlashBorrower.sol pragma solidity ^0.5.16; interface IERC3156FlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } // File: contracts/SToken.sol pragma solidity ^0.5.16; contract SToken is CToken { struct LiquidationLocalVars { uint borrowerTokensNew; uint liquidatorTokensNew; uint safetyVaultTokensNew; uint safetyVaultTokens; uint liquidatorSeizeTokens; } function mintFresh(address minter, uint mintAmount) internal returns (uint, uint) { (uint mintError, uint actualMintAmount) = super.mintFresh(minter, mintAmount); if (mintError == uint(Error.NO_ERROR)) { notifySavingsChange(minter); } return (mintError, actualMintAmount); } function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) { uint redeemError = super.redeemFresh(redeemer, redeemTokensIn, redeemAmountIn); if (redeemError == uint(Error.NO_ERROR)) { notifySavingsChange(redeemer); } return redeemError; } function notifySavingsChange(address addr) internal { QsConfig qsConfig = Qstroller(address(comptroller)).qsConfig(); ILiquidityGauge liquidityGauge = qsConfig.liquidityGauge(); if (address(liquidityGauge) != address(0x0)) { liquidityGauge.notifySavingsChange(addr); } } function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { uint errorCode = super.transferTokens(spender, src, dst, tokens); if (errorCode == uint(Error.NO_ERROR)) { notifySavingsChange(src); notifySavingsChange(dst); } return errorCode; } function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint) { /* Fail if seize not allowed */ uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); if (allowed != 0) { return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER); } LiquidationLocalVars memory vars; MathError mathErr; QsConfig qsConfig = Qstroller(address(comptroller)).qsConfig(); uint liquidationIncentive = comptroller.getLiquidationIncentive(seizerToken); (vars.liquidatorSeizeTokens, vars.safetyVaultTokens) = qsConfig.calculateSeizeTokenAllocation(seizeTokens, liquidationIncentive); address safetyVault = qsConfig.safetyVault(); /* * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: * borrowerTokensNew = accountTokens[borrower] - seizeTokens * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens */ (mathErr, vars.borrowerTokensNew) = subUInt(accountTokens[borrower], seizeTokens); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, uint(mathErr)); } (mathErr, vars.liquidatorTokensNew) = addUInt(accountTokens[liquidator], vars.liquidatorSeizeTokens); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, uint(mathErr)); } (mathErr, vars.safetyVaultTokensNew) = addUInt(accountTokens[safetyVault], vars.safetyVaultTokens); if (mathErr != MathError.NO_ERROR) { return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, uint(mathErr)); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accountTokens[borrower] = vars.borrowerTokensNew; accountTokens[liquidator] = vars.liquidatorTokensNew; accountTokens[safetyVault] = vars.safetyVaultTokensNew; /* Emit a Transfer event */ emit Transfer(borrower, liquidator, vars.liquidatorSeizeTokens); emit Transfer(borrower, safetyVault, vars.safetyVaultTokens); return uint(Error.NO_ERROR); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) { // // Check caller is admin if (msg.sender != comptroller.safetyGuardian()) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK); } // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK); } uint oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); return uint(Error.NO_ERROR); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReservesFresh(uint reduceAmount) internal returns (uint) { // totalReserves - reduceAmount uint totalReservesNew; address payable safetyGuardian = address(uint160(comptroller.safetyGuardian())); // Check caller is admin if (msg.sender != safetyGuardian) { return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = totalReserves - reduceAmount; // We checked reduceAmount <= totalReserves above, so this should never revert. require(totalReservesNew <= totalReserves, "reduce reserves unexpected underflow"); // Store reserves[n+1] = reserves[n] - reduceAmount totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. doTransferOut(safetyGuardian, reduceAmount); emit ReservesReduced(safetyGuardian, reduceAmount, totalReservesNew); return uint(Error.NO_ERROR); } /** * @notice updates the interest rate model (*requires fresh interest accrual) * @dev Admin function to update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != comptroller.safetyGuardian() && msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK); } // Track the market's current interest rate model oldInterestRateModel = interestRateModel; // Ensure invoke newInterestRateModel.isInterestRateModel() returns true require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); // Set the interest rate model to newInterestRateModel interestRateModel = newInterestRateModel; // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); return uint(Error.NO_ERROR); } function isNativeToken() public pure returns (bool) { return false; } /** * @dev The amount of currency available to be lent. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */ function maxFlashLoan(address token) external view returns (uint256) { validateFlashloanToken(token); return Qstroller(address(comptroller)).getFlashLoanCap(address(this)); } /** * @dev The fee to be charged for a given loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */ function flashFee(address token, uint256 amount) external view returns (uint256) { validateFlashloanToken(token); return getFlashFeeInternal(token, amount); } function getFlashFeeInternal(address token, uint256 amount) internal view returns (uint256) { token; return Qstroller(address(comptroller)).qsConfig().getFlashFee(msg.sender, address(this), amount); } /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param token The loan currency. * @param amount The amount of tokens lent. * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data) external returns (bool) { require(accrueInterest() == uint(Error.NO_ERROR), "Accrue interest failed"); validateFlashloanToken(token); Qstroller(address(comptroller)).flashLoanAllowed(address(this), address(receiver), amount); uint cashBefore = getCashPrior(); require(cashBefore >= amount, "Insufficient liquidity"); // 1. calculate fee uint fee = getFlashFeeInternal(token, amount); // 2. update totalBorrows totalBorrows = add_(totalBorrows, amount); // 3. transfer fund to receiver doFlashLoanTransferOut(address(uint160(address(receiver))), token, amount); // 4. execute receiver's callback function require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"), "IERC3156: Callback failed" ); // 5. take amount + fee from receiver uint256 repaymentAmount = add_(amount, fee); doFlashLoanTransferIn(address(receiver), token, repaymentAmount); // 6. update reserves totalReserves = add_(totalReserves, fee); totalBorrows = sub_(totalBorrows, amount); return true; } function doFlashLoanTransferOut(address payable receiver, address token, uint amount) internal { token; doTransferOut(receiver, amount); } function doFlashLoanTransferIn(address receiver, address token, uint amount) internal { token; uint actualAmount = doTransferIn(receiver, amount); require(actualAmount == amount, "!amount"); } function validateFlashloanToken(address token) view internal; } // File: contracts/compound/CErc20.sol pragma solidity ^0.5.16; /** * @title Compound's CErc20 Contract * @notice CTokens which wrap an EIP-20 underlying * @author Compound */ contract CErc20 is SToken, CErc20Interface { /** * @notice Initialize the new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token */ function initialize(address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_) public { // CToken initialize does the bulk of the work super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); // Set underlying and sanity check it underlying = underlying_; EIP20Interface(underlying).totalSupply(); } /*** User Interface ***/ /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function mint(uint mintAmount) external returns (uint) { (uint err,) = mintInternal(mintAmount); return err; } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint redeemTokens) external returns (uint) { return redeemInternal(redeemTokens); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint redeemAmount) external returns (uint) { return redeemUnderlyingInternal(redeemAmount); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint borrowAmount) external returns (uint) { return borrowInternal(borrowAmount); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrow(uint repayAmount) external returns (uint) { (uint err,) = repayBorrowInternal(repayAmount); return err; } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { (uint err,) = repayBorrowBehalfInternal(borrower, repayAmount); return err; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param repayAmount The amount of the underlying borrowed asset to repay * @param cTokenCollateral The market in which to seize collateral from the borrower * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { (uint err,) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); return err; } /** * @notice The sender adds to reserves. * @param addAmount The amount fo underlying token to add as reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves(uint addAmount) external returns (uint) { return _addReservesInternal(addAmount); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying tokens owned by this contract */ function getCashPrior() internal view returns (uint) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(this)); } /** * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. * This will revert due to insufficient balance or insufficient allowance. * This function returns the actual amount received, * which may be less than `amount` if there is a fee attached to the transfer. * * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ function doTransferIn(address from, uint amount) internal returns (uint) { EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); token.transferFrom(from, address(this), amount); bool success; assembly { switch returndatasize() case 0 { // This is a non-standard ERC-20 success := not(0) // set success to true } case 32 { // This is a compliant ERC-20 returndatacopy(0, 0, 32) success := mload(0) // Set `success = returndata` of external call } default { // This is an excessively non-compliant ERC-20, revert. revert(0, 0) } } require(success, "TOKEN_TRANSFER_IN_FAILED"); // Calculate the amount that was *actually* transferred uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW"); return balanceAfter - balanceBefore; // underflow already checked above, just subtract } /** * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified * it is >= amount, this should not revert in normal conditions. * * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ function doTransferOut(address payable to, uint amount) internal { EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); token.transfer(to, amount); bool success; assembly { switch returndatasize() case 0 { // This is a non-standard ERC-20 success := not(0) // set success to true } case 32 { // This is a complaint ERC-20 returndatacopy(0, 0, 32) success := mload(0) // Set `success = returndata` of external call } default { // This is an excessively non-compliant ERC-20, revert. revert(0, 0) } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); } function validateFlashloanToken(address token) view internal { require(underlying == token, "!flashloan token"); } } // File: contracts/compound/CErc20Delegate.sol pragma solidity ^0.5.16; /** * @title Compound's CErc20Delegate Contract * @notice CTokens which wrap an EIP-20 underlying and are delegated to * @author Compound */ contract CErc20Delegate is CErc20, CDelegateInterface { /** * @notice Construct an empty delegate */ constructor() public {} /** * @notice Called by the delegator on a delegate to initialize it for duty * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) public { // Shh -- currently unused data; // Shh -- we don't ever want this hook to be marked pure if (false) { implementation = address(0); } require(msg.sender == admin, "only the admin may call _becomeImplementation"); } /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() public { // Shh -- we don't ever want this hook to be marked pure if (false) { implementation = address(0); } require(msg.sender == admin, "only the admin may call _resignImplementation"); } }
We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the
POA solidity flattener or the
truffle flattener
.
Try to fetch constructor arguments automatically
No
Yes
ABI-encoded Constructor Arguments (if required by the contract)
Add arguments in
ABI hex encoded form
. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be
parsed here.
Add Contract Libraries
Contract Libraries
Library 1 Name
A library name called in the .sol file. Multiple libraries (up to 10) may be added for each contract. Click the Add Library button to add an additional one.
Library 1 Address
The 0x library address. This can be found in the generated json file or Truffle output (if using truffle).
Library 2 Name
Library 2 Address
Library 3 Name
Library 3 Address
Library 4 Name
Library 4 Address
Library 5 Name
Library 5 Address
Library 6 Name
Library 6 Address
Library 7 Name
Library 7 Address
Library 8 Name
Library 8 Address
Library 9 Name
Library 9 Address
Library 10 Name
Library 10 Address
Add Library
Loading...
Verify & publish
Cancel
Ok
Ok
Ok
No
Yes