all files / contracts/test/ InvariantChecker.sol

0% Statements 0/43
0% Branches 0/10
0% Functions 0/1
0% Lines 0/46
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                                                                                                                                                                                                       
pragma solidity ^0.8.0;
// SPDX-License-Identifier: GPL-2.0-or-later
 
import "hardhat/console.sol";
 
import "../Constants.sol";
import "../Euler.sol";
import "../modules/Markets.sol";
import "../modules/EToken.sol";
import "../modules/DToken.sol";
 
// This is a testing-only contract that verifies invariants of the Euler system.
 
struct LocalVars {
    uint eTokenBalances;
    uint dTokenBalances;
    uint dTokenBalancesExact;
 
    uint numUsersWithEtokens;
    uint numUsersWithDtokens;
 
    uint eTokenTotalSupply;
    uint reserveBalance;
    uint dTokenTotalSupply;
    uint dTokenTotalSupplyExact;
 
    uint poolSize;
}
 
contract InvariantChecker is Constants {
    function check(address eulerContract, address[] calldata markets, address[] calldata accounts, bool verbose) external view {
        Euler eulerProxy = Euler(eulerContract);
        Markets marketsProxy = Markets(eulerProxy.moduleIdToProxy(MODULEID__MARKETS));
 
        LocalVars memory v;
 
        for (uint i = 0; i < markets.length; ++i) {
            IERC20 eToken = IERC20(marketsProxy.underlyingToEToken(markets[i]));
            IERC20 dToken = IERC20(marketsProxy.eTokenToDToken(address(eToken)));
 
            v.eTokenBalances = 0;
            v.dTokenBalances = 0;
            v.dTokenBalancesExact = 0;
 
            v.numUsersWithEtokens = 0;
            v.numUsersWithDtokens = 0;
 
            for (uint j = 0; j < accounts.length; ++j) {
                address account = accounts[j];
 
                {
                    uint bal = eToken.balanceOf(account);
                    v.eTokenBalances += bal;
                    if (bal != 0) v.numUsersWithEtokens++;
                }
 
                {
                    uint bal = dToken.balanceOf(account);
                    v.dTokenBalances += bal;
                    if (bal != 0) v.numUsersWithDtokens++;
                }
 
                {
                    uint bal = DToken(address(dToken)).balanceOfExact(account);
                    v.dTokenBalancesExact += bal;
                }
            }
 
            v.eTokenTotalSupply = eToken.totalSupply();
            v.reserveBalance = EToken(address(eToken)).reserveBalance();
            v.dTokenTotalSupply = dToken.totalSupply();
            v.dTokenTotalSupplyExact = DToken(address(dToken)).totalSupplyExact();
 
            v.poolSize = IERC20(markets[i]).balanceOf(eulerContract);
 
            if (verbose) {
                console.log("--------------------------------------------------------------");
                console.log("MARKET = ", markets[i]);
                console.log("POOL SIZE           = ", v.poolSize);
                console.log("");
                console.log("USERS WITH ETOKENS  = ", v.numUsersWithEtokens);
                console.log("ETOKEN BALANCE SUM  = ", v.eTokenBalances);
                console.log("RESERVE BALANCE     = ", v.reserveBalance);
                console.log("ETOKEN TOTAL SUPPLY = ", v.eTokenTotalSupply);
                console.log("");
                console.log("USERS WITH DTOKENS  = ", v.numUsersWithDtokens);
                console.log("DTOKEN BALANCE SUM  = ", v.dTokenBalances);
                console.log("DTOKEN TOTAL SUPPLY = ", v.dTokenTotalSupply);
                console.log("DTOKEN BALEXACT SUM = ", v.dTokenBalancesExact);
                console.log("DTOKEN EXACT SUPPLY = ", v.dTokenTotalSupplyExact);
            }
 
            require(v.eTokenBalances + v.reserveBalance == v.eTokenTotalSupply, "invariant checker: eToken balance mismatch");
 
            // Due to rounding, user debt balances can grow slightly faster than the total debt supply
            require(v.dTokenBalancesExact >= v.dTokenTotalSupplyExact, "invariant checker: dToken exact balance mismatch");
        }
    }
}