Address Contract Verified
Address
0xf5e4fFeB7d2183B61753AA4074d72E51873C1D0a
Balance
0 ETH
Nonce
1
Code Size
16636 bytes
Creator
0x49E3C589...4d2B at tx 0x1b493160...403155
Indexed Transactions
0
Contract Bytecode
16636 bytes
0x6080604052600436106101ea5760003560e01c80637985c5e411610113578063a3f4df7e116100ab578063bd59121e1161006f578063bd59121e146105b9578063d56d6bbe146105d9578063f2fde38b146105ec578063f92d34331461060c578063fe9d03231461062157600080fd5b8063a3f4df7e146104fb578063abfad25d14610546578063ae91875414610559578063ae9d445c14610579578063b064b0b71461059957600080fd5b80637985c5e4146104175780637f7dde4a14610437578063877eb17914610457578063887105d3146104775780638da5cb5b1461048c5780638e54c119146104aa5780638f09162b146104bd5780638f32d59b146104d0578063a20baee61461038a57600080fd5b80634ff81443116101865780634ff81443146103195780635530273c146103395780635733d58f146103595780636f0b0c1c1461037557806372fe25aa1461038a578063741bef1a146103a657806377e16f1e146103c6578063794e5724146103e6578063795d26c31461040257600080fd5b80630b622ab2146101ef5780630e704d50146102255780631bf435551461023c5780631fd6a434146102675780633cc74225146102845780633d83908a146102a45780633db008c7146102c45780634870dd9a146102e457806348d399e7146102f9575b600080fd5b3480156101fb57600080fd5b5060085461020f906001600160a01b031681565b60405161021c9190613b15565b60405180910390f35b34801561023157600080fd5b5061023a610641565b005b34801561024857600080fd5b50610259686194049f30f720000081565b60405190815260200161021c565b34801561027357600080fd5b50610259680ad78ebc5ac620000081565b34801561029057600080fd5b5060015461020f906001600160a01b031681565b3480156102b057600080fd5b5060045461020f906001600160a01b031681565b3480156102d057600080fd5b5060075461020f906001600160a01b031681565b3480156102f057600080fd5b5061025960c881565b34801561030557600080fd5b5060055461020f906001600160a01b031681565b34801561032557600080fd5b50610259610334366004613b29565b610a6f565b34801561034557600080fd5b5061023a610354366004613b67565b610a80565b34801561036557600080fd5b506102596714d1120d7b16000081565b34801561038157600080fd5b5061023a610a98565b34801561039657600080fd5b50610259670de0b6b3a764000081565b3480156103b257600080fd5b5060025461020f906001600160a01b031681565b3480156103d257600080fd5b50600a5461020f906001600160a01b031681565b3480156103f257600080fd5b50610259670f43fc2c04ee000081565b34801561040e57600080fd5b50610259610afc565b34801561042357600080fd5b5061023a610432366004613ba9565b610bf8565b34801561044357600080fd5b5060005461020f906001600160a01b031681565b34801561046357600080fd5b5061023a610472366004613c87565b61150d565b34801561048357600080fd5b50610259611520565b34801561049857600080fd5b506003546001600160a01b031661020f565b61023a6104b8366004613cdf565b6115e5565b61023a6104cb366004613d51565b611609565b3480156104dc57600080fd5b506003546001600160a01b03163314604051901515815260200161021c565b34801561050757600080fd5b5061053960405180604001604052806012815260200171426f72726f7765724f7065726174696f6e7360701b81525081565b60405161021c9190613dcb565b61023a610554366004613dfe565b611bfe565b34801561056557600080fd5b50600b5461020f906001600160a01b031681565b34801561058557600080fd5b5061023a610594366004613b29565b611c24565b3480156105a557600080fd5b5061023a6105b4366004613b29565b611cb9565b3480156105c557600080fd5b5061023a6105d4366004613b67565b611d19565b61023a6105e7366004613b67565b611d2c565b3480156105f857600080fd5b5061023a610607366004613e36565b611d4a565b34801561061857600080fd5b50610259611de5565b34801561062d57600080fd5b5060065461020f906001600160a01b031681565b6004805460008054600a54604051635f7a196360e11b81526001600160a01b0394851695928516949091169291839163bef432c69161068291309101613b15565b602060405180830381865afa15801561069f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c39190613e53565b90506106cf8433611e07565b60025460408051630fdb11cf60e01b815290516000926001600160a01b031691630fdb11cf916004808301926020929190829003018187875af115801561071a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073e9190613e70565b9050811561074f5761074f81611ef1565b604051630b07655760e01b81526001600160a01b03861690630b0765579061077b903390600401613b15565b600060405180830381600087803b15801561079557600080fd5b505af11580156107a9573d6000803e3d6000fd5b50506040516309019aaf60e31b8152600092506001600160a01b038816915063480cd578906107dc903390600401613b15565b602060405180830381865afa1580156107f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081d9190613e70565b90506000866001600160a01b031663d66a2553336040518263ffffffff1660e01b815260040161084d9190613b15565b602060405180830381865afa15801561086a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088e9190613e70565b90506108ad85336108a8680ad78ebc5ac620000085613e9f565b611f69565b83156108d15760006108c48360008460008861204c565b90506108cf816120c1565b505b604051631fc5750960e31b81526001600160a01b0388169063fe2ba848906108fd903390600401613b15565b600060405180830381600087803b15801561091757600080fd5b505af115801561092b573d6000803e3d6000fd5b50506040516365e89c5760e11b81526001600160a01b038a16925063cbd138ae915061095b903390600401613b15565b600060405180830381600087803b15801561097557600080fd5b505af1158015610989573d6000803e3d6000fd5b50505050336001600160a01b03166000805160206140a7833981519152600080600060016040516109bd9493929190613edc565b60405180910390a26109e38686336109de680ad78ebc5ac620000086613e9f565b612151565b600654610a0690879087906001600160a01b0316680ad78ebc5ac6200000612151565b604051636250216960e01b81526001600160a01b03871690636250216990610a349033908690600401613efe565b600060405180830381600087803b158015610a4e57600080fd5b505af1158015610a62573d6000803e3d6000fd5b5050505050505050505050565b6000610a7a82612211565b92915050565b610a933384600080600087876000612226565b505050565b60095460405163b32beb5b60e01b81526001600160a01b039091169063b32beb5b90610ac8903390600401613b15565b600060405180830381600087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b50505050565b600080546040805163f07424e960e01b8152905183926001600160a01b03169163f07424e99160048083019260209291908290030181865afa158015610b46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6a9190613e70565b90506000600160009054906101000a90046001600160a01b03166001600160a01b031663f07424e96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be59190613e70565b9050610bf18183613f17565b9250505090565b6003546001600160a01b03163314610c2b5760405162461bcd60e51b8152600401610c2290613f2a565b60405180910390fd5b610c348b612800565b610c3d8a612800565b610c4689612800565b610c4f88612800565b610c5887612800565b610c6186612800565b610c6a85612800565b610c7384612800565b610c7c83612800565b610c8582612800565b6001600160a01b03811615610c9d57610c9d81612800565b600480546001600160a01b03199081166001600160a01b038e81169190911783556000805483168e83161781556001805484168e84169081179091556008805485168e85161790556006805485168d85161790556009805485168c85161790556002805485168b8516179055600b805485168a8516179055600a80548516898516179055600780548516888516179055600580549094169286169290921790925560408051638da5cb5b60e01b8152905192939192638da5cb5b928281019260209291908290030181865afa158015610d7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9e9190613f75565b6001600160a01b0316141580610e295750600154604080516348d399e760e01b815290516001600160a01b038085169316916348d399e79160048083019260209291908290030181865afa158015610dfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1e9190613f75565b6001600160a01b0316145b8015610f28575060006001600160a01b03168a6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9d9190613f75565b6001600160a01b0316141580610f285750600054604080516348d399e760e01b815290516001600160a01b038085169316916348d399e79160048083019260209291908290030181865afa158015610ef9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1d9190613f75565b6001600160a01b0316145b8015611027575060006001600160a01b0316886001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9c9190613f75565b6001600160a01b03161415806110275750600854604080516348d399e760e01b815290516001600160a01b038085169316916348d399e79160048083019260209291908290030181865afa158015610ff8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101c9190613f75565b6001600160a01b0316145b8015611126575060006001600160a01b0316866001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611077573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061109b9190613f75565b6001600160a01b03161415806111265750600954604080516348d399e760e01b815290516001600160a01b038085169316916348d399e79160048083019260209291908290030181865afa1580156110f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061111b9190613f75565b6001600160a01b0316145b8015611225575060075460408051633bf0b78f60e11b815290516000926001600160a01b0316916377e16f1e9160048083019260209291908290030181865afa158015611177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119b9190613f75565b6001600160a01b0316148061122557506007546040805163724d374560e01b815290516001600160a01b0380851693169163724d37459160048083019260209291908290030181865afa1580156111f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121a9190613f75565b6001600160a01b0316145b6112a85760405162461bcd60e51b815260206004820152604860248201527f5468652073616d6520636f6c6c61746572616c2061646472657373206d75737460448201527f206265207573656420666f722074686520656e7469726520736574206f6620636064820152676f6e74726163747360c01b608482015260a401610c22565b7f143219c9e69b09e07e095fcc889b43d8f46ca892bba65f08dc3a0050869a56788b6040516112d79190613b15565b60405180910390a17f78f058b189175430c48dc02699e3a0031ea4ff781536dc2fab847de4babdd8828a60405161130e9190613b15565b60405180910390a17f5ee0cae2f063ed938bb55046f6a932fb6ae792bf43624806bb90abe68a50be9b896040516113459190613b15565b60405180910390a17f82966d27eea39b038ee0fa30cd16532bb24f6e65d31cb58fb227aa5766cdcc7f8860405161137c9190613b15565b60405180910390a17fcfb07d791fcafc032b35837b50eb84b74df518cf4cc287e8084f47630fa70fa0876040516113b39190613b15565b60405180910390a17fe67f36a6e961157d6eff83b91f3af5a62131ceb6f04954ef74f51c1c05e7f88d866040516113ea9190613b15565b60405180910390a17f8c537274438aa850a330284665d81a85dd38267d09e4050d416bfc94142db264856040516114219190613b15565b60405180910390a17f65f4cf077bc01e4742eb5ad98326f6e95b63548ea24b17f8d5e823111fe78800846040516114589190613b15565b60405180910390a17fe1e858a66c0bbbcdfa22d58dde1e5d42370be20cdb176e560287f85412e546e08360405161148f9190613b15565b60405180910390a17f38335c64466e2376ab931166337e19127650d842036ebe01da1ba3e5c1255ebb826040516114c69190613b15565b60405180910390a17ffba7421f3d1a98e80d72491a6e0523133444a5842cc7310951d8b82d075a7dff816040516114fd9190613b15565b60405180910390a1610a626128a9565b610af6336000856001600087878b612226565b6000805460408051631529a63960e01b8152905183926001600160a01b031691631529a6399160048083019260209291908290030181865afa15801561156a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158e9190613e70565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc1573d6000803e3d6000fd5b6115ee836128f3565b9250611600338787878787878e612226565b50505050505050565b6040805160608082018352600480546001600160a01b039081168452600080548216602080870191909152600a548316868801528651610100810188528281528082018390528088018390529485018290526080850182905260a0850182905260c0850182905260e085018290526002548751630fdb11cf60e01b8152975196979596931694630fdb11cf948181019492939183900301908290875af11580156116b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116db9190613e70565b8082526000906116ea9061297c565b90506116f68882612999565b82516117029033612aad565b604082018790528061173e5761172283600001518460400151898b612b80565b6020830181905260408301805161173a908390613f17565b9052505b61174b8260400151612cc0565b6117588260400151612211565b6060830181905261176b5761176b613f5f565b611774866128f3565b95506117898683606001518460000151612d3f565b6080830152606082015161179e908790612d73565b60a083015280156117bb576117b68260800151612daa565b6117ee565b6117c88260800151612e22565b60006117e187600185606001516001876000015161204c565b90506117ec816120c1565b505b825160405163953f0bb160e01b81526001600160a01b039091169063953f0bb190611820903390600190600401613f92565b600060405180830381600087803b15801561183a57600080fd5b505af115801561184e573d6000803e3d6000fd5b505084516040516372423c1760e01b81526001600160a01b0390911692506372423c1791506118839033908a90600401613efe565b6020604051808303816000875af11580156118a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c69190613e70565b5082516060830151604051639976cf4560e01b81526001600160a01b0390921691639976cf45916118fc91339190600401613efe565b6020604051808303816000875af115801561191b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193f9190613e70565b5082516040516382fe3eb960e01b81526001600160a01b03909116906382fe3eb99061196f903390600401613b15565b600060405180830381600087803b15801561198957600080fd5b505af115801561199d573d6000803e3d6000fd5b50508451604051630c7940bd60e11b81526001600160a01b0390911692506318f2817a91506119d0903390600401613b15565b6020604051808303816000875af11580156119ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a139190613e70565b60c0830152600b5460a08301516040516346f7cf8760e01b81526001600160a01b03909216916346f7cf8791611a52913391908a908a90600401613fbf565b600060405180830381600087803b158015611a6c57600080fd5b505af1158015611a80573d6000803e3d6000fd5b505084516040516315d549f160e01b81526001600160a01b0390911692506315d549f19150611ab3903390600401613b15565b6020604051808303816000875af1158015611ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af69190613e70565b60e0830181905260405190815233907f59cfd0cd754bc5748b6770e94a4ffa5f678d885cb899dcfadc5734edb97c67ab9060200160405180910390a2611b40836020015187612eb2565b611b5983602001518460400151338a8660400151612f3c565b60208301516040840151600654611b859291906001600160a01b0316680ad78ebc5ac620000080612f3c565b606082015160c083015160405133926000805160206140a783398151915292611bb3928b9190600090613edc565b60405180910390a260208083015160405190815233917f65bd7154d79916d54881d83a1b22e14f736830d7a4995e5b97f4af8520718e22910160405180910390a25050505050505050565b611c06612ffd565b611c0f836128f3565b9250610af68460008060008787876000612226565b6007546001600160a01b03163314611c4e5760405162461bcd60e51b8152600401610c2290613fea565b600a54600754604051632770a7eb60e21b81526001600160a01b0392831692639dc29fac92611c84929116908590600401613efe565b600060405180830381600087803b158015611c9e57600080fd5b505af1158015611cb2573d6000803e3d6000fd5b5050505050565b6007546001600160a01b03163314611ce35760405162461bcd60e51b8152600401610c2290613fea565b600a546007546040516340c10f1960e01b81526001600160a01b03928316926340c10f1992611c84929116908590600401613efe565b610a933360008560008087876000612226565b611d35836128f3565b9250610a933360008060008787876000612226565b6003546001600160a01b03163314611d745760405162461bcd60e51b8152600401610c2290613f2a565b6001600160a01b038116611dd95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610c22565b611de28161306b565b50565b611df96103e8670de0b6b3a7640000614030565b611e04906005614052565b81565b6040516321e3780160e01b81526000906001600160a01b038416906321e3780190611e36908590600401613b15565b602060405180830381865afa158015611e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e779190614069565b90506001816004811115611e8d57611e8d613eb2565b14610a935760405162461bcd60e51b815260206004820152602e60248201527f426f72726f7765724f70733a2054726f766520646f6573206e6f74206578697360448201526d1d081bdc881a5cc818db1bdcd95960921b6064820152608401610c22565b611efa8161297c565b15611de25760405162461bcd60e51b815260206004820152603960248201527f426f72726f7765724f70733a204f7065726174696f6e206e6f74207065726d696044820152787474656420647572696e67205265636f76657279204d6f646560381b6064820152608401610c22565b6040516370a0823160e01b815281906001600160a01b038516906370a0823190611f97908690600401613b15565b602060405180830381865afa158015611fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd89190613e70565b1015610a935760405162461bcd60e51b815260206004820152603e60248201527f426f72726f7765724f70733a2043616c6c657220646f65736e7420686176652060448201527f656e6f75676820544855534420746f206d616b652072657061796d656e7400006064820152608401610c22565b600080612057611520565b90506000612063610afc565b905086612079576120748883613e9f565b612083565b6120838883613f17565b915084612099576120948682613e9f565b6120a3565b6120a38682613f17565b905060006120b2838387612d3f565b93505050505b95945050505050565b6714d1120d7b160000811015611de25760405162461bcd60e51b815260206004820152604960248201527f426f72726f7765724f70733a20416e206f7065726174696f6e2074686174207760448201527f6f756c6420726573756c7420696e20544352203c20434352206973206e6f74206064820152681c195c9b5a5d1d195960ba1b608482015260a401610c22565b604051637b7fc9eb60e11b8152600481018290526001600160a01b0385169063f6ff93d690602401600060405180830381600087803b15801561219357600080fd5b505af11580156121a7573d6000803e3d6000fd5b5050604051632770a7eb60e21b81526001600160a01b0386169250639dc29fac91506121d99085908590600401613efe565b600060405180830381600087803b1580156121f357600080fd5b505af1158015612207573d6000803e3d6000fd5b5050505050505050565b6000610a7a680ad78ebc5ac620000083613f17565b6040805160608082018352600480546001600160a01b039081168452600080548216602080870191909152600a5483168688015286516101a0810188528281528082018390528088018390529485018290526080850182905260a0850182905260c0850182905260e08501829052610100850182905261012085018290526101408501829052610160850182905261018085018290526002548751630fdb11cf60e01b8152975196979596931694630fdb11cf948181019492939183900301908290875af11580156122fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123209190613e70565b80825260009061232f9061297c565b9050871561234a576123418482612999565b61234a896130bd565b6123548a8861312d565b61235f8a8a8961319d565b825161236b908c611e07565b336001600160a01b038c1614806123a157506008546001600160a01b0316331480156123975750600087115b80156123a1575088155b6123ad576123ad613f5f565b8251604051630b07655760e01b81526001600160a01b0390911690630b076557906123dc908e90600401613b15565b600060405180830381600087803b1580156123f657600080fd5b505af115801561240a573d6000803e3d6000fd5b50505050612418878b613235565b15156060840152602083015260408201899052878015612436575080155b1561246c5761244f836000015184604001518b87612b80565b6101208301819052604083018051612468908390613f17565b9052505b825160405163d66a255360e01b81526001600160a01b039091169063d66a25539061249b908e90600401613b15565b602060405180830381865afa1580156124b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124dc9190613e70565b608083015282516040516309019aaf60e31b81526001600160a01b039091169063480cd57890612510908e90600401613b15565b602060405180830381865afa15801561252d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125519190613e70565b60a083018190526080830151835161256a929190612d3f565b8260c001818152505061259a8260a0015183608001518460200151856060015186604001518d8860000151613254565b60e083015260a08201518a11156125b3576125b3613f5f565b6125bf818b8a85613287565b871580156125cd5750600089115b1561261b576125f682604001516125e784608001516132fb565b6125f19190613e9f565b612cc0565b61260882608001518360400151613310565b61261b83604001518c8460400151611f69565b61263983600001518c8460200151856060015186604001518d6133a4565b6101408401526101608301528251604051630c7940bd60e11b81526001600160a01b03909116906318f2817a90612674908e90600401613b15565b6020604051808303816000875af1158015612693573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b79190613e70565b8261018001818152505060006126e58360a0015184608001518560200151866060015187604001518e613596565b600b5460405163015f109360e51b81529192506001600160a01b031690632be212609061271c908f9085908c908c90600401613fbf565b600060405180830381600087803b15801561273657600080fd5b505af115801561274a573d6000803e3d6000fd5b505050508b6001600160a01b03166000805160206140a7833981519152846101400151856101600151866101800151600260405161278b9493929190613edc565b60405180910390a261012083015160405190815233907f65bd7154d79916d54881d83a1b22e14f736830d7a4995e5b97f4af8520718e229060200160405180910390a26127f28460200151856040015133866020015187606001518f8f8a604001516135c7565b505050505050505050505050565b6001600160a01b0381166128565760405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606401610c22565b803b806128a55760405162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152606401610c22565b5050565b6003546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600380546001600160a01b0319169055565b6005546000906001600160a01b031661290d575034919050565b34156129785760405162461bcd60e51b815260206004820152603460248201527f426f72726f7765724f7065726174696f6e733a20455243323020636f6c6c61746044820152730cae4c2d840dccacac8cac85840dcdee8408aa8960631b6064820152608401610c22565b5090565b6000806129888361365c565b6714d1120d7b160000119392505050565b8015612a1257670de0b6b3a76400008211156128a55760405162461bcd60e51b815260206004820152603260248201527f4d6178206665652070657263656e74616765206d757374206c657373207468616044820152716e206f7220657175616c20746f203130302560701b6064820152608401610c22565b612a266103e8670de0b6b3a7640000614030565b612a31906005614052565b8210158015612a485750670de0b6b3a76400008211155b6128a55760405162461bcd60e51b815260206004820152603060248201527f4d6178206665652070657263656e74616765206d75737420626520626574776560448201526f656e20302e352520616e64203130302560801b6064820152608401610c22565b6040516321e3780160e01b81526000906001600160a01b038416906321e3780190612adc908590600401613b15565b602060405180830381865afa158015612af9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1d9190614069565b90506001816004811115612b3357612b33613eb2565b03610a935760405162461bcd60e51b815260206004820152601c60248201527f426f72726f7765724f70733a2054726f766520697320616374697665000000006044820152606401610c22565b6000846001600160a01b0316635dba4c4a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612bbd57600080fd5b505af1158015612bd1573d6000803e3d6000fd5b5050604051630631203b60e41b815260048101869052600092506001600160a01b038816915063631203b090602401602060405180830381865afa158015612c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c419190613e70565b9050612c4e818585613680565b6007546040516340c10f1960e01b81526001600160a01b03878116926340c10f1992612c8292909116908590600401613efe565b600060405180830381600087803b158015612c9c57600080fd5b505af1158015612cb0573d6000803e3d6000fd5b509293505050505b949350505050565b686194049f30f7200000811015611de25760405162461bcd60e51b815260206004820152603a60248201527f426f72726f7765724f70733a2054726f76652773206e65742064656274206d7560448201527f73742062652067726561746572207468616e206d696e696d756d0000000000006064820152608401610c22565b60008215612d6757600083612d548487614052565b612d5e9190614030565b9150612d6c9050565b506000195b9392505050565b60008115612da05781612d8f68056bc75e2d6310000085614052565b612d999190614030565b9050610a7a565b5060001992915050565b6714d1120d7b160000811015611de25760405162461bcd60e51b815260206004820152603760248201527f426f72726f7765724f70733a204f7065726174696f6e206d757374206c65617660448201527632903a3937bb32903bb4ba341024a1a9101f1e9021a1a960491b6064820152608401610c22565b670f43fc2c04ee0000811015611de25760405162461bcd60e51b815260206004820152604960248201527f426f72726f7765724f70733a20416e206f7065726174696f6e2074686174207760448201527f6f756c6420726573756c7420696e20494352203c204d4352206973206e6f74206064820152681c195c9b5a5d1d195960ba1b608482015260a401610c22565b600554612eca906001600160a01b03163384846136f1565b6005546001600160a01b0316612ede575050565b604051634c6ccf7360e11b8152600481018290526001600160a01b038316906398d99ee690602401600060405180830381600087803b158015612f2057600080fd5b505af1158015612f34573d6000803e3d6000fd5b505050505050565b6040516342c31a2360e01b8152600481018290526001600160a01b038616906342c31a2390602401600060405180830381600087803b158015612f7e57600080fd5b505af1158015612f92573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b03871692506340c10f199150612fc49086908690600401613efe565b600060405180830381600087803b158015612fde57600080fd5b505af1158015612ff2573d6000803e3d6000fd5b505050505050505050565b6008546001600160a01b031633146130695760405162461bcd60e51b815260206004820152602960248201527f426f72726f7765724f70733a2043616c6c6572206973206e6f742053746162696044820152681b1a5d1e48141bdbdb60ba1b6064820152608401610c22565b565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008111611de25760405162461bcd60e51b815260206004820152603760248201527f426f72726f7765724f70733a204465627420696e637265617365207265717569604482015276726573206e6f6e2d7a65726f20646562744368616e676560481b6064820152608401610c22565b801580613138575081155b6128a55760405162461bcd60e51b815260206004820152603060248201527f426f72726f7765724f7065726174696f6e733a2043616e6e6f7420776974686460448201526f1c985dc8185b99081859190818dbdb1b60821b6064820152608401610c22565b801515806131aa57508215155b806131b457508115155b610a935760405162461bcd60e51b815260206004820152604660248201527f426f72726f7765724f70733a205468657265206d75737420626520656974686560448201527f72206120636f6c6c61746572616c206368616e6765206f7220612064656274206064820152656368616e676560d01b608482015260a401610c22565b60008083156132495750829050600161324d565b8291505b9250929050565b60008060006132678a8a8a8a8a8a6137b2565b915091506000613278838387612d3f565b9b9a5050505050505050505050565b83156132c057613296836137f0565b81156132bb576132a98160e00151612daa565b6132bb8160e001518260c00151613864565b610af6565b6132cd8160e00151612e22565b6132ea81602001518260600151836040015185856000015161204c565b6101008201819052610af6906120c1565b6000610a7a680ad78ebc5ac620000083613e9f565b613323680ad78ebc5ac620000083613e9f565b8111156128a55760405162461bcd60e51b815260206004820152604360248201527f426f72726f7765724f70733a20416d6f756e7420726570616964206d7573742060448201527f6e6f74206265206c6172676572207468616e207468652054726f76652773206460648201526219589d60ea1b608482015260a401610c22565b6000806000856134245760405163d3d6f84360e01b81526001600160a01b038a169063d3d6f843906133dc908b908b90600401613efe565b6020604051808303816000875af11580156133fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061341f9190613e70565b613495565b6040516372423c1760e01b81526001600160a01b038a16906372423c1790613452908b908b90600401613efe565b6020604051808303816000875af1158015613471573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134959190613e70565b905060008461351457604051630930874960e11b81526001600160a01b038b16906312610e92906134cc908c908a90600401613efe565b6020604051808303816000875af11580156134eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350f9190613e70565b613585565b604051639976cf4560e01b81526001600160a01b038b1690639976cf4590613542908c908a90600401613efe565b6020604051808303816000875af1158015613561573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135859190613e70565b919a91995090975050505050505050565b60008060006135a98989898989896137b2565b9150915060006135b98383612d73565b9a9950505050505050505050565b81156135df576135da8888888685612f3c565b6135eb565b6135eb88888886612151565b8315613600576135fb8886612eb2565b612207565b604051636250216960e01b81526001600160a01b0389169063625021699061362e9089908990600401613efe565b600060405180830381600087803b15801561364857600080fd5b505af11580156127f2573d6000803e3d6000fd5b600080613667611520565b90506000613673610afc565b9050612cb8828286612d3f565b600082613695670de0b6b3a764000086614052565b61369f9190614030565b905081811115610af65760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610c22565b6001600160a01b03841661379d576000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461374c576040519150601f19603f3d011682016040523d82523d6000602084013e613751565b606091505b50509050806137975760405162461bcd60e51b815260206004820152601260248201527114d95b991a5b99c81155120819985a5b195960721b6044820152606401610c22565b50610af6565b610af66001600160a01b0385168484846138da565b6000808787866137cb576137c6888b613e9f565b6137d5565b6137d5888b613f17565b9150846137e65761350f868a613e9f565b613585868a613f17565b8015611de25760405162461bcd60e51b815260206004820152603e60248201527f426f72726f7765724f70733a20436f6c6c61746572616c20776974686472617760448201527f616c206e6f74207065726d6974746564205265636f76657279204d6f646500006064820152608401610c22565b808210156128a55760405162461bcd60e51b815260206004820152603e60248201527f426f72726f7765724f70733a2043616e6e6f7420646563726561736520796f7560448201527f722054726f766527732049435220696e205265636f76657279204d6f646500006064820152608401610c22565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092018352602080830180516001600160e01b03166323b872dd60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656490840152610af6928792916000916139729185169084906139f2565b90508051600014806139935750808060200190518101906139939190613e53565b610a935760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c22565b6060612cb8848460008585600080866001600160a01b03168587604051613a19919061408a565b60006040518083038185875af1925050503d8060008114613a56576040519150601f19603f3d011682016040523d82523d6000602084013e613a5b565b606091505b5091509150613a6c87838387613a77565b979650505050505050565b60608315613ae6578251600003613adf576001600160a01b0385163b613adf5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c22565b5081612cb8565b612cb88383815115613afb5781518083602001fd5b8060405162461bcd60e51b8152600401610c229190613dcb565b6001600160a01b0391909116815260200190565b600060208284031215613b3b57600080fd5b5035919050565b6001600160a01b0381168114611de257600080fd5b8035613b6281613b42565b919050565b600080600060608486031215613b7c57600080fd5b833592506020840135613b8e81613b42565b91506040840135613b9e81613b42565b809150509250925092565b60008060008060008060008060008060006101608c8e031215613bcb57600080fd5b8b35613bd681613b42565b9a5060208c0135613be681613b42565b995060408c0135613bf681613b42565b985060608c0135613c0681613b42565b975060808c0135613c1681613b42565b965060a08c0135613c2681613b42565b955060c08c0135613c3681613b42565b945060e08c0135613c4681613b42565b93506101008c0135613c5781613b42565b9250613c666101208d01613b57565b9150613c756101408d01613b57565b90509295989b509295989b9093969950565b60008060008060808587031215613c9d57600080fd5b84359350602085013592506040850135613cb681613b42565b91506060850135613cc681613b42565b939692955090935050565b8015158114611de257600080fd5b600080600080600080600060e0888a031215613cfa57600080fd5b8735965060208801359550604088013594506060880135613d1a81613cd1565b93506080880135925060a0880135613d3181613b42565b915060c0880135613d4181613b42565b8091505092959891949750929550565b600080600080600060a08688031215613d6957600080fd5b8535945060208601359350604086013592506060860135613d8981613b42565b91506080860135613d9981613b42565b809150509295509295909350565b60005b83811015613dc2578181015183820152602001613daa565b50506000910152565b6020815260008251806020840152613dea816040850160208701613da7565b601f01601f19169190910160400192915050565b60008060008060808587031215613e1457600080fd5b8435613e1f81613b42565b9350602085013592506040850135613cb681613b42565b600060208284031215613e4857600080fd5b8135612d6c81613b42565b600060208284031215613e6557600080fd5b8151612d6c81613cd1565b600060208284031215613e8257600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7a57610a7a613e89565b634e487b7160e01b600052602160045260246000fd5b60038110613ed857613ed8613eb2565b9052565b8481526020810184905260408101839052608081016120b86060830184613ec8565b6001600160a01b03929092168252602082015260400190565b80820180821115610a7a57610a7a613e89565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052600160045260246000fd5b600060208284031215613f8757600080fd5b8151612d6c81613b42565b6001600160a01b03831681526040810160058310613fb257613fb2613eb2565b8260208301529392505050565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b60208082526026908201527f426f72726f7765724f7065726174696f6e733a2063616c6c6572206d757374206040820152653132902821ab60d11b606082015260800190565b60008261404d57634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417610a7a57610a7a613e89565b60006020828403121561407b57600080fd5b815160058110612d6c57600080fd5b6000825161409c818460208701613da7565b919091019291505056fec3770d654ed33aeea6bf11ac8ef05d02a6a04ed4686dd2f624d853bbec43cc8ba2646970667358221220b59a6470e6a4da3df2a18f41adcd97f2de72b0b18a12778574b1386177c33e8564736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (100 runs)
IPCV.sol 57 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ITHUSDToken.sol";
interface IPCV {
// --- Events --
event THUSDTokenAddressSet(address _thusdTokenAddress);
event BorrowerOperationsAddressSet(address _borrowerOperationsAddress);
event CollateralAddressSet(address _collateralAddress);
event BAMMAddressSet(address _bammAddress);
event RolesSet(address _council, address _treasury);
event BAMMDeposit(uint256 _thusdAmount);
event BAMMWithdraw(uint256 _numShares);
event THUSDWithdraw(address _recipient, uint256 _thusdAmount);
event CollateralWithdraw(address _recipient, uint256 _collateralAmount);
event PCVDebtPaid(uint256 _paidDebt);
event RecipientAdded(address _recipient);
event RecipientRemoved(address _recipient);
// --- Functions ---
function debtToPay() external returns(uint256);
function payDebt(uint256 _thusdToBurn) external;
function setAddresses(
address _thusdTokenAddress,
address _borrowerOperations,
address payable _bammAddress,
address _collateralERC20
) external;
function initialize() external;
function depositToBAMM(uint256 _thusdAmount) external;
function withdrawFromBAMM(uint256 _numShares) external;
function withdrawTHUSD(address _recipient, uint256 _thusdAmount) external;
function withdrawCollateral(address _recipient, uint256 _collateralAmount) external;
function addRecipientToWhitelist(address _recipient) external;
function addRecipientsToWhitelist(address[] calldata _recipients) external;
function removeRecipientFromWhitelist(address _recipient) external;
function removeRecipientsFromWhitelist(address[] calldata _recipients) external;
function startChangingRoles(address _council, address _treasury) external;
function cancelChangingRoles() external;
function finalizeChangingRoles() external;
function collateralERC20() external view returns(IERC20);
function thusdToken() external view returns(ITHUSDToken);
}
IPool.sol 26 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Common interface for the Pools.
interface IPool {
// --- Events ---
event CollateralBalanceUpdated(uint256 _newBalance);
event THUSDBalanceUpdated(uint256 _newBalance);
event ActivePoolAddressChanged(address _newActivePoolAddress);
event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
event CollateralSent(address _to, uint256 _amount);
// --- Functions ---
function getCollateralBalance() external view returns (uint);
function getTHUSDDebt() external view returns (uint);
function increaseTHUSDDebt(uint256 _amount) external;
function decreaseTHUSDDebt(uint256 _amount) external;
}
BorrowerOperations.sol 705 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./Interfaces/IBorrowerOperations.sol";
import "./Interfaces/ITroveManager.sol";
import "./Interfaces/ITHUSDToken.sol";
import "./Interfaces/ICollSurplusPool.sol";
import "./Interfaces/ISortedTroves.sol";
import "./Interfaces/IPCV.sol";
import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";
import "./Dependencies/SendCollateral.sol";
contract BorrowerOperations is LiquityBase, Ownable, CheckContract, SendCollateral, IBorrowerOperations {
string constant public NAME = "BorrowerOperations";
// --- Connected contract declarations ---
ITroveManager public troveManager;
address public collateralAddress;
address public gasPoolAddress;
address public pcvAddress;
address public stabilityPoolAddress;
ICollSurplusPool collSurplusPool;
ITHUSDToken public thusdToken;
// A doubly linked list of Troves, sorted by their collateral ratios
ISortedTroves public sortedTroves;
/* --- Variable container structs ---
Used to hold, return and assign variables inside a function, in order to avoid the error:
"CompilerError: Stack too deep". */
struct LocalVariables_adjustTrove {
uint256 price;
uint256 collChange;
uint256 netDebtChange;
bool isCollIncrease;
uint256 debt;
uint256 coll;
uint256 oldICR;
uint256 newICR;
uint256 newTCR;
uint256 THUSDFee;
uint256 newDebt;
uint256 newColl;
uint256 stake;
}
struct LocalVariables_openTrove {
uint256 price;
uint256 THUSDFee;
uint256 netDebt;
uint256 compositeDebt;
uint256 ICR;
uint256 NICR;
uint256 stake;
uint256 arrayIndex;
}
struct ContractsCache {
ITroveManager troveManager;
IActivePool activePool;
ITHUSDToken thusdToken;
}
enum BorrowerOperation {
openTrove,
closeTrove,
adjustTrove
}
event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, BorrowerOperation operation);
// --- Dependency setters ---
function setAddresses(
address _troveManagerAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
address _gasPoolAddress,
address _collSurplusPoolAddress,
address _priceFeedAddress,
address _sortedTrovesAddress,
address _thusdTokenAddress,
address _pcvAddress,
address _collateralAddress
)
external
override
onlyOwner
{
// This makes impossible to open a trove with zero withdrawn THUSD
assert(MIN_NET_DEBT > 0);
checkContract(_troveManagerAddress);
checkContract(_activePoolAddress);
checkContract(_defaultPoolAddress);
checkContract(_stabilityPoolAddress);
checkContract(_gasPoolAddress);
checkContract(_collSurplusPoolAddress);
checkContract(_priceFeedAddress);
checkContract(_sortedTrovesAddress);
checkContract(_thusdTokenAddress);
checkContract(_pcvAddress);
if (_collateralAddress != address(0)) {
checkContract(_collateralAddress);
}
troveManager = ITroveManager(_troveManagerAddress);
activePool = IActivePool(_activePoolAddress);
defaultPool = IDefaultPool(_defaultPoolAddress);
stabilityPoolAddress = _stabilityPoolAddress;
gasPoolAddress = _gasPoolAddress;
collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress);
priceFeed = IPriceFeed(_priceFeedAddress);
sortedTroves = ISortedTroves(_sortedTrovesAddress);
thusdToken = ITHUSDToken(_thusdTokenAddress);
pcvAddress = _pcvAddress;
collateralAddress = _collateralAddress;
require(
(Ownable(_defaultPoolAddress).owner() != address(0) ||
defaultPool.collateralAddress() == _collateralAddress) &&
(Ownable(_activePoolAddress).owner() != address(0) ||
activePool.collateralAddress() == _collateralAddress) &&
(Ownable(_stabilityPoolAddress).owner() != address(0) ||
IStabilityPool(stabilityPoolAddress).collateralAddress() == _collateralAddress) &&
(Ownable(_collSurplusPoolAddress).owner() != address(0) ||
collSurplusPool.collateralAddress() == _collateralAddress) &&
(address(IPCV(pcvAddress).thusdToken()) == address(0) ||
address(IPCV(pcvAddress).collateralERC20()) == _collateralAddress),
"The same collateral address must be used for the entire set of contracts"
);
emit TroveManagerAddressChanged(_troveManagerAddress);
emit ActivePoolAddressChanged(_activePoolAddress);
emit DefaultPoolAddressChanged(_defaultPoolAddress);
emit StabilityPoolAddressChanged(_stabilityPoolAddress);
emit GasPoolAddressChanged(_gasPoolAddress);
emit CollSurplusPoolAddressChanged(_collSurplusPoolAddress);
emit PriceFeedAddressChanged(_priceFeedAddress);
emit SortedTrovesAddressChanged(_sortedTrovesAddress);
emit THUSDTokenAddressChanged(_thusdTokenAddress);
emit PCVAddressChanged(_pcvAddress);
emit CollateralAddressChanged(_collateralAddress);
_renounceOwnership();
}
/// Calls on PCV behalf
function mintBootstrapLoanFromPCV(uint256 _thusdToMint) external {
require(msg.sender == pcvAddress, "BorrowerOperations: caller must be PCV");
thusdToken.mint(pcvAddress, _thusdToMint);
}
function burnDebtFromPCV(uint256 _thusdToBurn) external {
require(msg.sender == pcvAddress, "BorrowerOperations: caller must be PCV");
thusdToken.burn(pcvAddress, _thusdToBurn);
}
// --- Borrower Trove Operations ---
function openTrove(uint256 _maxFeePercentage, uint256 _THUSDAmount, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable override {
ContractsCache memory contractsCache = ContractsCache(troveManager, activePool, thusdToken);
LocalVariables_openTrove memory vars;
vars.price = priceFeed.fetchPrice();
bool isRecoveryMode = _checkRecoveryMode(vars.price);
_requireValidMaxFeePercentage(_maxFeePercentage, isRecoveryMode);
_requireTroveisNotActive(contractsCache.troveManager, msg.sender);
vars.THUSDFee;
vars.netDebt = _THUSDAmount;
if (!isRecoveryMode) {
vars.THUSDFee = _triggerBorrowingFee(contractsCache.troveManager, contractsCache.thusdToken, _THUSDAmount, _maxFeePercentage);
vars.netDebt += vars.THUSDFee;
}
_requireAtLeastMinNetDebt(vars.netDebt);
// ICR is based on the composite debt, i.e. the requested THUSD amount + THUSD borrowing fee + THUSD gas comp.
vars.compositeDebt = _getCompositeDebt(vars.netDebt);
assert(vars.compositeDebt > 0);
// if ETH overwrite the asset value
_assetAmount = getAssetAmount(_assetAmount);
vars.ICR = LiquityMath._computeCR(_assetAmount, vars.compositeDebt, vars.price);
vars.NICR = LiquityMath._computeNominalCR(_assetAmount, vars.compositeDebt);
if (isRecoveryMode) {
_requireICRisAboveCCR(vars.ICR);
} else {
_requireICRisAboveMCR(vars.ICR);
uint256 newTCR = _getNewTCRFromTroveChange(_assetAmount, true, vars.compositeDebt, true, vars.price); // bools: coll increase, debt increase
_requireNewTCRisAboveCCR(newTCR);
}
// Set the trove struct's properties
contractsCache.troveManager.setTroveStatus(msg.sender, ITroveManager.Status.active);
contractsCache.troveManager.increaseTroveColl(msg.sender, _assetAmount);
contractsCache.troveManager.increaseTroveDebt(msg.sender, vars.compositeDebt);
contractsCache.troveManager.updateTroveRewardSnapshots(msg.sender);
vars.stake = contractsCache.troveManager.updateStakeAndTotalStakes(msg.sender);
sortedTroves.insert(msg.sender, vars.NICR, _upperHint, _lowerHint);
vars.arrayIndex = contractsCache.troveManager.addTroveOwnerToArray(msg.sender);
emit TroveCreated(msg.sender, vars.arrayIndex);
/*
* Move the collateral to the Active Pool, and mint the THUSDAmount to the borrower
* If the user has insuffient tokens to do the transfer to the Active Pool an error will cause the transaction to revert.
*/
_activePoolAddColl(contractsCache.activePool, _assetAmount);
_withdrawTHUSD(contractsCache.activePool, contractsCache.thusdToken, msg.sender, _THUSDAmount, vars.netDebt);
// Move the THUSD gas compensation to the Gas Pool
_withdrawTHUSD(contractsCache.activePool, contractsCache.thusdToken, gasPoolAddress, THUSD_GAS_COMPENSATION, THUSD_GAS_COMPENSATION);
emit TroveUpdated(msg.sender, vars.compositeDebt, _assetAmount, vars.stake, BorrowerOperation.openTrove);
emit THUSDBorrowingFeePaid(msg.sender, vars.THUSDFee);
}
// Send collateral to a trove
function addColl(uint256 _assetAmount, address _upperHint, address _lowerHint) external payable override {
_assetAmount = getAssetAmount(_assetAmount);
_adjustTrove(msg.sender, 0, 0, false, _assetAmount, _upperHint, _lowerHint, 0);
}
// Send collateral to a trove. Called by only the Stability Pool.
function moveCollateralGainToTrove(address _borrower, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable override {
_requireCallerIsStabilityPool();
_assetAmount = getAssetAmount(_assetAmount);
_adjustTrove(_borrower, 0, 0, false, _assetAmount, _upperHint, _lowerHint, 0);
}
function getAssetAmount(uint256 _assetAmount) internal view returns (uint256) {
if (collateralAddress == address(0)) {
return msg.value;
}
require(msg.value == 0, "BorrowerOperations: ERC20 collateral needed, not ETH");
return _assetAmount;
}
// Withdraw collateral from a trove
function withdrawColl(uint256 _collWithdrawal, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, _collWithdrawal, 0, false, 0, _upperHint, _lowerHint, 0);
}
// Withdraw THUSD tokens from a trove: mint new THUSD tokens to the owner, and increase the trove's debt accordingly
function withdrawTHUSD(uint256 _maxFeePercentage, uint256 _THUSDAmount, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, 0, _THUSDAmount, true, 0, _upperHint, _lowerHint, _maxFeePercentage);
}
// Repay THUSD tokens to a Trove: Burn the repaid THUSD tokens, and reduce the trove's debt accordingly
function repayTHUSD(uint256 _THUSDAmount, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, 0, _THUSDAmount, false, 0, _upperHint, _lowerHint, 0);
}
function adjustTrove(uint256 _maxFeePercentage, uint256 _collWithdrawal, uint256 _THUSDChange, bool _isDebtIncrease, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable override {
_assetAmount = getAssetAmount(_assetAmount);
_adjustTrove(msg.sender, _collWithdrawal, _THUSDChange, _isDebtIncrease, _assetAmount, _upperHint, _lowerHint, _maxFeePercentage);
}
/*
* _adjustTrove(): Alongside a debt change, this function can perform either a collateral top-up or a collateral withdrawal.
*
* It therefore expects either a positive msg.value, or a positive _collWithdrawal argument.
*
* If both are positive, it will revert.
*/
function _adjustTrove(address _borrower, uint256 _collWithdrawal, uint256 _THUSDChange, bool _isDebtIncrease, uint256 _assetAmount, address _upperHint, address _lowerHint, uint256 _maxFeePercentage) internal {
ContractsCache memory contractsCache = ContractsCache(troveManager, activePool, thusdToken);
LocalVariables_adjustTrove memory vars;
vars.price = priceFeed.fetchPrice();
bool isRecoveryMode = _checkRecoveryMode(vars.price);
if (_isDebtIncrease) {
_requireValidMaxFeePercentage(_maxFeePercentage, isRecoveryMode);
_requireNonZeroDebtChange(_THUSDChange);
}
_requireSingularCollChange(_collWithdrawal, _assetAmount);
_requireNonZeroAdjustment(_collWithdrawal, _THUSDChange, _assetAmount);
_requireTroveisActive(contractsCache.troveManager, _borrower);
// Confirm the operation is either a borrower adjusting their own trove, or a pure collateral transfer from the Stability Pool to a trove
assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && _assetAmount > 0 && _THUSDChange == 0));
contractsCache.troveManager.applyPendingRewards(_borrower);
// Get the collChange based on whether or not collateral was sent in the transaction
(vars.collChange, vars.isCollIncrease) = _getCollChange(_assetAmount, _collWithdrawal);
vars.netDebtChange = _THUSDChange;
// If the adjustment incorporates a debt increase and system is in Normal Mode, then trigger a borrowing fee
if (_isDebtIncrease && !isRecoveryMode) {
vars.THUSDFee = _triggerBorrowingFee(contractsCache.troveManager, contractsCache.thusdToken, _THUSDChange, _maxFeePercentage);
vars.netDebtChange += vars.THUSDFee; // The raw debt change includes the fee
}
vars.debt = contractsCache.troveManager.getTroveDebt(_borrower);
vars.coll = contractsCache.troveManager.getTroveColl(_borrower);
// Get the trove's old ICR before the adjustment, and what its new ICR will be after the adjustment
vars.oldICR = LiquityMath._computeCR(vars.coll, vars.debt, vars.price);
vars.newICR = _getNewICRFromTroveChange(vars.coll, vars.debt, vars.collChange, vars.isCollIncrease, vars.netDebtChange, _isDebtIncrease, vars.price);
assert(_collWithdrawal <= vars.coll);
// Check the adjustment satisfies all conditions for the current system mode
_requireValidAdjustmentInCurrentMode(isRecoveryMode, _collWithdrawal, _isDebtIncrease, vars);
// When the adjustment is a debt repayment, check it's a valid amount and that the caller has enough THUSD
if (!_isDebtIncrease && _THUSDChange > 0) {
_requireAtLeastMinNetDebt(_getNetDebt(vars.debt) - vars.netDebtChange);
_requireValidTHUSDRepayment(vars.debt, vars.netDebtChange);
_requireSufficientTHUSDBalance(contractsCache.thusdToken, _borrower, vars.netDebtChange);
}
(vars.newColl, vars.newDebt) = _updateTroveFromAdjustment(contractsCache.troveManager, _borrower, vars.collChange, vars.isCollIncrease, vars.netDebtChange, _isDebtIncrease);
vars.stake = contractsCache.troveManager.updateStakeAndTotalStakes(_borrower);
// Re-insert trove in to the sorted list
uint256 newNICR = _getNewNominalICRFromTroveChange(vars.coll, vars.debt, vars.collChange, vars.isCollIncrease, vars.netDebtChange, _isDebtIncrease);
sortedTroves.reInsert(_borrower, newNICR, _upperHint, _lowerHint);
emit TroveUpdated(_borrower, vars.newDebt, vars.newColl, vars.stake, BorrowerOperation.adjustTrove);
emit THUSDBorrowingFeePaid(msg.sender, vars.THUSDFee);
// Use the unmodified _THUSDChange here, as we don't send the fee to the user
_moveTokensAndCollateralfromAdjustment(
contractsCache.activePool,
contractsCache.thusdToken,
msg.sender,
vars.collChange,
vars.isCollIncrease,
_THUSDChange,
_isDebtIncrease,
vars.netDebtChange
);
}
function closeTrove() external override {
ITroveManager troveManagerCached = troveManager;
IActivePool activePoolCached = activePool;
ITHUSDToken thusdTokenCached = thusdToken;
bool canMint = thusdTokenCached.mintList(address(this));
_requireTroveisActive(troveManagerCached, msg.sender);
uint256 price = priceFeed.fetchPrice();
if (canMint) {
_requireNotInRecoveryMode(price);
}
troveManagerCached.applyPendingRewards(msg.sender);
uint256 coll = troveManagerCached.getTroveColl(msg.sender);
uint256 debt = troveManagerCached.getTroveDebt(msg.sender);
_requireSufficientTHUSDBalance(thusdTokenCached, msg.sender, debt - THUSD_GAS_COMPENSATION);
if (canMint) {
uint256 newTCR = _getNewTCRFromTroveChange(coll, false, debt, false, price);
_requireNewTCRisAboveCCR(newTCR);
}
troveManagerCached.removeStake(msg.sender);
troveManagerCached.closeTrove(msg.sender);
emit TroveUpdated(msg.sender, 0, 0, 0, BorrowerOperation.closeTrove);
// Burn the repaid THUSD from the user's balance and the gas compensation from the Gas Pool
_repayTHUSD(activePoolCached, thusdTokenCached, msg.sender, debt - THUSD_GAS_COMPENSATION);
_repayTHUSD(activePoolCached, thusdTokenCached, gasPoolAddress, THUSD_GAS_COMPENSATION);
// Send the collateral back to the user
activePoolCached.sendCollateral(msg.sender, coll);
}
/**
* Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode
*/
function claimCollateral() external override {
// send collateral from CollSurplus Pool to owner
collSurplusPool.claimColl(msg.sender);
}
// --- Helper functions ---
function _triggerBorrowingFee(ITroveManager _troveManager, ITHUSDToken _thusdToken, uint256 _THUSDAmount, uint256 _maxFeePercentage) internal returns (uint) {
_troveManager.decayBaseRateFromBorrowing(); // decay the baseRate state variable
uint256 THUSDFee = _troveManager.getBorrowingFee(_THUSDAmount);
_requireUserAcceptsFee(THUSDFee, _THUSDAmount, _maxFeePercentage);
// Send fee to PCV contract
_thusdToken.mint(pcvAddress, THUSDFee);
return THUSDFee;
}
function _getUSDValue(uint256 _coll, uint256 _price) internal pure returns (uint) {
uint256 usdValue = _price * _coll / DECIMAL_PRECISION;
return usdValue;
}
function _getCollChange(
uint256 _collReceived,
uint256 _requestedCollWithdrawal
)
internal
pure
returns(uint256 collChange, bool isCollIncrease)
{
if (_collReceived != 0) {
collChange = _collReceived;
isCollIncrease = true;
} else {
collChange = _requestedCollWithdrawal;
}
}
// Update trove's coll and debt based on whether they increase or decrease
function _updateTroveFromAdjustment
(
ITroveManager _troveManager,
address _borrower,
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool _isDebtIncrease
)
internal
returns (uint, uint)
{
uint256 newColl = (_isCollIncrease) ? _troveManager.increaseTroveColl(_borrower, _collChange)
: _troveManager.decreaseTroveColl(_borrower, _collChange);
uint256 newDebt = (_isDebtIncrease) ? _troveManager.increaseTroveDebt(_borrower, _debtChange)
: _troveManager.decreaseTroveDebt(_borrower, _debtChange);
return (newColl, newDebt);
}
function _moveTokensAndCollateralfromAdjustment
(
IActivePool _activePool,
ITHUSDToken _thusdToken,
address _borrower,
uint256 _collChange,
bool _isCollIncrease,
uint256 _THUSDChange,
bool _isDebtIncrease,
uint256 _netDebtChange
)
internal
{
if (_isDebtIncrease) {
_withdrawTHUSD(_activePool, _thusdToken, _borrower, _THUSDChange, _netDebtChange);
} else {
_repayTHUSD(_activePool, _thusdToken, _borrower, _THUSDChange);
}
if (_isCollIncrease) {
_activePoolAddColl(_activePool, _collChange);
} else {
_activePool.sendCollateral(_borrower, _collChange);
}
}
// Send collateral to Active Pool and increase its recorded collateral balance
function _activePoolAddColl(IActivePool _activePool, uint256 _amount) internal {
sendCollateralFrom(IERC20(collateralAddress), msg.sender, address(_activePool), _amount);
if (collateralAddress == address(0)) {
return;
}
_activePool.updateCollateralBalance(_amount);
}
// Issue the specified amount of THUSD to _account and increases the total active debt (_netDebtIncrease potentially includes a THUSDFee)
function _withdrawTHUSD(IActivePool _activePool, ITHUSDToken _thusdToken, address _account, uint256 _THUSDAmount, uint256 _netDebtIncrease) internal {
_activePool.increaseTHUSDDebt(_netDebtIncrease);
_thusdToken.mint(_account, _THUSDAmount);
}
// Burn the specified amount of THUSD from _account and decreases the total active debt
function _repayTHUSD(IActivePool _activePool, ITHUSDToken _thusdToken, address _account, uint256 _THUSD) internal {
_activePool.decreaseTHUSDDebt(_THUSD);
_thusdToken.burn(_account, _THUSD);
}
// --- 'Require' wrapper functions ---
function _requireSingularCollChange(uint256 _collWithdrawal, uint256 _assetAmount) internal pure {
require(_assetAmount == 0 || _collWithdrawal == 0, "BorrowerOperations: Cannot withdraw and add coll");
}
function _requireCallerIsBorrower(address _borrower) internal view {
require(msg.sender == _borrower, "BorrowerOps: Caller must be the borrower for a withdrawal");
}
function _requireNonZeroAdjustment(uint256 _collWithdrawal, uint256 _THUSDChange, uint256 _assetAmount) internal pure {
require(_assetAmount != 0 || _collWithdrawal != 0 || _THUSDChange != 0, "BorrowerOps: There must be either a collateral change or a debt change");
}
function _requireTroveisActive(ITroveManager _troveManager, address _borrower) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_borrower);
require(status == ITroveManager.Status.active, "BorrowerOps: Trove does not exist or is closed");
}
function _requireTroveisNotActive(ITroveManager _troveManager, address _borrower) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_borrower);
require(status != ITroveManager.Status.active, "BorrowerOps: Trove is active");
}
function _requireNonZeroDebtChange(uint256 _THUSDChange) internal pure {
require(_THUSDChange > 0, "BorrowerOps: Debt increase requires non-zero debtChange");
}
function _requireNotInRecoveryMode(uint256 _price) internal view {
require(!_checkRecoveryMode(_price), "BorrowerOps: Operation not permitted during Recovery Mode");
}
function _requireNoCollWithdrawal(uint256 _collWithdrawal) internal pure {
require(_collWithdrawal == 0, "BorrowerOps: Collateral withdrawal not permitted Recovery Mode");
}
function _requireValidAdjustmentInCurrentMode
(
bool _isRecoveryMode,
uint256 _collWithdrawal,
bool _isDebtIncrease,
LocalVariables_adjustTrove memory _vars
)
internal
view
{
/*
*In Recovery Mode, only allow:
*
* - Pure collateral top-up
* - Pure debt repayment
* - Collateral top-up with debt repayment
* - A debt increase combined with a collateral top-up which makes the ICR >= 150% and improves the ICR (and by extension improves the TCR).
*
* In Normal Mode, ensure:
*
* - The new ICR is above MCR
* - The adjustment won't pull the TCR below CCR
*/
if (_isRecoveryMode) {
_requireNoCollWithdrawal(_collWithdrawal);
if (_isDebtIncrease) {
_requireICRisAboveCCR(_vars.newICR);
_requireNewICRisAboveOldICR(_vars.newICR, _vars.oldICR);
}
} else { // if Normal Mode
_requireICRisAboveMCR(_vars.newICR);
_vars.newTCR = _getNewTCRFromTroveChange(_vars.collChange, _vars.isCollIncrease, _vars.netDebtChange, _isDebtIncrease, _vars.price);
_requireNewTCRisAboveCCR(_vars.newTCR);
}
}
function _requireICRisAboveMCR(uint256 _newICR) internal pure {
require(_newICR >= MCR, "BorrowerOps: An operation that would result in ICR < MCR is not permitted");
}
function _requireICRisAboveCCR(uint256 _newICR) internal pure {
require(_newICR >= CCR, "BorrowerOps: Operation must leave trove with ICR >= CCR");
}
function _requireNewICRisAboveOldICR(uint256 _newICR, uint256 _oldICR) internal pure {
require(_newICR >= _oldICR, "BorrowerOps: Cannot decrease your Trove's ICR in Recovery Mode");
}
function _requireNewTCRisAboveCCR(uint256 _newTCR) internal pure {
require(_newTCR >= CCR, "BorrowerOps: An operation that would result in TCR < CCR is not permitted");
}
function _requireAtLeastMinNetDebt(uint256 _netDebt) internal pure {
require (_netDebt >= MIN_NET_DEBT, "BorrowerOps: Trove's net debt must be greater than minimum");
}
function _requireValidTHUSDRepayment(uint256 _currentDebt, uint256 _debtRepayment) internal pure {
require(_debtRepayment <= _currentDebt - THUSD_GAS_COMPENSATION, "BorrowerOps: Amount repaid must not be larger than the Trove's debt");
}
function _requireCallerIsStabilityPool() internal view {
require(msg.sender == stabilityPoolAddress, "BorrowerOps: Caller is not Stability Pool");
}
function _requireSufficientTHUSDBalance(ITHUSDToken _thusdToken, address _borrower, uint256 _debtRepayment) internal view {
require(_thusdToken.balanceOf(_borrower) >= _debtRepayment, "BorrowerOps: Caller doesnt have enough THUSD to make repayment");
}
function _requireValidMaxFeePercentage(uint256 _maxFeePercentage, bool _isRecoveryMode) internal pure {
if (_isRecoveryMode) {
require(_maxFeePercentage <= DECIMAL_PRECISION,
"Max fee percentage must less than or equal to 100%");
} else {
require(_maxFeePercentage >= BORROWING_FEE_FLOOR && _maxFeePercentage <= DECIMAL_PRECISION,
"Max fee percentage must be between 0.5% and 100%");
}
}
// --- ICR and TCR getters ---
// Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
function _getNewNominalICRFromTroveChange
(
uint256 _coll,
uint256 _debt,
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool _isDebtIncrease
)
pure
internal
returns (uint)
{
(uint256 newColl, uint256 newDebt) = _getNewTroveAmounts(_coll, _debt, _collChange, _isCollIncrease, _debtChange, _isDebtIncrease);
uint256 newNICR = LiquityMath._computeNominalCR(newColl, newDebt);
return newNICR;
}
// Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
function _getNewICRFromTroveChange
(
uint256 _coll,
uint256 _debt,
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool _isDebtIncrease,
uint256 _price
)
pure
internal
returns (uint)
{
(uint256 newColl, uint256 newDebt) = _getNewTroveAmounts(_coll, _debt, _collChange, _isCollIncrease, _debtChange, _isDebtIncrease);
uint256 newICR = LiquityMath._computeCR(newColl, newDebt, _price);
return newICR;
}
function _getNewTroveAmounts(
uint256 _coll,
uint256 _debt,
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool _isDebtIncrease
)
internal
pure
returns (uint, uint)
{
uint256 newColl = _coll;
uint256 newDebt = _debt;
newColl = _isCollIncrease ? _coll + _collChange : _coll - _collChange;
newDebt = _isDebtIncrease ? _debt + _debtChange : _debt - _debtChange;
return (newColl, newDebt);
}
function _getNewTCRFromTroveChange
(
uint256 _collChange,
bool _isCollIncrease,
uint256 _debtChange,
bool _isDebtIncrease,
uint256 _price
)
internal
view
returns (uint)
{
uint256 totalColl = getEntireSystemColl();
uint256 totalDebt = getEntireSystemDebt();
totalColl = _isCollIncrease ? totalColl + _collChange : totalColl - _collChange;
totalDebt = _isDebtIncrease ? totalDebt + _debtChange : totalDebt - _debtChange;
uint256 newTCR = LiquityMath._computeCR(totalColl, totalDebt, _price);
return newTCR;
}
function getCompositeDebt(uint256 _debt) external pure override returns (uint) {
return _getCompositeDebt(_debt);
}
}
Ownable.sol 85 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* Based on OpenZeppelin's Ownable contract:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
*
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () {
_owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*
* NOTE: This function is not safe, as it doesn’t check owner is calling it.
* Make sure you check it before calling it.
*/
function _renounceOwnership() internal {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
BaseMath.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract BaseMath {
uint256 constant public DECIMAL_PRECISION = 1e18;
}
IERC2612.sol 58 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @dev Interface of the ERC2612 standard as defined in the EIP.
*
* Adds the {permit} method, which can be used to change one's
* {IERC20-allowance} without having to send a transaction, by signing a
* message. This allows users to spend tokens without having to hold Ether.
*
* See https://eips.ethereum.org/EIPS/eip-2612.
*
* Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
*/
interface IERC2612 {
/**
* @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(address owner, address spender, uint256 amount,
uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/**
* @dev Returns the current ERC2612 nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases `owner`'s nonce by one. This
* prevents a signature from being used multiple times.
*
* `owner` can limit the time a Permit is valid for by setting `deadline` to
* a value in the near future. The deadline argument can be set to uint(-1) to
* create Permits that effectively never expire.
*/
function nonces(address owner) external view returns (uint256);
function version() external view returns (string memory);
function permitTypeHash() external view returns (bytes32);
function domainSeparator() external view returns (bytes32);
}
IPriceFeed.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IPriceFeed {
// --- Events ---
event LastGoodPriceUpdated(uint256 _lastGoodPrice);
// --- Function ---
function fetchPrice() external returns (uint);
}
IActivePool.sol 21 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./IPool.sol";
interface IActivePool is IPool {
// --- Events ---
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolTHUSDDebtUpdated(uint256 _THUSDDebt);
event ActivePoolCollateralBalanceUpdated(uint256 _collateral);
event CollateralAddressChanged(address _newCollateralAddress);
event CollSurplusPoolAddressChanged(address _newCollSurplusPoolAddress);
// --- Functions ---
function sendCollateral(address _account, uint256 _amount) external;
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
ITHUSDToken.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../Dependencies/IERC2612.sol";
interface ITHUSDToken is IERC20Metadata, IERC2612 {
// --- Events ---
event TroveManagerAddressAdded(address _troveManagerAddress);
event StabilityPoolAddressAdded(address _newStabilityPoolAddress);
event BorrowerOperationsAddressAdded(address _newBorrowerOperationsAddress);
event THUSDTokenBalanceUpdated(address _user, uint256 _amount);
// --- Functions ---
function mintList(address contractAddress) external view returns (bool);
function burnList(address contractAddress) external view returns (bool);
function mint(address _account, uint256 _amount) external;
function burn(address _account, uint256 _amount) external;
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
}
IDefaultPool.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./IPool.sol";
interface IDefaultPool is IPool {
// --- Events ---
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event DefaultPoolTHUSDDebtUpdated(uint256 _THUSDDebt);
event DefaultPoolCollateralBalanceUpdated(uint256 _collateral);
event CollateralAddressChanged(address _newCollateralAddress);
// --- Functions ---
function sendCollateralToActivePool(uint256 _amount) external;
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
ILiquityBase.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./IPriceFeed.sol";
interface ILiquityBase {
function priceFeed() external view returns (IPriceFeed);
}
LiquityBase.sol 90 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./BaseMath.sol";
import "./LiquityMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/IPriceFeed.sol";
import "../Interfaces/ILiquityBase.sol";
/*
* Base contract for TroveManager, BorrowerOperations and StabilityPool. Contains global system constants and
* common functions.
*/
contract LiquityBase is BaseMath, ILiquityBase {
uint256 constant public _100pct = 1e18; // 1e18 == 100%
// Minimum collateral ratio for individual troves
uint256 constant public MCR = 1.1e18; // 110%
// Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered.
uint256 constant public CCR = 1.5e18; // 150%
// Amount of THUSD to be locked in gas pool on opening troves
uint256 constant public THUSD_GAS_COMPENSATION = 200e18;
// Minimum amount of net THUSD debt a trove must have
uint256 constant public MIN_NET_DEBT = 1800e18;
// uint256 constant public MIN_NET_DEBT = 0;
uint256 constant public PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5%
uint256 constant public BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%
IActivePool public activePool;
IDefaultPool public defaultPool;
IPriceFeed public override priceFeed;
// --- Gas compensation functions ---
// Returns the composite debt (drawn debt + gas compensation) of a trove, for the purpose of ICR calculation
function _getCompositeDebt(uint256 _debt) internal pure returns (uint) {
return _debt + THUSD_GAS_COMPENSATION;
}
function _getNetDebt(uint256 _debt) internal pure returns (uint) {
return _debt - THUSD_GAS_COMPENSATION;
}
// Return the amount of collateral to be drawn from a trove's collateral and sent as gas compensation.
function _getCollGasCompensation(uint256 _entireColl) internal pure returns (uint) {
return _entireColl / PERCENT_DIVISOR;
}
function getEntireSystemColl() public view returns (uint256 entireSystemColl) {
uint256 activeColl = activePool.getCollateralBalance();
uint256 liquidatedColl = defaultPool.getCollateralBalance();
return activeColl + liquidatedColl;
}
function getEntireSystemDebt() public view returns (uint256 entireSystemDebt) {
uint256 activeDebt = activePool.getTHUSDDebt();
uint256 closedDebt = defaultPool.getTHUSDDebt();
return activeDebt + closedDebt;
}
function _getTCR(uint256 _price) internal view returns (uint256 TCR) {
uint256 entireSystemColl = getEntireSystemColl();
uint256 entireSystemDebt = getEntireSystemDebt();
TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt, _price);
return TCR;
}
function _checkRecoveryMode(uint256 _price) internal view returns (bool) {
uint256 TCR = _getTCR(_price);
return TCR < CCR;
}
function _requireUserAcceptsFee(uint256 _fee, uint256 _amount, uint256 _maxFeePercentage) internal pure {
uint256 feePercentage = _fee * DECIMAL_PRECISION / _amount;
require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum");
}
}
LiquityMath.sol 108 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
library LiquityMath {
uint256 internal constant DECIMAL_PRECISION = 1e18;
/* Precision for Nominal ICR (independent of price). Rationale for the value:
*
* - Making it “too high” could lead to overflows.
* - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.
*
* This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH,
* and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.
*
*/
uint256 internal constant NICR_PRECISION = 1e20;
function _min(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a < _b) ? _a : _b;
}
function _max(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a : _b;
}
/*
* Multiply two decimal numbers and use normal rounding rules:
* -round product up if 19'th mantissa digit >= 5
* -round product down if 19'th mantissa digit < 5
*
* Used only inside the exponentiation, _decPow().
*/
function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
uint256 prod_xy = x * y;
decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;
}
/*
* _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
*
* Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity.
*
* Called by one function that represent time in units of minutes:
* 1) TroveManager._calcDecayedBaseRate
*
* The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
* "minutes in 1000 years": 60 * 24 * 365 * 1000
*
* If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
* negligibly different from just passing the cap, since:
*
* In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
* In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
*/
function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint) {
if (_minutes > 525600000) {_minutes = 525600000;} // cap to avoid overflow
if (_minutes == 0) {return DECIMAL_PRECISION;}
uint256 y = DECIMAL_PRECISION;
uint256 x = _base;
uint256 n = _minutes;
// Exponentiation-by-squaring
while (n > 1) {
if (n % 2 == 0) {
x = decMul(x, x);
n = n / 2;
} else { // if (n % 2 != 0)
y = decMul(x, y);
x = decMul(x, x);
n = (n - 1) / 2;
}
}
return decMul(x, y);
}
function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a - _b : _b - _a;
}
function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint) {
if (_debt > 0) {
return _coll * NICR_PRECISION / _debt;
}
// Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
else { // if (_debt == 0)
return type(uint256).max;
}
}
function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint) {
if (_debt > 0) {
uint256 newCollRatio = _coll * _price / _debt;
return newCollRatio;
}
// Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
else { // if (_debt == 0)
return type(uint256).max;
}
}
}
ISortedTroves.sol 46 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Common interface for the SortedTroves Doubly Linked List.
interface ISortedTroves {
// --- Events ---
event SortedTrovesAddressChanged(address _sortedDoublyLLAddress);
event BorrowerOperationsAddressChanged(address _borrowerOperationsAddress);
event NodeAdded(address _id, uint256 _NICR);
event NodeRemoved(address _id);
// --- Functions ---
function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress) external;
function insert(address _id, uint256 _ICR, address _prevId, address _nextId) external;
function remove(address _id) external;
function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) external;
function contains(address _id) external view returns (bool);
function isFull() external view returns (bool);
function isEmpty() external view returns (bool);
function getSize() external view returns (uint256);
function getMaxSize() external view returns (uint256);
function getFirst() external view returns (address);
function getLast() external view returns (address);
function getNext(address _id) external view returns (address);
function getPrev(address _id) external view returns (address);
function validInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (bool);
function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address);
}
ITroveManager.sol 148 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./ILiquityBase.sol";
import "./IStabilityPool.sol";
import "./ITHUSDToken.sol";
import "./IPCV.sol";
// Common interface for the Trove Manager.
interface ITroveManager is ILiquityBase {
enum Status {
nonExistent,
active,
closedByOwner,
closedByLiquidation,
closedByRedemption
}
// --- Events ---
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
event ActivePoolAddressChanged(address _activePoolAddress);
event DefaultPoolAddressChanged(address _defaultPoolAddress);
event StabilityPoolAddressChanged(address _stabilityPoolAddress);
event GasPoolAddressChanged(address _gasPoolAddress);
event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
event SortedTrovesAddressChanged(address _sortedTrovesAddress);
event PCVAddressChanged(address _pcvAddress);
event Liquidation(uint256 _liquidatedDebt, uint256 _liquidatedColl, uint256 _collGasCompensation, uint256 _THUSDGasCompensation);
event Redemption(uint256 _attemptedTHUSDAmount, uint256 _actualTHUSDAmount, uint256 _collateralSent, uint256 _collateralFee);
event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
event TroveLiquidated(address indexed _borrower, uint256 _debt, uint256 _coll, uint8 operation);
event BaseRateUpdated(uint256 _baseRate);
event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);
event TotalStakesUpdated(uint256 _newTotalStakes);
event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);
event LTermsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
event TroveSnapshotsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
event TroveIndexUpdated(address _borrower, uint256 _newIndex);
// --- Functions ---
function setAddresses(
address _borrowerOperationsAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
address _gasPoolAddress,
address _collSurplusPoolAddress,
address _priceFeedAddress,
address _thusdTokenAddress,
address _sortedTrovesAddress,
address _pcvAddress
) external;
function stabilityPool() external view returns (IStabilityPool);
function thusdToken() external view returns (ITHUSDToken);
function pcv() external view returns (IPCV);
function getTroveOwnersCount() external view returns (uint);
function getTroveFromTroveOwnersArray(uint256 _index) external view returns (address);
function getNominalICR(address _borrower) external view returns (uint);
function getCurrentICR(address _borrower, uint256 _price) external view returns (uint);
function liquidate(address _borrower) external;
function liquidateTroves(uint256 _n) external;
function batchLiquidateTroves(address[] calldata _troveArray) external;
function redeemCollateral(
uint256 _THUSDAmount,
address _firstRedemptionHint,
address _upperPartialRedemptionHint,
address _lowerPartialRedemptionHint,
uint256 _partialRedemptionHintNICR,
uint256 _maxIterations,
uint256 _maxFee
) external;
function updateStakeAndTotalStakes(address _borrower) external returns (uint);
function updateTroveRewardSnapshots(address _borrower) external;
function addTroveOwnerToArray(address _borrower) external returns (uint256 index);
function applyPendingRewards(address _borrower) external;
function getPendingCollateralReward(address _borrower) external view returns (uint);
function getPendingTHUSDDebtReward(address _borrower) external view returns (uint);
function hasPendingRewards(address _borrower) external view returns (bool);
function getEntireDebtAndColl(address _borrower) external view returns (
uint256 debt,
uint256 coll,
uint256 pendingTHUSDDebtReward,
uint256 pendingCollateralReward
);
function closeTrove(address _borrower) external;
function removeStake(address _borrower) external;
function getRedemptionRate() external view returns (uint);
function getRedemptionRateWithDecay() external view returns (uint);
function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint);
function getBorrowingRate() external view returns (uint);
function getBorrowingRateWithDecay() external view returns (uint);
function getBorrowingFee(uint256 THUSDDebt) external view returns (uint);
function getBorrowingFeeWithDecay(uint256 _THUSDDebt) external view returns (uint);
function decayBaseRateFromBorrowing() external;
function getTroveStatus(address _borrower) external view returns (Status);
function getTroveStake(address _borrower) external view returns (uint);
function getTroveDebt(address _borrower) external view returns (uint);
function getTroveColl(address _borrower) external view returns (uint);
function setTroveStatus(address _borrower, Status _status) external;
function increaseTroveColl(address _borrower, uint256 _collIncrease) external returns (uint);
function decreaseTroveColl(address _borrower, uint256 _collDecrease) external returns (uint);
function increaseTroveDebt(address _borrower, uint256 _debtIncrease) external returns (uint);
function decreaseTroveDebt(address _borrower, uint256 _collDecrease) external returns (uint);
function getTCR(uint256 _price) external view returns (uint);
function checkRecoveryMode(uint256 _price) external view returns (bool);
}
IStabilityPool.sol 142 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/*
* The Stability Pool holds THUSD tokens deposited by Stability Pool depositors.
*
* When a trove is liquidated, then depending on system conditions, some of its THUSD debt gets offset with
* THUSD in the Stability Pool: that is, the offset debt evaporates, and an equal amount of THUSD tokens in the Stability Pool is burned.
*
* Thus, a liquidation causes each depositor to receive a THUSD loss, in proportion to their deposit as a share of total deposits.
* They also receive an collateral gain, as the collateral of the liquidated trove is distributed among Stability depositors,
* in the same proportion.
*
* When a liquidation occurs, it depletes every deposit by the same fraction: for example, a liquidation that depletes 40%
* of the total THUSD in the Stability Pool, depletes 40% of each deposit.
*
* A deposit that has experienced a series of liquidations is termed a "compounded deposit": each liquidation depletes the deposit,
* multiplying it by some factor in range ]0,1[
*
* Please see the implementation spec in the proof document, which closely follows on from the compounded deposit / collateral gain derivations:
* https://github.com/liquity/liquity/blob/master/papers/Scalable_Reward_Distribution_with_Compounding_Stakes.pdf
*
*/
interface IStabilityPool {
// --- Events ---
event StabilityPoolCollateralBalanceUpdated(uint256 _newBalance);
event StabilityPoolTHUSDBalanceUpdated(uint256 _newBalance);
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _newActivePoolAddress);
event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
event SortedTrovesAddressChanged(address _newSortedTrovesAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event CollateralAddressChanged(address _newCollateralAddress);
event P_Updated(uint256 _P);
event S_Updated(uint256 _S, uint128 _epoch, uint128 _scale);
event EpochUpdated(uint128 _currentEpoch);
event ScaleUpdated(uint128 _currentScale);
event DepositSnapshotUpdated(address indexed _depositor, uint256 _P, uint256 _S);
event UserDepositChanged(address indexed _depositor, uint256 _newDeposit);
event CollateralGainWithdrawn(address indexed _depositor, uint256 _collateral, uint256 _THUSDLoss);
event CollateralSent(address _to, uint256 _amount);
// --- Functions ---
/*
* Called only once on init, to set addresses of other Liquity contracts
* Callable only by owner, renounces ownership at the end
*/
function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _activePoolAddress,
address _thusdTokenAddress,
address _sortedTrovesAddress,
address _priceFeedAddress,
address _collateralAddress
) external;
/*
* Initial checks:
* - _amount is not zero
* ---
* - Sends depositor's accumulated gains (collateral) to depositor
*/
function provideToSP(uint256 _amount) external;
/*
* Initial checks:
* - _amount is zero or there are no under collateralized troves left in the system
* - User has a non zero deposit
* ---
* - Sends all depositor's accumulated gains (collateral) to depositor
* - Decreases deposit stake, and takes new snapshot.
*
* If _amount > userDeposit, the user withdraws all of their compounded deposit.
*/
function withdrawFromSP(uint256 _amount) external;
/*
* Initial checks:
* - User has a non zero deposit
* - User has an open trove
* - User has some collateral gain
* ---
* - Transfers the depositor's entire collateral gain from the Stability Pool to the caller's trove
* - Leaves their compounded deposit in the Stability Pool
* - Updates snapshots for deposit
*/
function withdrawCollateralGainToTrove(address _upperHint, address _lowerHint) external;
/*
* Initial checks:
* - Caller is TroveManager
* ---
* Cancels out the specified debt against the THUSD contained in the Stability Pool (as far as possible)
* and transfers the Trove's collateral from ActivePool to StabilityPool.
* Only called by liquidation functions in the TroveManager.
*/
function offset(uint256 _debt, uint256 _coll) external;
/*
* Returns the total amount of collateral held by the pool, accounted in an internal variable instead of `balance`,
* to exclude edge cases like collateral received from a self-destruct.
*/
function getCollateralBalance() external view returns (uint);
/*
* Returns THUSD held in the pool. Changes when users deposit/withdraw, and when Trove debt is offset.
*/
function getTotalTHUSDDeposits() external view returns (uint);
/*
* Calculates the collateral gain earned by the deposit since its last snapshots were taken.
*/
function getDepositorCollateralGain(address _depositor) external view returns (uint);
/*
* Return the user's compounded deposit.
*/
function getCompoundedTHUSDDeposit(address _depositor) external view returns (uint);
/*
* Only callable by Active Pool, updates ERC20 tokens recieved
*/
function updateCollateralBalance(uint256 _amount) external;
/*
* Fallback function
* Only callable by Active Pool, it just accounts for ETH received
* receive() external payable;
*/
function collateralAddress() external view returns(address);
}
CheckContract.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract CheckContract {
/**
* Check that the account is an already deployed non-destroyed contract.
* See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12
*/
function checkContract(address _account) internal view {
require(_account != address(0), "Account cannot be zero address");
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(_account) }
require(size > 0, "Account code size cannot be zero");
}
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
SendCollateral.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract SendCollateral {
using SafeERC20 for IERC20;
/**
* Sends collateral to recipient
*/
function sendCollateral(IERC20 _collateralERC20, address _recipient, uint256 _amount) internal {
if (address(_collateralERC20) == address(0)) {
// ETH
// require(_amount <= address(this).balance, "Not enough ETH");
(bool success, ) = _recipient.call{ value: _amount }(""); // re-entry is fine here
require(success, "Sending ETH failed");
} else {
// ERC20
// require(_amount <= _collateralERC20.balanceOf(address(this)), "Not enough collateral");
_collateralERC20.safeTransfer(_recipient, _amount);
}
}
/**
* Sends collateral to recipient
*/
function sendCollateralFrom(IERC20 _collateralERC20, address _from, address _recipient, uint256 _amount) internal {
if (address(_collateralERC20) == address(0)) {
// ETH
// require(_amount <= address(this).balance, "Not enough ETH");
(bool success, ) = _recipient.call{ value: _amount }(""); // re-entry is fine here
require(success, "Sending ETH failed");
} else {
// ERC20
// require(_amount <= _collateralERC20.balanceOf(address(this)), "Not enough collateral");
_collateralERC20.safeTransferFrom(_from, _recipient, _amount);
}
}
}
ICollSurplusPool.sol 38 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface ICollSurplusPool {
// --- Events ---
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _newActivePoolAddress);
event CollateralAddressChanged(address _newCollateralAddress);
event CollBalanceUpdated(address indexed _account, uint256 _newBalance);
event CollateralSent(address _to, uint256 _amount);
// --- Contract setters ---
function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _activePoolAddress,
address _collateralAddress
) external;
function getCollateralBalance() external view returns (uint);
function getCollateral(address _account) external view returns (uint);
function accountSurplus(address _account, uint256 _amount) external;
function claimColl(address _account) external;
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
IBorrowerOperations.sol 64 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Common interface for the Trove Manager.
interface IBorrowerOperations {
// --- Events ---
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _activePoolAddress);
event DefaultPoolAddressChanged(address _defaultPoolAddress);
event StabilityPoolAddressChanged(address _stabilityPoolAddress);
event GasPoolAddressChanged(address _gasPoolAddress);
event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event SortedTrovesAddressChanged(address _sortedTrovesAddress);
event THUSDTokenAddressChanged(address _thusdTokenAddress);
event PCVAddressChanged(address _pcvAddress);
event CollateralAddressChanged(address _newCollateralAddress);
event TroveCreated(address indexed _borrower, uint256 arrayIndex);
event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
event THUSDBorrowingFeePaid(address indexed _borrower, uint256 _THUSDFee);
// --- Functions ---
function setAddresses(
address _troveManagerAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
address _gasPoolAddress,
address _collSurplusPoolAddress,
address _priceFeedAddress,
address _sortedTrovesAddress,
address _thusdTokenAddress,
address _pcvAddress,
address _collateralAddress
) external;
function openTrove(uint256 _maxFee, uint256 _THUSDAmount, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function addColl(uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function moveCollateralGainToTrove(address _user, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function withdrawColl(uint256 _amount, address _upperHint, address _lowerHint) external;
function withdrawTHUSD(uint256 _maxFee, uint256 _amount, address _upperHint, address _lowerHint) external;
function repayTHUSD(uint256 _amount, address _upperHint, address _lowerHint) external;
function closeTrove() external;
function adjustTrove(uint256 _maxFee, uint256 _collWithdrawal, uint256 _debtChange, bool isDebtIncrease, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function claimCollateral() external;
function getCompositeDebt(uint256 _debt) external pure returns (uint);
function collateralAddress() external view returns(address);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
* 0 before setting it to a non-zero value.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
Read Contract
BORROWING_FEE_FLOOR 0xf92d3433 → uint256
CCR 0x5733d58f → uint256
DECIMAL_PRECISION 0xa20baee6 → uint256
MCR 0x794e5724 → uint256
MIN_NET_DEBT 0x1bf43555 → uint256
NAME 0xa3f4df7e → string
PERCENT_DIVISOR 0x4870dd9a → uint256
THUSD_GAS_COMPENSATION 0x1fd6a434 → uint256
_100pct 0x72fe25aa → uint256
activePool 0x7f7dde4a → address
collateralAddress 0x48d399e7 → address
defaultPool 0x3cc74225 → address
gasPoolAddress 0xfe9d0323 → address
getCompositeDebt 0x4ff81443 → uint256
getEntireSystemColl 0x887105d3 → uint256
getEntireSystemDebt 0x795d26c3 → uint256
isOwner 0x8f32d59b → bool
owner 0x8da5cb5b → address
pcvAddress 0x3db008c7 → address
priceFeed 0x741bef1a → address
sortedTroves 0xae918754 → address
stabilityPoolAddress 0x0b622ab2 → address
thusdToken 0x77e16f1e → address
troveManager 0x3d83908a → address
Write Contract 13 functions
These functions modify contract state and require a wallet transaction to execute.
addColl 0xd56d6bbe
uint256 _assetAmount
address _upperHint
address _lowerHint
adjustTrove 0x8e54c119
uint256 _maxFeePercentage
uint256 _collWithdrawal
uint256 _THUSDChange
bool _isDebtIncrease
uint256 _assetAmount
address _upperHint
address _lowerHint
burnDebtFromPCV 0xae9d445c
uint256 _thusdToBurn
claimCollateral 0x6f0b0c1c
No parameters
closeTrove 0x0e704d50
No parameters
mintBootstrapLoanFromPCV 0xb064b0b7
uint256 _thusdToMint
moveCollateralGainToTrove 0xabfad25d
address _borrower
uint256 _assetAmount
address _upperHint
address _lowerHint
openTrove 0x8f09162b
uint256 _maxFeePercentage
uint256 _THUSDAmount
uint256 _assetAmount
address _upperHint
address _lowerHint
repayTHUSD 0xbd59121e
uint256 _THUSDAmount
address _upperHint
address _lowerHint
setAddresses 0x7985c5e4
address _troveManagerAddress
address _activePoolAddress
address _defaultPoolAddress
address _stabilityPoolAddress
address _gasPoolAddress
address _collSurplusPoolAddress
address _priceFeedAddress
address _sortedTrovesAddress
address _thusdTokenAddress
address _pcvAddress
address _collateralAddress
transferOwnership 0xf2fde38b
address newOwner
withdrawColl 0x5530273c
uint256 _collWithdrawal
address _upperHint
address _lowerHint
withdrawTHUSD 0x877eb179
uint256 _maxFeePercentage
uint256 _THUSDAmount
address _upperHint
address _lowerHint
Recent Transactions
No transactions found for this address