Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x27D7D02AED6C4F95Ada2faf02DcCB9666D3abB8C
Balance 0 ETH
Nonce 1
Code Size 21601 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

21601 bytes
0x608060405234801561001057600080fd5b50600436106104145760003560e01c8063795d26c311610221578063bcd375261161012b578063d5b35635116100c3578063ee266b8711610087578063ee266b87146109c5578063f2fde38b146109ce578063f36b2425146109e1578063f92d343314610562578063fe2ba848146109e957600080fd5b8063d5b356351461095a578063d66a25531461096d578063d9a7244414610996578063e2ac77b0146109a9578063e6a91f16146109bc57600080fd5b8063bcd37526146108dc578063bf9befb1146108ef578063c52861f2146108f8578063c7b5548114610900578063cbd138ae1461090f578063d293c71014610922578063d380a37c14610935578063d38b05581461093e578063d3d6f8431461094757600080fd5b80639976cf45116101b9578063ae9187541161017d578063ae9187541461085d578063b0d8e18114610870578063b7f8cf9b14610883578063b82f263d14610896578063b91af97c146108a957600080fd5b80639976cf45146107f65780639ba7a40814610809578063a20baee614610720578063a3f4df7e1461081c578063a68b663e1461085457600080fd5b8063795d26c3146107775780637f7dde4a1461077f578063807d138d1461079257806382fe3eb91461079b578063887105d3146107ae5780638da5cb5b146107b65780638f32d59b146107c7578063953f0bb1146107da57806396d711ff146107ed57600080fd5b8063480cd57811610322578063653d46e7116102ba57806372fe25aa1161027e57806372fe25aa14610720578063741bef1a1461072f578063756b253e1461074257806377e16f1e14610755578063794e57241461076857600080fd5b8063653d46e71461068657806366ca4a21146106995780636c37a4af146106a15780636ef64338146106b457806372423c171461070d57600080fd5b8063480cd578146105be5780634870dd9a146105ea57806349eefeee146105f25780634a3c95c4146105fa5780634e443d9e1461060d5780635733d58f146106305780635dba4c4a1461063f578063631203b01461064757806364cee2601461065a57600080fd5b80631f68f20a116103b05780632b11551a116103745780632b11551a1461056a5780632f865568146105725780633cc74225146105855780634597f6ed14610598578063477d66cf146105ab57600080fd5b80631f68f20a146105055780631fd6a4341461050e57806321e378011461051e578063240926691461055a57806328d28b5b1461056257600080fd5b8063048c661d14610419578063071a7541146104425780630b0765571461045857806312610e921461046d57806315d549f1146104805780631673c79a1461049357806318f2817a146104cf5780631bf43555146104e25780631e8b1c2b146104f2575b600080fd5b60055461042c906001600160a01b031681565b6040516104399190614e03565b60405180910390f35b61044a600281565b604051908152602001610439565b61046b610466366004614e3c565b6109fc565b005b61044a61047b366004614e59565b610a24565b61044a61048e366004614e3c565b610a77565b6104ba6104a1366004614e3c565b6013602052600090815260409020805460019091015482565b60408051928352602083019190915201610439565b61044a6104dd366004614e3c565b610a99565b61044a686194049f30f720000081565b61046b610500366004614e9b565b610aac565b61044a600b5481565b61044a680ad78ebc5ac620000081565b61054d61052c366004614e3c565b6001600160a01b03166000908152600d602052604090206003015460ff1690565b6040516104399190614f8a565b61044a610e4d565b61044a610e6e565b61044a610e82565b61046b610580366004614e3c565b610e94565b60015461042c906001600160a01b031681565b60095461042c906001600160a01b031681565b61044a6105b9366004614f98565b610f00565b61044a6105cc366004614e3c565b6001600160a01b03166000908152600d602052604090206001015490565b61044a60c881565b60145461044a565b61044a610608366004614e3c565b610f13565b61062061061b366004614f98565b610fce565b6040519015158152602001610439565b61044a6714d1120d7b16000081565b61046b610fd9565b61044a610655366004614f98565b611045565b61044a610668366004614e3c565b6001600160a01b03166000908152600d602052604090206002015490565b61046b610694366004614f98565b611052565b61044a6113c8565b61046b6106af366004614fb1565b6113da565b6106fc6106c2366004614e3c565b600d6020526000908152604090208054600182015460028301546003909301549192909160ff81169061010090046001600160801b031685565b60405161043995949392919061507d565b61044a61071b366004614e59565b61172b565b61044a670de0b6b3a764000081565b60025461042c906001600160a01b031681565b61042c610750366004614f98565b611783565b60085461042c906001600160a01b031681565b61044a670f43fc2c04ee000081565b61044a6117ad565b60005461042c906001600160a01b031681565b61044a600f5481565b61046b6107a9366004614e3c565b6118a9565b61044a6118ba565b6003546001600160a01b031661042c565b6003546001600160a01b03163314610620565b61046b6107e83660046150ba565b61197f565b61044a60105481565b61044a610804366004614e59565b6119c8565b61044a610817366004614e3c565b6119f6565b6108476040518060400160405280600c81526020016b2a3937bb32a6b0b730b3b2b960a11b81525081565b60405161043991906150f7565b61044a60125481565b600a5461042c906001600160a01b031681565b61044a61087e366004614e3c565b611a22565b60045461042c906001600160a01b031681565b61044a6108a4366004614f98565b611a49565b6108bc6108b7366004614e3c565b611a54565b604080519485526020850193909352918301526060820152608001610439565b61046b6108ea366004615145565b611aac565b61044a600e5481565b61044a612148565b61044a670ddd4b8c6c7d70d881565b61046b61091d366004614e3c565b61215a565b61044a610930366004614e59565b61216d565b61044a600c5481565b61044a60155481565b61044a610955366004614e59565b61218c565b61044a610968366004614f98565b6121bd565b61044a61097b366004614e3c565b6001600160a01b03166000908152600d602052604090205490565b61042c6109a4366004614f98565b6121d0565b6106206109b7366004614e3c565b612200565b61044a60165481565b61044a60115481565b61046b6109dc366004614e3c565b612263565b61044a6122fb565b61046b6109f7366004614e3c565b612308565b610a04612319565b600054600154610a21916001600160a01b0390811691168361239b565b50565b6000610a2e612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906151c9565b6001600160a01b0385166000908152600d602052604090208190559150505b92915050565b6000610a81612319565b610a8a82612497565b6001600160801b031692915050565b6000610aa3612319565b610a7182612530565b8051600003610b215760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2043616c6c646174612061646472657373206160448201527572726179206d757374206e6f7420626520656d70747960501b60648201526084015b60405180910390fd5b6000546001546005546001600160a01b03928316929182169116610b43614d3e565b610b4b614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc491906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2f91906151dc565b60208301528151610c3f906125d5565b1580156040840152610c6757610c608585846000015185602001518a6125f2565b9050610c7f565b610c7c8585846000015185602001518a612827565b90505b6000816020015111610ca35760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad92610ce092600401918252602082015260400190565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d2685858360c001518460e001516128fc565b61010081015115610d9d57600754610100820151604051636250216960e01b81526001600160a01b0388811693636250216993610d6a939290911691600401615237565b600060405180830381600087803b158015610d8457600080fd5b505af1158015610d98573d6000803e3d6000fd5b505050505b610dab858260400151612b3b565b6020810151606083015261010081015160408201518251610dcc91906151c9565b610dd691906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a1610e45853383606001518460400151612c86565b505050505050565b610e606064670de0b6b3a7640000615266565b610e6b90600561527a565b81565b610e606103e8670de0b6b3a7640000615266565b6000610e8f600b54612d5d565b905090565b610e9d81612d9a565b604080516001808252818301909252600091602080830190803683370190505090508181600081518110610ed357610ed3615291565b60200260200101906001600160a01b031690816001600160a01b031681525050610efc81610aac565b5050565b6000610a71610f0d6113c8565b83612e31565b6001600160a01b0381166000908152601360205260408120546011548290610f3c9083906151c9565b9050801580610f7b575060016001600160a01b0385166000908152600d602052604090206003015460ff166004811115610f7857610f78614f60565b14155b15610f8a575060009392505050565b6001600160a01b0384166000908152600d602052604081206002015490670de0b6b3a7640000610fba848461527a565b610fc49190615266565b9695505050505050565b6000610a71826125d5565b610fe1612319565b6000610feb612e57565b9050670de0b6b3a7640000811115611005576110056152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610a21612e9b565b6000610a71610f0d6122fb565b6040805160e081018252600080546001600160a01b0390811683526001548116602084015292820181905260608201819052600a548316608083015260a0820181905260c08201526005549091166110a8614d3e565b6110b0614d6f565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611105573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112991906151dc565b826000018181525050826001600160a01b03166301081fda6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611170573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119491906151dc565b602083015281516111a4906125d5565b15801560408401526111cb576111c4848360000151846020015188612ef2565b90506111eb565b6111e884600001518560200151846000015185602001518961327e565b90505b600081602001511161120f5760405162461bcd60e51b8152600401610b18906151f5565b608081015160a082015160405163335525ad60e01b81526001600160a01b0386169263335525ad9261124c92600401918252602082015260400190565b600060405180830381600087803b15801561126657600080fd5b505af115801561127a573d6000803e3d6000fd5b5050505061129a846000015185602001518360c001518460e001516128fc565b61010081015115611311578351600754610100830151604051636250216960e01b81526001600160a01b03938416936362502169936112de93911691600401615237565b600060405180830381600087803b1580156112f857600080fd5b505af115801561130c573d6000803e3d6000fd5b505050505b61132384600001518260400151612b3b565b602081015160608301526101008101516040820151825161134491906151c9565b61134e91906151c9565b6080838101829052606080850151604085810151868401518251938452602084019690965290820152908101929092527f4152c73dd2614c4f9fc35e8c9cf16013cd588c75b49a4c1673ecffdcbcda9403910160405180910390a16113c184600001513383606001518460400151612c86565b5050505050565b6000610e8f6113d5612e57565b6133b4565b6003546001600160a01b031633146114045760405162461bcd60e51b8152600401610b18906152bd565b61140d8a613406565b61141689613406565b61141f88613406565b61142887613406565b61143186613406565b61143a85613406565b61144384613406565b61144c83613406565b61145582613406565b61145e81613406565b600480546001600160a01b03199081166001600160a01b038d8116919091179092556000805482168c84161790556001805482168b84161790556005805482168a8416179055600680548216898416179055600780548216888416179055600280548216878416179055600880548216868416179055600a80548216858416179055600980549091169183169190911790556040517f3ca631ffcd2a9b5d9ae18543fc82f58eb4ca33af9e6ab01b7a8e95331e6ed98590611520908c90614e03565b60405180910390a17f78f058b189175430c48dc02699e3a0031ea4ff781536dc2fab847de4babdd882896040516115579190614e03565b60405180910390a17f5ee0cae2f063ed938bb55046f6a932fb6ae792bf43624806bb90abe68a50be9b8860405161158e9190614e03565b60405180910390a17f82966d27eea39b038ee0fa30cd16532bb24f6e65d31cb58fb227aa5766cdcc7f876040516115c59190614e03565b60405180910390a17fcfb07d791fcafc032b35837b50eb84b74df518cf4cc287e8084f47630fa70fa0866040516115fc9190614e03565b60405180910390a17fe67f36a6e961157d6eff83b91f3af5a62131ceb6f04954ef74f51c1c05e7f88d856040516116339190614e03565b60405180910390a17f8c537274438aa850a330284665d81a85dd38267d09e4050d416bfc94142db2648460405161166a9190614e03565b60405180910390a17fe1e858a66c0bbbcdfa22d58dde1e5d42370be20cdb176e560287f85412e546e0836040516116a19190614e03565b60405180910390a17f65f4cf077bc01e4742eb5ad98326f6e95b63548ea24b17f8d5e823111fe78800826040516116d89190614e03565b60405180910390a17f38335c64466e2376ab931166337e19127650d842036ebe01da1ba3e5c1255ebb8160405161170f9190614e03565b60405180910390a161171f6134ab565b50505050505050505050565b6000611735612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906152f2565b6001600160a01b0385166000908152600d6020526040902060010181905591505092915050565b6014818154811061179357600080fd5b6000918252602090912001546001600160a01b0316905081565b600080546040805163f07424e960e01b8152905183926001600160a01b03169163f07424e99160048083019260209291908290030181865afa1580156117f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181b91906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b031663f07424e96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189691906151dc565b90506118a281836152f2565b9250505090565b6118b1612319565b610a21816134f5565b6000805460408051631529a63960e01b8152905183926001600160a01b031691631529a6399160048083019260209291908290030181865afa158015611904573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b611987612319565b6001600160a01b0382166000908152600d60205260409020600301805482919060ff191660018360048111156119bf576119bf614f60565b02179055505050565b60006119d2612319565b6001600160a01b0383166000908152600d6020526040812054610a529084906152f2565b6001600160a01b0381166000908152601360205260408120600101546012548290610f3c9083906151c9565b6000806000611a3084613559565b915091506000611a4083836135d5565b95945050505050565b6000610a718261360b565b6001600160a01b0381166000908152600d602052604081208054600190910154909180611a80856119f6565b9150611a8b85610f13565b9050611a9782856152f2565b9350611aa381846152f2565b92509193509193565b6040805160e08082018352600080546001600160a01b03908116845260015481166020808601919091526008548216858701526009548216606080870191909152600a548316608080880191909152600754841660a08089019190915260065490941660c080890191909152885161010081018a5286815293840186905297830185905290820184905281018390529081018290529384018190529083015290611b5583613637565b600260009054906101000a90046001600160a01b03166001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bce91906151dc565b60c08201819052611bde906136d2565b611be789613745565b611bf68260400151338b6137ac565b611bfe6117ad565b60e0820152888152608082015160c0820151600091611c1e918b906138a7565b15611c2a575087611d3c565b82608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c909190615305565b90505b6001600160a01b03811615801590611cbf5750670f43fc2c04ee0000611cbd828460c0015161216d565b105b15611d3c5782608001516001600160a01b031663b72703ac826040518263ffffffff1660e01b8152600401611cf49190614e03565b602060405180830381865afa158015611d11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d359190615305565b9050611c93565b84600003611d4a5760001994505b6001600160a01b03811615801590611d625750815115155b8015611d6e5750600085115b15611e815784611d7d81615322565b955050600083608001516001600160a01b031663b72703ac836040518263ffffffff1660e01b8152600401611db29190614e03565b602060405180830381865afa158015611dcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df39190615305565b9050611e08846000015185602001518461239b565b6000611e21858486600001518760c001518e8e8e6139f1565b9050806040015115611e34575050611e81565b8051602085018051611e479083906152f2565b9052506020810151604085018051611e609083906152f2565b905250805184518590611e749083906151c9565b905250909150611d4a9050565b6000826040015111611ee75760405162461bcd60e51b815260206004820152602960248201527f54726f76654d616e616765723a20556e61626c6520746f2072656465656d20616044820152681b9e48185b5bdd5b9d60ba1b6064820152608401610b18565b611efe82604001518360c001518460e00151613c7f565b50611f0c8260400151613d22565b606083018190526040830151611f23919086613d2f565b825160608085015190840151604051636250216960e01b81526001600160a01b0390931692636250216992611f5c929091600401615237565b600060405180830381600087803b158015611f7657600080fd5b505af1158015611f8a573d6000803e3d6000fd5b50505060608301516040840151611fa192506151c9565b60808301526020820151604080840151606085015191517f43a3f4082a4dbc33d78e317d2497d3a730bc7fc3574159dcea1056e62e5d9ad893611ffd938f93919293845260208401929092526040830152606082015260800190565b60405180910390a182604001516001600160a01b0316639dc29fac3384602001516040518363ffffffff1660e01b815260040161203b929190615237565b600060405180830381600087803b15801561205557600080fd5b505af1158015612069573d6000803e3d6000fd5b505084516020850151604051637b7fc9eb60e11b81526001600160a01b03909216935063f6ff93d692506120a39160040190815260200190565b600060405180830381600087803b1580156120bd57600080fd5b505af11580156120d1573d6000803e3d6000fd5b505084516080850151604051636250216960e01b81526001600160a01b0390921693506362502169925061210a91339190600401615237565b600060405180830381600087803b15801561212457600080fd5b505af1158015612138573d6000803e3d6000fd5b5050505050505050505050505050565b6000610e8f612155612e57565b612d5d565b612162612319565b610a21816002613da0565b600080600061217b85613559565b915091506000610fc4838387613f47565b6000612196612319565b6001600160a01b0383166000908152600d602052604081206001015461175c9084906151c9565b6000610a716121ca612148565b83613f78565b6000601482815481106121e5576121e5615291565b6000918252602090912001546001600160a01b031692915050565b600060016001600160a01b0383166000908152600d602052604090206003015460ff16600481111561223457612234614f60565b1461224157506000919050565b506011546001600160a01b039091166000908152601360205260409020541090565b6003546001600160a01b0316331461228d5760405162461bcd60e51b8152600401610b18906152bd565b6001600160a01b0381166122f25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610b18565b610a2181614008565b6000610e8f600b546133b4565b612310612319565b610a218161405a565b6004546001600160a01b031633146123995760405162461bcd60e51b815260206004820152603b60248201527f54726f76654d616e616765723a2043616c6c6572206973206e6f74207468652060448201527f426f72726f7765724f7065726174696f6e7320636f6e747261637400000000006064820152608401610b18565b565b6123a481612200565b15612492576123b281612d9a565b60006123bd82610f13565b905060006123ca836119f6565b6001600160a01b0384166000908152600d60205260408120600101805492935084929091906123fa9084906152f2565b90915550506001600160a01b0383166000908152600d6020526040812080548392906124279084906152f2565b909155506124369050836134f5565b612442858583856140ac565b6001600160a01b0383166000818152600d602052604080822080546001820154600290920154925160008051602061540c833981519152946124879492939291615349565b60405180910390a250505b505050565b601480546001808201835560008381527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec90920180546001600160a01b0319166001600160a01b038616179055915490916124f1916151c9565b6001600160a01b03929092166000908152600d602052604090206003018054610100600160881b0319166101006001600160801b038516021790555090565b6001600160a01b0381166000908152600d602052604081206001015481906125579061418d565b6001600160a01b0384166000908152600d60205260409020600201805490829055600e5491925090829061258c9083906151c9565b61259691906152f2565b600e8190556040519081527f6bac5e0eb3c44eb03a60ab11ec3a2c051771616aecadbcfff2630aabae5203829060200160405180910390a15092915050565b6000806125e18361360b565b6714d1120d7b160000119392505050565b6125fa614d6f565b612602614dbb565b61260a614d6f565b8482526000608083015261261c6117ad565b60a08301526126296118ba565b60c0830152600060208301525b83518260200151101561281c578382602001518151811061265957612659615291565b6020908102919091018101516001600160a01b0316606084018190526000908152600d909152604090206003015460019060ff16600481111561269e5761269e614f60565b03612804576126b182606001518761216d565b604083015260808201516127a757670f43fc2c04ee00008260400151101580156126da57508151155b6128045760006126f38360c001518460a0015189613f47565b90506127108989856060015186604001518760000151868d6141c3565b915081608001518360000181815161272891906151c9565b905250608082015160a0840180516127419083906151c9565b905250610100820151604083015160a084015161275e91906152f2565b61276891906152f2565b8360c00181815161277991906151c9565b90525061278684836144be565b935061279b8360c001518460a00151896145a8565b15608084015250612804565b816080015180156127c35750670f43fc2c04ee00008260400151105b15612804576127dc8888846060015185600001516145c9565b90508060800151826000018181516127f491906151c9565b90525061280183826144be565b92505b602082018051906128148261536b565b905250612636565b505095945050505050565b61282f614d6f565b612837614dbb565b61283f614d6f565b848252600060208301525b83518260200151101561281c578382602001518151811061286d5761286d615291565b60209081029190910101516001600160a01b031660608301819052612892908761216d565b60408301819052670f43fc2c04ee000011156128e4576128bc8888846060015185600001516145c9565b90508060800151826000018181516128d491906151c9565b9052506128e183826144be565b92505b602082018051906128f48261536b565b90525061284a565b8115612b355760155460009061291a670de0b6b3a76400008461527a565b61292491906152f2565b90506000601654670de0b6b3a76400008561293f919061527a565b61294991906152f2565b90506000600e548361295b9190615266565b90506000600e548361296d9190615266565b9050600e548261297d919061527a565b61298790856151c9565b601555600e54612997908261527a565b6129a190846151c9565b60168190555081601160008282546129b991906152f2565b9250508190555080601260008282546129d291906152f2565b90915550506011546012546040517f9f8bc8ab0daf5bceef75ecfd2085d1fcc6548c657ea970d9a23a60610d0737e392612a1492908252602082015260400190565b60405180910390a1604051637b7fc9eb60e11b8152600481018790526001600160a01b0389169063f6ff93d690602401600060405180830381600087803b158015612a5e57600080fd5b505af1158015612a72573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018990526001600160a01b038a1692506342c31a239150602401600060405180830381600087803b158015612ab857600080fd5b505af1158015612acc573d6000803e3d6000fd5b5050604051636250216960e01b81526001600160a01b038b16925063625021699150612afe908a908990600401615237565b600060405180830381600087803b158015612b1857600080fd5b505af1158015612b2c573d6000803e3d6000fd5b50505050505050505b50505050565b600e54600f819055506000826001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba891906151dc565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631529a6396040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2391906151dc565b905080612c3084846151c9565b612c3a91906152f2565b6010819055600f546040517f51bf4c63ec3cba9d03d43238abbdd979dd91bd16d9895c74ceea9118c7baaf6092612c78928252602082015260400190565b60405180910390a150505050565b8115612cf1576006546040516316268b0d60e21b81526001600160a01b039091169063589a2c3490612cbe9086908690600401615237565b600060405180830381600087803b158015612cd857600080fd5b505af1158015612cec573d6000803e3d6000fd5b505050505b8015612b3557604051636250216960e01b81526001600160a01b03851690636250216990612d259086908590600401615237565b600060405180830381600087803b158015612d3f57600080fd5b505af1158015612d53573d6000803e3d6000fd5b5050505050505050565b6000610a7182612d776103e8670de0b6b3a7640000615266565b612d8290600561527a565b612d8c91906152f2565b670de0b6b3a7640000614710565b60016001600160a01b0382166000908152600d602052604090206003015460ff166004811115612dcc57612dcc614f60565b14610a215760405162461bcd60e51b815260206004820152602f60248201527f54726f76654d616e616765723a2054726f766520646f6573206e6f742065786960448201526e1cdd081bdc881a5cc818db1bdcd959608a1b6064820152608401610b18565b6000670de0b6b3a7640000612e46838561527a565b612e509190615266565b9392505050565b600080612e62614726565b90506000612e78670ddd4b8c6c7d70d883614742565b9050670de0b6b3a764000081600b54612e91919061527a565b6118a29190615266565b6000600c5442612eab91906151c9565b9050603c8110610a215742600c8190556040519081527f860f8d2f0c74dd487e89e2883e3b25b8159ce1e1b3433a291cba7b82c508f3bc906020015b60405180910390a150565b612efa614d6f565b612f02614dbb565b612f0a614d6f565b84825260006080830152612f1c6117ad565b60a0830152612f296118ba565b8260c001818152505086608001516001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f989190615305565b82606001906001600160a01b031690816001600160a01b031681525050600087608001516001600160a01b0316631e2231436040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ff9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301d9190615305565b6000602085015290505b8483602001511080156130505750806001600160a01b031683606001516001600160a01b031614155b156132735760808801516060840151604051632dc9c0eb60e21b81526000926001600160a01b03169163b72703ac9161308c9190600401614e03565b602060405180830381865afa1580156130a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130cd9190615305565b90506130dd84606001518961216d565b604085015260808401516131de57670f43fc2c04ee000084604001511015801561310657508351155b156131115750613273565b60006131268560c001518660a001518b613f47565b8a5160208c015160608801516040890151895194955061314794868f6141c3565b935083608001518560000181815161315f91906151c9565b905250608084015160a0860180516131789083906151c9565b905250610100840151604085015160a086015161319591906152f2565b61319f91906152f2565b8560c0018181516131b091906151c9565b9052506131bd86856144be565b95506131d28560c001518660a001518b6145a8565b1560808601525061324d565b836080015180156131fa5750670f43fc2c04ee00008460400151105b156132475761321b89600001518a60200151866060015187600001516145c9565b925082608001518460000181815161323391906151c9565b90525061324085846144be565b945061324d565b50613273565b6001600160a01b031660608401526020830180519061326b8261536b565b905250613027565b505050949350505050565b613286614d6f565b61328e614dbb565b613296614d6f565b600a54858352600060208401526001600160a01b03165b84836020015110156133a857806001600160a01b0316634d6228316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061331b9190615305565b6001600160a01b031660608401819052613335908861216d565b60408401819052670f43fc2c04ee0000111561338b5761335f8989856060015186600001516145c9565b915081608001518360000181815161337791906151c9565b90525061338484836144be565b9350613390565b6133a8565b602083018051906133a08261536b565b9052506132ad565b50505095945050505050565b6000610a71826133ce6103e8670de0b6b3a7640000615266565b6133d990600561527a565b6133e391906152f2565b6133f66064670de0b6b3a7640000615266565b61340190600561527a565b614710565b6001600160a01b03811661345c5760405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606401610b18565b803b80610efc5760405162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152606401610b18565b6003546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600380546001600160a01b0319169055565b601180546001600160a01b038316600090815260136020526040908190209182556012546001909201829055915491517fc437f324d85e369394148dd9d62f98f534b382e01ed3dd2eb98138fb6d3ab49a92612ee792908252602082015260400190565b600080600061356784610f13565b90506000613574856119f6565b6001600160a01b0386166000908152600d60205260408120600101549192509061359f9084906152f2565b6001600160a01b0387166000908152600d6020526040812054919250906135c79084906152f2565b919791965090945050505050565b6000811561360257816135f168056bc75e2d631000008561527a565b6135fb9190615266565b9050610a71565b50600019610a71565b6000806136166118ba565b905060006136226117ad565b905061362f828286613f47565b949350505050565b61364b6103e8670de0b6b3a7640000615266565b61365690600561527a565b811015801561366d5750670de0b6b3a76400008111155b610a215760405162461bcd60e51b815260206004820152603060248201527f4d6178206665652070657263656e74616765206d75737420626520626574776560448201526f656e20302e352520616e64203130302560801b6064820152608401610b18565b670f43fc2c04ee00006136e48261360b565b1015610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a2043616e6e6f742072656465656d207768656e604482015269102a21a9101e1026a1a960b11b6064820152608401610b18565b60008111610a215760405162461bcd60e51b815260206004820152602e60248201527f54726f76654d616e616765723a20416d6f756e74206d7573742062652067726560448201526d61746572207468616e207a65726f60901b6064820152608401610b18565b6040516370a0823160e01b815281906001600160a01b038516906370a08231906137da908690600401614e03565b602060405180830381865afa1580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381b91906151dc565b10156124925760405162461bcd60e51b815260206004820152604f60248201527f54726f76654d616e616765723a2052657175657374656420726564656d70746960448201527f6f6e20616d6f756e74206d757374206265203c3d20757365722773205448555360648201526e4420746f6b656e2062616c616e636560881b608482015260a401610b18565b60006001600160a01b03831615806139295750604051630bb7c8fd60e31b81526001600160a01b03851690635dbe47e8906138e6908690600401614e03565b602060405180830381865afa158015613903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139279190615384565b155b806139445750670f43fc2c04ee0000613942848461216d565b105b1561395157506000612e50565b60405163765e015960e01b81526000906001600160a01b0386169063765e015990613980908790600401614e03565b602060405180830381865afa15801561399d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c19190615305565b90506001600160a01b0381161580611a405750670f43fc2c04ee00006139e7828561216d565b1095945050505050565b613a17604051806060016040528060008152602001600081526020016000151581525090565b6001600160a01b0387166000908152600d6020526040902054613a4a90879061340190680ad78ebc5ac6200000906151c9565b8082528590613a6290670de0b6b3a76400009061527a565b613a6c9190615266565b60208083019190915281516001600160a01b0389166000908152600d9092526040822054613a9a91906151c9565b6020808401516001600160a01b038b166000908152600d90925260408220600101549293509091613acb91906151c9565b9050680ad78ebc5ac62000008203613b4357613ae68961405a565b613af1896004613da0565b613b068a8a680ad78ebc5ac6200000846147ed565b886001600160a01b031660008051602061540c83398151915260008060006003604051613b369493929190615349565b60405180910390a2613c71565b6000613b4f82846135d5565b90508481141580613b705750686194049f30f7200000613b6e8461494c565b105b15613b845750506001604083015250613c74565b60808b015160405163015f109360e51b81526001600160a01b038c81166004830152602482018490528981166044830152888116606483015290911690632be2126090608401600060405180830381600087803b158015613be457600080fd5b505af1158015613bf8573d6000803e3d6000fd5b5050506001600160a01b038b166000908152600d6020526040902084815560010183905550613c268a612530565b506001600160a01b038a166000818152600d60205260409081902060020154905160008051602061540c83398151915291613c679187918791600390615349565b60405180910390a2505b50505b979650505050505050565b600080613c8a612e57565b9050600083613c99868861527a565b613ca39190615266565b90506000613cb2600283615266565b613cbc90846152f2565b9050613cd081670de0b6b3a7640000614710565b905060008111613ce257613ce26152a7565b600b8190556040518181527fc454ee9b76c52f782a256af821b857ca6e125d1e3333bcede402fec2bed9600c9060200160405180910390a1610fc4612e9b565b6000610a716121ca610e82565b600082613d44670de0b6b3a76400008661527a565b613d4e9190615266565b905081811115612b355760405162461bcd60e51b815260206004820152601d60248201527f4665652065786365656465642070726f7669646564206d6178696d756d0000006044820152606401610b18565b6000816004811115613db457613db4614f60565b14158015613dd457506001816004811115613dd157613dd1614f60565b14155b613de057613de06152a7565b60145460085460048054604051635f7a196360e11b81526001600160a01b039384169363bef432c693613e17939091169101614e03565b602060405180830381865afa158015613e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e589190615384565b15613e6657613e6681614961565b6001600160a01b0383166000908152600d60205260409020600301805483919060ff19166001836004811115613e9e57613e9e614f60565b02179055506001600160a01b0383166000908152600d60209081526040808320600180820185905590849055601390925282208281550155613ee08382614a3b565b600a54604051631484968760e11b81526001600160a01b03909116906329092d0e90613f10908690600401614e03565b600060405180830381600087803b158015613f2a57600080fd5b505af1158015613f3e573d6000803e3d6000fd5b50505050505050565b60008215613f6f57600083613f5c848761527a565b613f669190615266565b9150612e509050565b50600019612e50565b600080670de0b6b3a7640000613f8e848661527a565b613f989190615266565b9050828110612e505760405162461bcd60e51b815260206004820152603660248201527f54726f76654d616e616765723a2046656520776f756c642065617420757020616044820152751b1b081c995d1d5c9b99590818dbdb1b185d195c985b60521b6064820152608401610b18565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0381166000908152600d6020526040812060020154600e8054919283926140899084906151c9565b9091555050506001600160a01b03166000908152600d6020526040812060020155565b604051637b7fc9eb60e11b8152600481018390526001600160a01b0384169063f6ff93d690602401600060405180830381600087803b1580156140ee57600080fd5b505af1158015614102573d6000803e3d6000fd5b50506040516342c31a2360e01b8152600481018590526001600160a01b03871692506342c31a239150602401600060405180830381600087803b15801561414857600080fd5b505af115801561415c573d6000803e3d6000fd5b5050604051633ef8ba8160e11b8152600481018490526001600160a01b0386169250637df175029150602401612d25565b6000806010546000036141a1575081610a71565b6000600f54116141b3576141b36152a7565b601054600f54612e46908561527a565b6141cb614d6f565b6141ef60405180606001604052806000815260200160008152602001600081525090565b6014546001106141ff5750613c74565b61420887611a54565b6040850152602084810191909152840181905290835261422790614c0b565b60408301819052680ad78ebc5ac62000006060840152602083015161424c91906151c9565b8152670de0b6b3a76400008611614317576142718989836020015184604001516140ac565b61427a8761405a565b60006080830181905260a0830152815160c0830152805160e08301526142a1876003613da0565b815160208301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b60405180910390a2866001600160a01b031660008051602061540c8339815191526000806000600260405161430a9493929190615349565b60405180910390a26144b2565b670de0b6b3a7640000861180156143355750670f43fc2c04ee000086105b156143855761434e8989836020015184604001516140ac565b6143578761405a565b81518151614366919087614c18565b60e086015260c085015260a084015260808301526142a1876003613da0565b670f43fc2c04ee0000861015801561439c57508386105b80156143a9575081518510155b156144a1576143c28989836020015184604001516140ac565b846000036143d2576143d26152a7565b6143db8761405a565b6143ee8260000151836020015185614c79565b91506143fb876003613da0565b6101008201511561447057600754610100830151604051633f10abab60e01b81526001600160a01b0390921691633f10abab9161443d918b9190600401615237565b600060405180830381600087803b15801561445757600080fd5b505af115801561446b573d6000803e3d6000fd5b505050505b815160a08301516040516001600160a01b038a16926000805160206153ec833981519152926142d2926002906153a6565b6144a9614d6f565b9150613c749050565b50979650505050505050565b6144c6614d6f565b816040015183604001516144da91906152f2565b6040820152606080830151908401516144f391906152f2565b60608201528151602084015161450991906152f2565b602080830191909152820151835161452191906152f2565b81526080808301519084015161453791906152f2565b608082015260a0808301519084015161455091906152f2565b60a082015260c0808301519084015161456991906152f2565b60c082015260e0808301519084015161458291906152f2565b60e0820152610100808301519084015161459c91906152f2565b61010082015292915050565b6000806145b6858585613f47565b6714d1120d7b1600001195945050505050565b6145d1614d6f565b6145f560405180606001604052806000815260200160008152602001600081525090565b6145fe84611a54565b604085019081526020858101928352860192909252918452905190516146289188918891906140ac565b6146318461405a565b61463e8260200151614c0b565b60408301819052680ad78ebc5ac620000060608401526020830151600091614665916151c9565b905061467683600001518286614c18565b60e087015260c086015260a08501526080840152614695856003613da0565b825160208401516040516001600160a01b038816926000805160206153ec833981519152926146c6926001906153a6565b60405180910390a2846001600160a01b031660008051602061540c833981519152600080600060016040516146fe9493929190615349565b60405180910390a25050949350505050565b600081831061471f5781612e50565b5090919050565b6000603c600c544261473891906151c9565b610e8f9190615266565b6000631f54050082111561475857631f54050091505b8160000361476f5750670de0b6b3a7640000610a71565b670de0b6b3a764000083835b60018111156147e35761478f6002826153c1565b6000036147b4576147a08283614d07565b91506147ad600282615266565b905061477b565b6147be8284614d07565b92506147ca8283614d07565b915060026147d96001836151c9565b6147ad9190615266565b610fc48284614d07565b6040808501516006549151632770a7eb60e21b81526001600160a01b0391821692639dc29fac92614825929116908690600401615237565b600060405180830381600087803b15801561483f57600080fd5b505af1158015614853573d6000803e3d6000fd5b50508551604051637b7fc9eb60e11b8152600481018690526001600160a01b03909116925063f6ff93d69150602401600060405180830381600087803b15801561489c57600080fd5b505af11580156148b0573d6000803e3d6000fd5b505050508360a001516001600160a01b0316633f10abab84836040518363ffffffff1660e01b81526004016148e6929190615237565b600060405180830381600087803b15801561490057600080fd5b505af1158015614914573d6000803e3d6000fd5b5050855160a0870151604051636250216960e01b81526001600160a01b03909216935063625021699250612d25918590600401615237565b6000610a71680ad78ebc5ac6200000836151c9565b6001811180156149dc5750600a546040805163de8fa43160e01b815290516001926001600160a01b03169163de8fa4319160048083019260209291908290030181865afa1580156149b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149da91906151dc565b115b610a215760405162461bcd60e51b815260206004820152602a60248201527f54726f76654d616e616765723a204f6e6c79206f6e652074726f766520696e206044820152697468652073797374656d60b01b6064820152608401610b18565b6001600160a01b0382166000908152600d602052604081206003015460ff1690816004811115614a6d57614a6d614f60565b14158015614a8d57506001816004811115614a8a57614a8a614f60565b14155b614a9957614a996152a7565b6001600160a01b0383166000908152600d602052604081206003015461010090046001600160801b0316908390614ad16001836151c9565b905080836001600160801b03161115614aec57614aec6152a7565b600060148281548110614b0157614b01615291565b600091825260209091200154601480546001600160a01b03909216925082916001600160801b038716908110614b3957614b39615291565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918316808252600d83526040918290206003018054610100600160881b0319166101006001600160801b038a16908102919091179091558251918252928101929092527f02b04ae5f7be9ca7c103293a2aa15f3c339d15d6eda53b721fef7b0e609c831a910160405180910390a16014805480614be057614be06153d5565b600082815260209020810160001990810180546001600160a01b031916905501905550505050505050565b6000610a7160c883615266565b60008080808415614c6357614c2d8786614710565b935086614c3a858861527a565b614c449190615266565b9250614c5084886151c9565b9150614c5c83876151c9565b9050614c70565b5060009250829150859050845b93509350935093565b614c81614d6f565b83815260208101839052600082614ca0670f43fc2c04ee00008761527a565b614caa9190615266565b9050614cb581614c0b565b60408301819052680ad78ebc5ac6200000606084015260808301869052614cdc90826151c9565b60a0830152614ceb81856151c9565b61010083015250600060c0820181905260e08201529392505050565b600080614d14838561527a565b9050670de0b6b3a7640000614d2a600282615266565b614d3490836152f2565b61362f9190615266565b6040518060a00160405280600081526020016000815260200160001515815260200160008152602001600081525090565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e0016040528060008152602001600081526020016000815260200160006001600160a01b0316815260200160001515815260200160008152602001600081525090565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610a2157600080fd5b8035614e3781614e17565b919050565b600060208284031215614e4e57600080fd5b8135612e5081614e17565b60008060408385031215614e6c57600080fd5b8235614e7781614e17565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215614eae57600080fd5b823567ffffffffffffffff80821115614ec657600080fd5b818501915085601f830112614eda57600080fd5b813581811115614eec57614eec614e85565b8060051b604051601f19603f83011681018181108582111715614f1157614f11614e85565b604052918252848201925083810185019188831115614f2f57600080fd5b938501935b82851015614f5457614f4585614e2c565b84529385019392850192614f34565b98975050505050505050565b634e487b7160e01b600052602160045260246000fd5b60058110614f8657614f86614f60565b9052565b60208101610a718284614f76565b600060208284031215614faa57600080fd5b5035919050565b6000806000806000806000806000806101408b8d031215614fd157600080fd5b8a35614fdc81614e17565b995060208b0135614fec81614e17565b985060408b0135614ffc81614e17565b975060608b013561500c81614e17565b965060808b013561501c81614e17565b955060a08b013561502c81614e17565b945060c08b013561503c81614e17565b935060e08b013561504c81614e17565b92506101008b013561505d81614e17565b915061506c6101208c01614e2c565b90509295989b9194979a5092959850565b858152602081018590526040810184905260a0810161509f6060830185614f76565b6001600160801b039290921660809190910152949350505050565b600080604083850312156150cd57600080fd5b82356150d881614e17565b91506020830135600581106150ec57600080fd5b809150509250929050565b600060208083528351808285015260005b8181101561512457858101830151858201604001528201615108565b506000604082860101526040601f19601f8301168501019250505092915050565b600080600080600080600060e0888a03121561516057600080fd5b87359650602088013561517281614e17565b9550604088013561518281614e17565b9450606088013561519281614e17565b9699959850939660808101359560a0820135955060c0909101359350915050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7157610a716151b3565b6000602082840312156151ee57600080fd5b5051919050565b60208082526022908201527f54726f76654d616e616765723a206e6f7468696e6720746f206c697175696461604082015261746560f01b606082015260800190565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601260045260246000fd5b60008261527557615275615250565b500490565b8082028115828204841417610a7157610a716151b3565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820180821115610a7157610a716151b3565b60006020828403121561531757600080fd5b8151612e5081614e17565b600081615331576153316151b3565b506000190190565b60048110614f8657614f86614f60565b848152602081018490526040810183905260808101611a406060830184615339565b60006001820161537d5761537d6151b3565b5060010190565b60006020828403121561539657600080fd5b81518015158114612e5057600080fd5b838152602081018390526060810161362f6040830184615339565b6000826153d0576153d0615250565b500690565b634e487b7160e01b600052603160045260246000fdfeea67486ed7ebe3eea8ab3390efd4a3c8aae48be5bea27df104a8af786c408434c3770d654ed33aeea6bf11ac8ef05d02a6a04ed4686dd2f624d853bbec43cc8ba26469706673582212202d04ada939169bc7aa7216586e65c2ce4e3fc480b31b737556f319a5eca7f7f364736f6c63430008110033

Verified Source Code Full Match

Compiler: v0.8.17+commit.8df45f5f EVM: london Optimization: Yes (100 runs)
TroveManager.sol 1512 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./Interfaces/ITroveManager.sol";
import "./Interfaces/IStabilityPool.sol";
import "./Interfaces/ICollSurplusPool.sol";
import "./Interfaces/IGasPool.sol";
import "./Interfaces/ITHUSDToken.sol";
import "./Interfaces/ISortedTroves.sol";
import "./Interfaces/IPCV.sol";
import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager {

    string constant public NAME = "TroveManager";

    // --- Connected contract declarations ---

    address public borrowerOperationsAddress;

    IStabilityPool public override stabilityPool;

    address gasPoolAddress;

    ICollSurplusPool collSurplusPool;

    ITHUSDToken public override thusdToken;

    IPCV public override pcv;

    // A doubly linked list of Troves, sorted by their sorted by their collateral ratios
    ISortedTroves public sortedTroves;

    // --- Data structures ---

    /*
     * Half-life of 12h. 12h = 720 min
     * (1/2) = d^720 => d = (1/2)^(1/720)
     */
    uint256 constant public MINUTE_DECAY_FACTOR = 999037758833783000;
    uint256 constant public REDEMPTION_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%
    uint256 constant public MAX_BORROWING_FEE = DECIMAL_PRECISION / 100 * 5; // 5%

    /*
    * BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a redemption.
    * Corresponds to (1 / ALPHA) in the white paper.
    */
    uint256 constant public BETA = 2;

    uint256 public baseRate;

    // The timestamp of the latest fee operation (redemption or new THUSD issuance)
    uint256 public lastFeeOperationTime;

    // Store the necessary data for a trove
    struct Trove {
        uint256 debt;
        uint256 coll;
        uint256 stake;
        Status status;
        uint128 arrayIndex;
    }

    mapping (address => Trove) public Troves;

    uint256 public totalStakes;

    // Snapshot of the value of totalStakes, taken immediately after the latest liquidation
    uint256 public totalStakesSnapshot;

    // Snapshot of the total collateral across the ActivePool and DefaultPool, immediately after the latest liquidation.
    uint256 public totalCollateralSnapshot;

    /*
    * L_Collateral and L_THUSDDebt track the sums of accumulated liquidation rewards per unit staked. During its lifetime, each stake earns:
    *
    * An collateral gain of ( stake * [L_Collateral - L_Collateral(0)] )
    * A THUSDDebt increase  of ( stake * [L_THUSDDebt - L_THUSDDebt(0)] )
    *
    * Where L_Collateral(0) and L_THUSDDebt(0) are snapshots of L_Collateral and L_THUSDDebt for the active Trove taken at the instant the stake was made
    */
    uint256 public L_Collateral;
    uint256 public L_THUSDDebt;

    // Map addresses with active troves to their RewardSnapshot
    mapping (address => RewardSnapshot) public rewardSnapshots;

    // Object containing the collateral and THUSD snapshots for a given active trove
    struct RewardSnapshot { uint256 collateral; uint256 THUSDDebt;}

    // Array of all active trove addresses - used to to compute an approximate hint off-chain, for the sorted list insertion
    address[] public TroveOwners;

    // Error trackers for the trove redistribution calculation
    uint256 public lastCollateralError_Redistribution;
    uint256 public lastTHUSDDebtError_Redistribution;

    /*
    * --- Variable container structs for liquidations ---
    *
    * These structs are used to hold, return and assign variables inside the liquidation functions,
    * in order to avoid the error: "CompilerError: Stack too deep".
    **/

    struct LocalVariables_OuterLiquidationFunction {
        uint256 price;
        uint256 THUSDInStabPool;
        bool recoveryModeAtStart;
        uint256 liquidatedDebt;
        uint256 liquidatedColl;
    }

    struct LocalVariables_InnerSingleLiquidateFunction {
        uint256 collToLiquidate;
        uint256 pendingDebtReward;
        uint256 pendingCollReward;
    }

    struct LocalVariables_LiquidationSequence {
        uint256 remainingTHUSDInStabPool;
        uint256 i;
        uint256 ICR;
        address user;
        bool backToNormalMode;
        uint256 entireSystemDebt;
        uint256 entireSystemColl;
    }

    struct LiquidationValues {
        uint256 entireTroveDebt;
        uint256 entireTroveColl;
        uint256 collGasCompensation;
        uint256 THUSDGasCompensation;
        uint256 debtToOffset;
        uint256 collToSendToSP;
        uint256 debtToRedistribute;
        uint256 collToRedistribute;
        uint256 collSurplus;
    }

    struct LiquidationTotals {
        uint256 totalCollInSequence;
        uint256 totalDebtInSequence;
        uint256 totalCollGasCompensation;
        uint256 totalTHUSDGasCompensation;
        uint256 totalDebtToOffset;
        uint256 totalCollToSendToSP;
        uint256 totalDebtToRedistribute;
        uint256 totalCollToRedistribute;
        uint256 totalCollSurplus;
    }

    struct ContractsCache {
        IActivePool activePool;
        IDefaultPool defaultPool;
        ITHUSDToken thusdToken;
        IPCV pcv;
        ISortedTroves sortedTroves;
        ICollSurplusPool collSurplusPool;
        address gasPoolAddress;
    }
    // --- Variable container structs for redemptions ---

    struct RedemptionTotals {
        uint256 remainingTHUSD;
        uint256 totalTHUSDToRedeem;
        uint256 totalCollateralDrawn;
        uint256 collateralFee;
        uint256 collateralToSendToRedeemer;
        uint256 decayedBaseRate;
        uint256 price;
        uint256 totalTHUSDDebtAtStart;
    }

    struct SingleRedemptionValues {
        uint256 THUSDLot;
        uint256 collateralLot;
        bool cancelledPartial;
    }

    // --- Events ---
    event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 _stake, TroveManagerOperation _operation);
    event TroveLiquidated(address indexed _borrower, uint256 _debt, uint256 _coll, TroveManagerOperation _operation);

    enum TroveManagerOperation {
        applyPendingRewards,
        liquidateInNormalMode,
        liquidateInRecoveryMode,
        redeemCollateral
    }


    // --- Dependency setter ---

    function setAddresses(
        address _borrowerOperationsAddress,
        address _activePoolAddress,
        address _defaultPoolAddress,
        address _stabilityPoolAddress,
        address _gasPoolAddress,
        address _collSurplusPoolAddress,
        address _priceFeedAddress,
        address _thusdTokenAddress,
        address _sortedTrovesAddress,
        address _pcvAddress
    )
        external
        override
        onlyOwner
    {
        checkContract(_borrowerOperationsAddress);
        checkContract(_activePoolAddress);
        checkContract(_defaultPoolAddress);
        checkContract(_stabilityPoolAddress);
        checkContract(_gasPoolAddress);
        checkContract(_collSurplusPoolAddress);
        checkContract(_priceFeedAddress);
        checkContract(_thusdTokenAddress);
        checkContract(_sortedTrovesAddress);
        checkContract(_pcvAddress);

        borrowerOperationsAddress = _borrowerOperationsAddress;
        activePool = IActivePool(_activePoolAddress);
        defaultPool = IDefaultPool(_defaultPoolAddress);
        stabilityPool = IStabilityPool(_stabilityPoolAddress);
        gasPoolAddress = _gasPoolAddress;
        collSurplusPool = ICollSurplusPool(_collSurplusPoolAddress);
        priceFeed = IPriceFeed(_priceFeedAddress);
        thusdToken = ITHUSDToken(_thusdTokenAddress);
        sortedTroves = ISortedTroves(_sortedTrovesAddress);
        pcv = IPCV(_pcvAddress);

        emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
        emit ActivePoolAddressChanged(_activePoolAddress);
        emit DefaultPoolAddressChanged(_defaultPoolAddress);
        emit StabilityPoolAddressChanged(_stabilityPoolAddress);
        emit GasPoolAddressChanged(_gasPoolAddress);
        emit CollSurplusPoolAddressChanged(_collSurplusPoolAddress);
        emit PriceFeedAddressChanged(_priceFeedAddress);
        emit THUSDTokenAddressChanged(_thusdTokenAddress);
        emit SortedTrovesAddressChanged(_sortedTrovesAddress);
        emit PCVAddressChanged(_pcvAddress);

        _renounceOwnership();
    }

    // --- Getters ---

    function getTroveOwnersCount() external view override returns (uint) {
        return TroveOwners.length;
    }

    function getTroveFromTroveOwnersArray(uint256 _index) external view override returns (address) {
        return TroveOwners[_index];
    }

    // --- Trove Liquidation functions ---

    // Single liquidation function. Closes the trove if its ICR is lower than the minimum collateral ratio.
    function liquidate(address _borrower) external override {
        _requireTroveIsActive(_borrower);

        address[] memory borrowers = new address[](1);
        borrowers[0] = _borrower;
        batchLiquidateTroves(borrowers);
    }

    // --- Inner single liquidation functions ---

    // Liquidate one trove, in Normal Mode.
    function _liquidateNormalMode(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        address _borrower,
        uint256 _THUSDInStabPool
    )
        internal
        returns (LiquidationValues memory singleLiquidation)
    {
        LocalVariables_InnerSingleLiquidateFunction memory vars;

        (singleLiquidation.entireTroveDebt,
        singleLiquidation.entireTroveColl,
        vars.pendingDebtReward,
        vars.pendingCollReward) = getEntireDebtAndColl(_borrower);

        _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
        _removeStake(_borrower);

        singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;
        uint256 collToLiquidate = singleLiquidation.entireTroveColl - singleLiquidation.collGasCompensation;

        (singleLiquidation.debtToOffset,
        singleLiquidation.collToSendToSP,
        singleLiquidation.debtToRedistribute,
        singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, collToLiquidate, _THUSDInStabPool);

        _closeTrove(_borrower, Status.closedByLiquidation);
        emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInNormalMode);
        emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInNormalMode);
        return singleLiquidation;
    }

    // Liquidate one trove, in Recovery Mode.
    function _liquidateRecoveryMode(
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        address _borrower,
        uint256 _ICR,
        uint256 _THUSDInStabPool,
        uint256 _TCR,
        uint256 _price
    )
        internal
        returns (LiquidationValues memory singleLiquidation)
    {
        LocalVariables_InnerSingleLiquidateFunction memory vars;
        if (TroveOwners.length <= 1) {return singleLiquidation;} // don't liquidate if last trove
        (singleLiquidation.entireTroveDebt,
        singleLiquidation.entireTroveColl,
        vars.pendingDebtReward,
        vars.pendingCollReward) = getEntireDebtAndColl(_borrower);

        singleLiquidation.collGasCompensation = _getCollGasCompensation(singleLiquidation.entireTroveColl);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;
        vars.collToLiquidate = singleLiquidation.entireTroveColl - singleLiquidation.collGasCompensation;

        // If ICR <= 100%, purely redistribute the Trove across all active Troves
        if (_ICR <= _100pct) {
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            _removeStake(_borrower);

            singleLiquidation.debtToOffset = 0;
            singleLiquidation.collToSendToSP = 0;
            singleLiquidation.debtToRedistribute = singleLiquidation.entireTroveDebt;
            singleLiquidation.collToRedistribute = vars.collToLiquidate;

            _closeTrove(_borrower, Status.closedByLiquidation);
            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);

        // If 100% < ICR < MCR, offset as much as possible, and redistribute the remainder
        } else if ((_ICR > _100pct) && (_ICR < MCR)) {
             _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            _removeStake(_borrower);

            (singleLiquidation.debtToOffset,
            singleLiquidation.collToSendToSP,
            singleLiquidation.debtToRedistribute,
            singleLiquidation.collToRedistribute) = _getOffsetAndRedistributionVals(singleLiquidation.entireTroveDebt, vars.collToLiquidate, _THUSDInStabPool);

            _closeTrove(_borrower, Status.closedByLiquidation);
            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);
        /*
        * If 110% <= ICR < current TCR (accounting for the preceding liquidations in the current sequence)
        * and there is THUSD in the Stability Pool, only offset, with no redistribution,
        * but at a capped rate of 1.1 and only if the whole debt can be liquidated.
        * The remainder due to the capped rate will be claimable as collateral surplus.
        */
        } else if ((_ICR >= MCR) && (_ICR < _TCR) && (singleLiquidation.entireTroveDebt <= _THUSDInStabPool)) {
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, vars.pendingDebtReward, vars.pendingCollReward);
            assert(_THUSDInStabPool != 0);

            _removeStake(_borrower);
            singleLiquidation = _getCappedOffsetVals(singleLiquidation.entireTroveDebt, singleLiquidation.entireTroveColl, _price);

            _closeTrove(_borrower, Status.closedByLiquidation);
            if (singleLiquidation.collSurplus > 0) {
                collSurplusPool.accountSurplus(_borrower, singleLiquidation.collSurplus);
            }

            emit TroveLiquidated(_borrower, singleLiquidation.entireTroveDebt, singleLiquidation.collToSendToSP, TroveManagerOperation.liquidateInRecoveryMode);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.liquidateInRecoveryMode);

        } else { // if (_ICR >= MCR && ( _ICR >= _TCR || singleLiquidation.entireTroveDebt > _THUSDInStabPool))
            LiquidationValues memory zeroVals;
            return zeroVals;
        }

        return singleLiquidation;
    }

    /* In a full liquidation, returns the values for a trove's coll and debt to be offset, and coll and debt to be
    * redistributed to active troves.
    */
    function _getOffsetAndRedistributionVals
    (
        uint256 _debt,
        uint256 _coll,
        uint256 _THUSDInStabPool
    )
        internal
        pure
        returns (uint256 debtToOffset, uint256 collToSendToSP, uint256 debtToRedistribute, uint256 collToRedistribute)
    {
        if (_THUSDInStabPool > 0) {
        /*
        * Offset as much debt & collateral as possible against the Stability Pool, and redistribute the remainder
        * between all active troves.
        *
        *  If the trove's debt is larger than the deposited THUSD in the Stability Pool:
        *
        *  - Offset an amount of the trove's debt equal to the THUSD in the Stability Pool
        *  - Send a fraction of the trove's collateral to the Stability Pool, equal to the fraction of its offset debt
        *
        */
            debtToOffset = LiquityMath._min(_debt, _THUSDInStabPool);
            collToSendToSP = _coll * debtToOffset / _debt;
            debtToRedistribute = _debt - debtToOffset;
            collToRedistribute = _coll - collToSendToSP;
        } else {
            debtToOffset = 0;
            collToSendToSP = 0;
            debtToRedistribute = _debt;
            collToRedistribute = _coll;
        }
    }

    /*
    *  Get its offset coll/debt and collateral gas comp, and close the trove.
    */
    function _getCappedOffsetVals
    (
        uint256 _entireTroveDebt,
        uint256 _entireTroveColl,
        uint256 _price
    )
        internal
        pure
        returns (LiquidationValues memory singleLiquidation)
    {
        singleLiquidation.entireTroveDebt = _entireTroveDebt;
        singleLiquidation.entireTroveColl = _entireTroveColl;
        uint256 cappedCollPortion = _entireTroveDebt * MCR / _price;

        singleLiquidation.collGasCompensation = _getCollGasCompensation(cappedCollPortion);
        singleLiquidation.THUSDGasCompensation = THUSD_GAS_COMPENSATION;

        singleLiquidation.debtToOffset = _entireTroveDebt;
        singleLiquidation.collToSendToSP = cappedCollPortion - singleLiquidation.collGasCompensation;
        singleLiquidation.collSurplus = _entireTroveColl - cappedCollPortion;
        singleLiquidation.debtToRedistribute = 0;
        singleLiquidation.collToRedistribute = 0;
    }

    /*
    * Liquidate a sequence of troves. Closes a maximum number of n under-collateralized Troves,
    * starting from the one with the lowest collateral ratio in the system, and moving upwards
    */
    function liquidateTroves(uint256 _n) external override {
        ContractsCache memory contractsCache = ContractsCache(
            activePool,
            defaultPool,
            ITHUSDToken(address(0)),
            IPCV(address(0)),
            sortedTroves,
            ICollSurplusPool(address(0)),
            address(0)
        );
        IStabilityPool stabilityPoolCached = stabilityPool;

        LocalVariables_OuterLiquidationFunction memory vars;

        LiquidationTotals memory totals;

        vars.price = priceFeed.fetchPrice();
        vars.THUSDInStabPool = stabilityPoolCached.getTotalTHUSDDeposits();
        vars.recoveryModeAtStart = _checkRecoveryMode(vars.price);

        // Perform the appropriate liquidation sequence - tally the values, and obtain their totals
        if (vars.recoveryModeAtStart) {
            totals = _getTotalsFromLiquidateTrovesSequence_RecoveryMode(contractsCache, vars.price, vars.THUSDInStabPool, _n);
        } else { // if !vars.recoveryModeAtStart
            totals = _getTotalsFromLiquidateTrovesSequence_NormalMode(contractsCache.activePool, contractsCache.defaultPool, vars.price, vars.THUSDInStabPool, _n);
        }

        require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate");

        // Move liquidated collateral and THUSD to the appropriate pools
        stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP);
        _redistributeDebtAndColl(contractsCache.activePool, contractsCache.defaultPool, totals.totalDebtToRedistribute, totals.totalCollToRedistribute);
        if (totals.totalCollSurplus > 0) {
            contractsCache.activePool.sendCollateral(address(collSurplusPool), totals.totalCollSurplus);
        }

        // Update system snapshots
        _updateSystemSnapshots_excludeCollRemainder(contractsCache.activePool, totals.totalCollGasCompensation);

        vars.liquidatedDebt = totals.totalDebtInSequence;
        vars.liquidatedColl = totals.totalCollInSequence - totals.totalCollGasCompensation - totals.totalCollSurplus;
        emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalTHUSDGasCompensation);

        // Send gas compensation to caller
        _sendGasCompensation(contractsCache.activePool, msg.sender, totals.totalTHUSDGasCompensation, totals.totalCollGasCompensation);
    }

    /*
    * This function is used when the liquidateTroves sequence starts during Recovery Mode. However, it
    * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence
    */
    function _getTotalsFromLiquidateTrovesSequence_RecoveryMode
    (
        ContractsCache memory _contractsCache,
        uint256 _price,
        uint256 _THUSDInStabPool,
        uint256 _n
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;
        vars.backToNormalMode = false;
        vars.entireSystemDebt = getEntireSystemDebt();
        vars.entireSystemColl = getEntireSystemColl();

        vars.user = _contractsCache.sortedTroves.getLast();
        address firstUser = _contractsCache.sortedTroves.getFirst();
        for (vars.i = 0; vars.i < _n && vars.user != firstUser; vars.i++) {
            // we need to cache it, because current user is likely going to be deleted
            address nextUser = _contractsCache.sortedTroves.getPrev(vars.user);

            vars.ICR = getCurrentICR(vars.user, _price);

            if (!vars.backToNormalMode) {
                // Break the loop if ICR is greater than MCR and Stability Pool is empty
                if (vars.ICR >= MCR && vars.remainingTHUSDInStabPool == 0) { break; }

                uint256 TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price);

                singleLiquidation = _liquidateRecoveryMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.ICR, vars.remainingTHUSDInStabPool, TCR, _price);

                // Update aggregate trackers
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;
                vars.entireSystemDebt -= singleLiquidation.debtToOffset;
                vars.entireSystemColl -= singleLiquidation.collToSendToSP
                    + singleLiquidation.collGasCompensation
                    + singleLiquidation.collSurplus;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

                vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price);
            }
            else if (vars.backToNormalMode && vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_contractsCache.activePool, _contractsCache.defaultPool, vars.user, vars.remainingTHUSDInStabPool);

                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            }  else break;  // break if the loop reaches a Trove with ICR >= MCR

            vars.user = nextUser;
        }
    }

    function _getTotalsFromLiquidateTrovesSequence_NormalMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        uint256 _n
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;
        ISortedTroves sortedTrovesCached = sortedTroves;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;

        for (vars.i = 0; vars.i < _n; vars.i++) {
            vars.user = sortedTrovesCached.getLast();
            vars.ICR = getCurrentICR(vars.user, _price);

            if (vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);

                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            } else break;  // break if the loop reaches a Trove with ICR >= MCR
        }
    }

    /*
    * Attempt to liquidate a custom list of troves provided by the caller.
    */
    function batchLiquidateTroves(address[] memory _troveArray) public override {
        require(_troveArray.length != 0, "TroveManager: Calldata address array must not be empty");

        IActivePool activePoolCached = activePool;
        IDefaultPool defaultPoolCached = defaultPool;
        IStabilityPool stabilityPoolCached = stabilityPool;

        LocalVariables_OuterLiquidationFunction memory vars;
        LiquidationTotals memory totals;

        vars.price = priceFeed.fetchPrice();
        vars.THUSDInStabPool = stabilityPoolCached.getTotalTHUSDDeposits();
        vars.recoveryModeAtStart = _checkRecoveryMode(vars.price);

        // Perform the appropriate liquidation sequence - tally values and obtain their totals.
        if (vars.recoveryModeAtStart) {
            totals = _getTotalFromBatchLiquidate_RecoveryMode(activePoolCached, defaultPoolCached, vars.price, vars.THUSDInStabPool, _troveArray);
        } else {  //  if !vars.recoveryModeAtStart
            totals = _getTotalsFromBatchLiquidate_NormalMode(activePoolCached, defaultPoolCached, vars.price, vars.THUSDInStabPool, _troveArray);
        }

        require(totals.totalDebtInSequence > 0, "TroveManager: nothing to liquidate");

        // Move liquidated collateral and THUSD to the appropriate pools
        stabilityPoolCached.offset(totals.totalDebtToOffset, totals.totalCollToSendToSP);
        _redistributeDebtAndColl(activePoolCached, defaultPoolCached, totals.totalDebtToRedistribute, totals.totalCollToRedistribute);
        if (totals.totalCollSurplus > 0) {
            activePoolCached.sendCollateral(address(collSurplusPool), totals.totalCollSurplus);
        }

        // Update system snapshots
        _updateSystemSnapshots_excludeCollRemainder(activePoolCached, totals.totalCollGasCompensation);

        vars.liquidatedDebt = totals.totalDebtInSequence;
        vars.liquidatedColl = totals.totalCollInSequence - totals.totalCollGasCompensation - totals.totalCollSurplus;
        emit Liquidation(vars.liquidatedDebt, vars.liquidatedColl, totals.totalCollGasCompensation, totals.totalTHUSDGasCompensation);

        // Send gas compensation to caller
        _sendGasCompensation(activePoolCached, msg.sender, totals.totalTHUSDGasCompensation, totals.totalCollGasCompensation);
    }

    /*
    * This function is used when the batch liquidation sequence starts during Recovery Mode. However, it
    * handle the case where the system *leaves* Recovery Mode, part way through the liquidation sequence
    */
    function _getTotalFromBatchLiquidate_RecoveryMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        address[] memory _troveArray
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;
        vars.backToNormalMode = false;
        vars.entireSystemDebt = getEntireSystemDebt();
        vars.entireSystemColl = getEntireSystemColl();

        for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
            vars.user = _troveArray[vars.i];
            // Skip non-active troves
            if (Troves[vars.user].status != Status.active) { continue; }
            vars.ICR = getCurrentICR(vars.user, _price);

            if (!vars.backToNormalMode) {

                // Skip this trove if ICR is greater than MCR and Stability Pool is empty
                if (vars.ICR >= MCR && vars.remainingTHUSDInStabPool == 0) { continue; }

                uint256 TCR = LiquityMath._computeCR(vars.entireSystemColl, vars.entireSystemDebt, _price);

                singleLiquidation = _liquidateRecoveryMode(_activePool, _defaultPool, vars.user, vars.ICR, vars.remainingTHUSDInStabPool, TCR, _price);

                // Update aggregate trackers
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;
                vars.entireSystemDebt -= singleLiquidation.debtToOffset;
                vars.entireSystemColl -= singleLiquidation.collToSendToSP
                    + singleLiquidation.collGasCompensation
                    + singleLiquidation.collSurplus;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

                vars.backToNormalMode = !_checkPotentialRecoveryMode(vars.entireSystemColl, vars.entireSystemDebt, _price);
            }

            else if (vars.backToNormalMode && vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);

            } else continue; // In Normal Mode skip troves with ICR >= MCR
        }
    }

    function _getTotalsFromBatchLiquidate_NormalMode
    (
        IActivePool _activePool,
        IDefaultPool _defaultPool,
        uint256 _price,
        uint256 _THUSDInStabPool,
        address[] memory _troveArray
    )
        internal
        returns(LiquidationTotals memory totals)
    {
        LocalVariables_LiquidationSequence memory vars;
        LiquidationValues memory singleLiquidation;

        vars.remainingTHUSDInStabPool = _THUSDInStabPool;

        for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
            vars.user = _troveArray[vars.i];
            vars.ICR = getCurrentICR(vars.user, _price);

            if (vars.ICR < MCR) {
                singleLiquidation = _liquidateNormalMode(_activePool, _defaultPool, vars.user, vars.remainingTHUSDInStabPool);
                vars.remainingTHUSDInStabPool -= singleLiquidation.debtToOffset;

                // Add liquidation values to their respective running totals
                totals = _addLiquidationValuesToTotals(totals, singleLiquidation);
            }
        }
    }

    // --- Liquidation helper functions ---

    function _addLiquidationValuesToTotals(LiquidationTotals memory oldTotals, LiquidationValues memory singleLiquidation)
    internal pure returns(LiquidationTotals memory newTotals) {

        // Tally all the values with their respective running totals
        newTotals.totalCollGasCompensation = oldTotals.totalCollGasCompensation + singleLiquidation.collGasCompensation;
        newTotals.totalTHUSDGasCompensation = oldTotals.totalTHUSDGasCompensation + singleLiquidation.THUSDGasCompensation;
        newTotals.totalDebtInSequence = oldTotals.totalDebtInSequence + singleLiquidation.entireTroveDebt;
        newTotals.totalCollInSequence = oldTotals.totalCollInSequence + singleLiquidation.entireTroveColl;
        newTotals.totalDebtToOffset = oldTotals.totalDebtToOffset + singleLiquidation.debtToOffset;
        newTotals.totalCollToSendToSP = oldTotals.totalCollToSendToSP + singleLiquidation.collToSendToSP;
        newTotals.totalDebtToRedistribute = oldTotals.totalDebtToRedistribute + singleLiquidation.debtToRedistribute;
        newTotals.totalCollToRedistribute = oldTotals.totalCollToRedistribute + singleLiquidation.collToRedistribute;
        newTotals.totalCollSurplus = oldTotals.totalCollSurplus + singleLiquidation.collSurplus;

        return newTotals;
    }

    function _sendGasCompensation(IActivePool _activePool, address _liquidator, uint256 _THUSD, uint256 _collateral) internal {
        if (_THUSD > 0) {
            IGasPool(gasPoolAddress).sendTHUSD(_liquidator, _THUSD);
        }

        if (_collateral > 0) {
            _activePool.sendCollateral(_liquidator, _collateral);
        }
    }

    // Move a Trove's pending debt and collateral rewards from distributions, from the Default Pool to the Active Pool
    function _movePendingTroveRewardsToActivePool(IActivePool _activePool, IDefaultPool _defaultPool, uint256 _THUSD, uint256 _collateral) internal {
        _defaultPool.decreaseTHUSDDebt(_THUSD);
        _activePool.increaseTHUSDDebt(_THUSD);
        _defaultPool.sendCollateralToActivePool(_collateral);
    }

    // --- Redemption functions ---

    // Redeem as much collateral as possible from _borrower's Trove in exchange for THUSD up to _maxTHUSDamount
    function _redeemCollateralFromTrove(
        ContractsCache memory _contractsCache,
        address _borrower,
        uint256 _maxTHUSDamount,
        uint256 _price,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR
    )
        internal returns (SingleRedemptionValues memory singleRedemption)
    {
        // Determine the remaining amount (lot) to be redeemed, capped by the entire debt of the Trove minus the liquidation reserve
        singleRedemption.THUSDLot = LiquityMath._min(_maxTHUSDamount, Troves[_borrower].debt - THUSD_GAS_COMPENSATION);

        // Get the collateralLot of equivalent value in USD
        singleRedemption.collateralLot = singleRedemption.THUSDLot * DECIMAL_PRECISION / _price;

        // Decrease the debt and collateral of the current Trove according to the THUSD lot and corresponding collateral to send
        uint256 newDebt = Troves[_borrower].debt - singleRedemption.THUSDLot;
        uint256 newColl = Troves[_borrower].coll - singleRedemption.collateralLot;

        if (newDebt == THUSD_GAS_COMPENSATION) {
            // No debt left in the Trove (except for the liquidation reserve), therefore the trove gets closed
            _removeStake(_borrower);
            _closeTrove(_borrower, Status.closedByRedemption);
            _redeemCloseTrove(_contractsCache, _borrower, THUSD_GAS_COMPENSATION, newColl);
            emit TroveUpdated(_borrower, 0, 0, 0, TroveManagerOperation.redeemCollateral);

        } else {
            uint256 newNICR = LiquityMath._computeNominalCR(newColl, newDebt);

            /*
            * If the provided hint is out of date, we bail since trying to reinsert without a good hint will almost
            * certainly result in running out of gas.
            *
            * If the resultant net debt of the partial is less than the minimum, net debt we bail.
            */
            if (newNICR != _partialRedemptionHintNICR || _getNetDebt(newDebt) < MIN_NET_DEBT) {
                singleRedemption.cancelledPartial = true;
                return singleRedemption;
            }

            _contractsCache.sortedTroves.reInsert(_borrower, newNICR, _upperPartialRedemptionHint, _lowerPartialRedemptionHint);

            Troves[_borrower].debt = newDebt;
            Troves[_borrower].coll = newColl;
            _updateStakeAndTotalStakes(_borrower);

            emit TroveUpdated(
                _borrower,
                newDebt, newColl,
                Troves[_borrower].stake,
                TroveManagerOperation.redeemCollateral
            );
        }

        return singleRedemption;
    }

    /*
    * Called when a full redemption occurs, and closes the trove.
    * The redeemer swaps (debt - liquidation reserve) THUSD for (debt - liquidation reserve) worth of collateral, so the THUSD liquidation reserve left corresponds to the remaining debt.
    * In order to close the trove, the THUSD liquidation reserve is burned, and the corresponding debt is removed from the active pool.
    * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove.
    * Any surplus collateral left in the trove, is sent to the Coll surplus pool, and can be later claimed by the borrower.
    */
    function _redeemCloseTrove(ContractsCache memory _contractsCache, address _borrower, uint256 _THUSD, uint256 _collateral) internal {
        _contractsCache.thusdToken.burn(gasPoolAddress, _THUSD);
        // Update Active Pool THUSD, and send collateral to account
        _contractsCache.activePool.decreaseTHUSDDebt(_THUSD);

        // send collateral from Active Pool to CollSurplus Pool
        _contractsCache.collSurplusPool.accountSurplus(_borrower, _collateral);
        _contractsCache.activePool.sendCollateral(address(_contractsCache.collSurplusPool), _collateral);
    }

    function _isValidFirstRedemptionHint(ISortedTroves _sortedTroves, address _firstRedemptionHint, uint256 _price) internal view returns (bool) {
        if (_firstRedemptionHint == address(0) ||
            !_sortedTroves.contains(_firstRedemptionHint) ||
            getCurrentICR(_firstRedemptionHint, _price) < MCR
        ) {
            return false;
        }

        address nextTrove = _sortedTroves.getNext(_firstRedemptionHint);
        return nextTrove == address(0) || getCurrentICR(nextTrove, _price) < MCR;
    }

    /* Send _THUSDamount THUSD to the system and redeem the corresponding amount of collateral from as many Troves as are needed to fill the redemption
    * request.  Applies pending rewards to a Trove before reducing its debt and coll.
    *
    * Note that if _amount is very large, this function can run out of gas, specially if traversed troves are small. This can be easily avoided by
    * splitting the total _amount in appropriate chunks and calling the function multiple times.
    *
    * Param `_maxIterations` can also be provided, so the loop through Troves is capped (if it’s zero, it will be ignored).This makes it easier to
    * avoid OOG for the frontend, as only knowing approximately the average cost of an iteration is enough, without needing to know the “topology”
    * of the trove list. It also avoids the need to set the cap in stone in the contract, nor doing gas calculations, as both gas price and opcode
    * costs can vary.
    *
    * All Troves that are redeemed from -- with the likely exception of the last one -- will end up with no debt left, therefore they will be closed.
    * If the last Trove does have some remaining debt, it has a finite ICR, and the reinsertion could be anywhere in the list, therefore it requires a hint.
    * A frontend should use getRedemptionHints() to calculate what the ICR of this Trove will be after redemption, and pass a hint for its position
    * in the sortedTroves list along with the ICR value that the hint was found for.
    *
    * If another transaction modifies the list between calling getRedemptionHints() and passing the hints to redeemCollateral(), it
    * is very likely that the last (partially) redeemed Trove would end up with a different ICR than what the hint is for. In this case the
    * redemption will stop after the last completely redeemed Trove and the sender will keep the remaining THUSD amount, which they can attempt
    * to redeem later.
    */
    function redeemCollateral(
        uint256 _THUSDamount,
        address _firstRedemptionHint,
        address _upperPartialRedemptionHint,
        address _lowerPartialRedemptionHint,
        uint256 _partialRedemptionHintNICR,
        uint256 _maxIterations,
        uint256 _maxFeePercentage
    )
        external
        override
    {
        ContractsCache memory contractsCache = ContractsCache(
            activePool,
            defaultPool,
            thusdToken,
            pcv,
            sortedTroves,
            collSurplusPool,
            gasPoolAddress
        );
        RedemptionTotals memory totals;

        _requireValidMaxFeePercentage(_maxFeePercentage);
        totals.price = priceFeed.fetchPrice();
        _requireTCRoverMCR(totals.price);
        _requireAmountGreaterThanZero(_THUSDamount);
        _requireTHUSDBalanceCoversRedemption(contractsCache.thusdToken, msg.sender, _THUSDamount);

        totals.totalTHUSDDebtAtStart = getEntireSystemDebt();
        totals.remainingTHUSD = _THUSDamount;
        address currentBorrower;

        if (_isValidFirstRedemptionHint(contractsCache.sortedTroves, _firstRedemptionHint, totals.price)) {
            currentBorrower = _firstRedemptionHint;
        } else {
            currentBorrower = contractsCache.sortedTroves.getLast();
            // Find the first trove with ICR >= MCR
            while (currentBorrower != address(0) && getCurrentICR(currentBorrower, totals.price) < MCR) {
                currentBorrower = contractsCache.sortedTroves.getPrev(currentBorrower);
            }
        }

        // Loop through the Troves starting from the one with lowest collateral ratio until _amount of THUSD is exchanged for collateral
        if (_maxIterations == 0) { _maxIterations = type(uint256).max; }
        while (currentBorrower != address(0) && totals.remainingTHUSD > 0 && _maxIterations > 0) {
            _maxIterations--;
            // Save the address of the Trove preceding the current one, before potentially modifying the list
            address nextUserToCheck = contractsCache.sortedTroves.getPrev(currentBorrower);

            _applyPendingRewards(contractsCache.activePool, contractsCache.defaultPool, currentBorrower);

            SingleRedemptionValues memory singleRedemption = _redeemCollateralFromTrove(
                contractsCache,
                currentBorrower,
                totals.remainingTHUSD,
                totals.price,
                _upperPartialRedemptionHint,
                _lowerPartialRedemptionHint,
                _partialRedemptionHintNICR
            );

            if (singleRedemption.cancelledPartial) break; // Partial redemption was cancelled (out-of-date hint, or new net debt < minimum), therefore we could not redeem from the last Trove

            totals.totalTHUSDToRedeem  += singleRedemption.THUSDLot;
            totals.totalCollateralDrawn += singleRedemption.collateralLot;

            totals.remainingTHUSD -= singleRedemption.THUSDLot;
            currentBorrower = nextUserToCheck;
        }
        require(totals.totalCollateralDrawn > 0, "TroveManager: Unable to redeem any amount");

        // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.
        // Use the saved total THUSD supply value, from before it was reduced by the redemption.
        _updateBaseRateFromRedemption(totals.totalCollateralDrawn, totals.price, totals.totalTHUSDDebtAtStart);

        // Calculate the collateral fee
        totals.collateralFee = _getRedemptionFee(totals.totalCollateralDrawn);

        _requireUserAcceptsFee(totals.collateralFee, totals.totalCollateralDrawn, _maxFeePercentage);

        // Send the collateral fee to the PCV contract
        contractsCache.activePool.sendCollateral(address(contractsCache.pcv), totals.collateralFee);

        totals.collateralToSendToRedeemer = totals.totalCollateralDrawn - totals.collateralFee;

        emit Redemption(_THUSDamount, totals.totalTHUSDToRedeem, totals.totalCollateralDrawn, totals.collateralFee);

        // Burn the total THUSD that is cancelled with debt, and send the redeemed collateral to msg.sender
        contractsCache.thusdToken.burn(msg.sender, totals.totalTHUSDToRedeem);
        // Update Active Pool THUSD, and send collateral to account
        contractsCache.activePool.decreaseTHUSDDebt(totals.totalTHUSDToRedeem);
        contractsCache.activePool.sendCollateral(msg.sender, totals.collateralToSendToRedeemer);
    }

    // --- Helper functions ---

    // Return the nominal collateral ratio (ICR) of a given Trove, without the price. Takes a trove's pending coll and debt rewards from redistributions into account.
    function getNominalICR(address _borrower) public view override returns (uint) {
        (uint256 currentCollateral, uint256 currentTHUSDDebt) = _getCurrentTroveAmounts(_borrower);

        uint256 NICR = LiquityMath._computeNominalCR(currentCollateral, currentTHUSDDebt);
        return NICR;
    }

    // Return the current collateral ratio (ICR) of a given Trove. Takes a trove's pending coll and debt rewards from redistributions into account.
    function getCurrentICR(address _borrower, uint256 _price) public view override returns (uint) {
        (uint256 currentCollateral, uint256 currentTHUSDDebt) = _getCurrentTroveAmounts(_borrower);

        uint256 ICR = LiquityMath._computeCR(currentCollateral, currentTHUSDDebt, _price);
        return ICR;
    }

    function _getCurrentTroveAmounts(address _borrower) internal view returns (uint, uint) {
        uint256 pendingCollateralReward = getPendingCollateralReward(_borrower);
        uint256 pendingTHUSDDebtReward = getPendingTHUSDDebtReward(_borrower);

        uint256 currentCollateral = Troves[_borrower].coll + pendingCollateralReward;
        uint256 currentTHUSDDebt = Troves[_borrower].debt + pendingTHUSDDebtReward;

        return (currentCollateral, currentTHUSDDebt);
    }

    function applyPendingRewards(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
        return _applyPendingRewards(activePool, defaultPool, _borrower);
    }

    // Add the borrowers's coll and debt rewards earned from redistributions, to their Trove
    function _applyPendingRewards(IActivePool _activePool, IDefaultPool _defaultPool, address _borrower) internal {
        if (hasPendingRewards(_borrower)) {
            _requireTroveIsActive(_borrower);

            // Compute pending rewards
            uint256 pendingCollateralReward = getPendingCollateralReward(_borrower);
            uint256 pendingTHUSDDebtReward = getPendingTHUSDDebtReward(_borrower);

            // Apply pending rewards to trove's state
            Troves[_borrower].coll += pendingCollateralReward;
            Troves[_borrower].debt += pendingTHUSDDebtReward;

            _updateTroveRewardSnapshots(_borrower);

            // Transfer from DefaultPool to ActivePool
            _movePendingTroveRewardsToActivePool(_activePool, _defaultPool, pendingTHUSDDebtReward, pendingCollateralReward);

            emit TroveUpdated(
                _borrower,
                Troves[_borrower].debt,
                Troves[_borrower].coll,
                Troves[_borrower].stake,
                TroveManagerOperation.applyPendingRewards
            );
        }
    }

    // Update borrower's snapshots of L_Collateral and L_THUSDDebt to reflect the current values
    function updateTroveRewardSnapshots(address _borrower) external override {
        _requireCallerIsBorrowerOperations();
       return _updateTroveRewardSnapshots(_borrower);
    }

    function _updateTroveRewardSnapshots(address _borrower) internal {
        rewardSnapshots[_borrower].collateral = L_Collateral;
        rewardSnapshots[_borrower].THUSDDebt = L_THUSDDebt;
        emit TroveSnapshotsUpdated(L_Collateral, L_THUSDDebt);
    }

    // Get the borrower's pending accumulated collateral reward, earned by their stake
    function getPendingCollateralReward(address _borrower) public view override returns (uint) {
        uint256 snapshotCollateral = rewardSnapshots[_borrower].collateral;
        uint256 rewardPerUnitStaked = L_Collateral - snapshotCollateral;

        if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; }

        uint256 stake = Troves[_borrower].stake;

        uint256 pendingCollateralReward = stake * rewardPerUnitStaked / DECIMAL_PRECISION;

        return pendingCollateralReward;
    }

    // Get the borrower's pending accumulated THUSD reward, earned by their stake
    function getPendingTHUSDDebtReward(address _borrower) public view override returns (uint) {
        uint256 snapshotTHUSDDebt = rewardSnapshots[_borrower].THUSDDebt;
        uint256 rewardPerUnitStaked = L_THUSDDebt - snapshotTHUSDDebt;

        if ( rewardPerUnitStaked == 0 || Troves[_borrower].status != Status.active) { return 0; }

        uint256 stake =  Troves[_borrower].stake;

        uint256 pendingTHUSDDebtReward = stake * rewardPerUnitStaked / DECIMAL_PRECISION;

        return pendingTHUSDDebtReward;
    }

    function hasPendingRewards(address _borrower) public view override returns (bool) {
        /*
        * A Trove has pending rewards if its snapshot is less than the current rewards per-unit-staked sum:
        * this indicates that rewards have occured since the snapshot was made, and the user therefore has
        * pending rewards
        */
        if (Troves[_borrower].status != Status.active) {return false;}

        return (rewardSna...

// [truncated — 67966 bytes total]
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;
}
IGasPool.sol 13 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;


interface IGasPool {
    // --- Events ---
    event TroveManagerAddressChanged(address _newTroveManagerAddress);
    event THUSDTokenAddressChanged(address _thusdTokenAddress);

    // --- Functions ---
    function sendTHUSD(address _account, uint256 _amount) external;
}
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");
    }
}
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);
}
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);
}
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

BETA 0x071a7541 → uint256
BORROWING_FEE_FLOOR 0xf92d3433 → uint256
CCR 0x5733d58f → uint256
DECIMAL_PRECISION 0xa20baee6 → uint256
L_Collateral 0xee266b87 → uint256
L_THUSDDebt 0xa68b663e → uint256
MAX_BORROWING_FEE 0x24092669 → uint256
MCR 0x794e5724 → uint256
MINUTE_DECAY_FACTOR 0xc7b55481 → uint256
MIN_NET_DEBT 0x1bf43555 → uint256
NAME 0xa3f4df7e → string
PERCENT_DIVISOR 0x4870dd9a → uint256
REDEMPTION_FEE_FLOOR 0x28d28b5b → uint256
THUSD_GAS_COMPENSATION 0x1fd6a434 → uint256
TroveOwners 0x756b253e → address
Troves 0x6ef64338 → uint256, uint256, uint256, uint8, uint128
_100pct 0x72fe25aa → uint256
activePool 0x7f7dde4a → address
baseRate 0x1f68f20a → uint256
borrowerOperationsAddress 0xb7f8cf9b → address
checkRecoveryMode 0x4e443d9e → bool
defaultPool 0x3cc74225 → address
getBorrowingFee 0x631203b0 → uint256
getBorrowingFeeWithDecay 0x477d66cf → uint256
getBorrowingRate 0xf36b2425 → uint256
getBorrowingRateWithDecay 0x66ca4a21 → uint256
getCurrentICR 0xd293c710 → uint256
getEntireDebtAndColl 0xb91af97c → uint256, uint256, uint256, uint256
getEntireSystemColl 0x887105d3 → uint256
getEntireSystemDebt 0x795d26c3 → uint256
getNominalICR 0xb0d8e181 → uint256
getPendingCollateralReward 0x4a3c95c4 → uint256
getPendingTHUSDDebtReward 0x9ba7a408 → uint256
getRedemptionFeeWithDecay 0xd5b35635 → uint256
getRedemptionRate 0x2b11551a → uint256
getRedemptionRateWithDecay 0xc52861f2 → uint256
getTCR 0xb82f263d → uint256
getTroveColl 0x480cd578 → uint256
getTroveDebt 0xd66a2553 → uint256
getTroveFromTroveOwnersArray 0xd9a72444 → address
getTroveOwnersCount 0x49eefeee → uint256
getTroveStake 0x64cee260 → uint256
getTroveStatus 0x21e37801 → uint8
hasPendingRewards 0xe2ac77b0 → bool
isOwner 0x8f32d59b → bool
lastCollateralError_Redistribution 0xd38b0558 → uint256
lastFeeOperationTime 0xd380a37c → uint256
lastTHUSDDebtError_Redistribution 0xe6a91f16 → uint256
owner 0x8da5cb5b → address
pcv 0x4597f6ed → address
priceFeed 0x741bef1a → address
rewardSnapshots 0x1673c79a → uint256, uint256
sortedTroves 0xae918754 → address
stabilityPool 0x048c661d → address
thusdToken 0x77e16f1e → address
totalCollateralSnapshot 0x96d711ff → uint256
totalStakes 0xbf9befb1 → uint256
totalStakesSnapshot 0x807d138d → uint256

Write Contract 18 functions

These functions modify contract state and require a wallet transaction to execute.

addTroveOwnerToArray 0x15d549f1
address _borrower
returns: uint256
applyPendingRewards 0x0b076557
address _borrower
batchLiquidateTroves 0x1e8b1c2b
address[] _troveArray
closeTrove 0xcbd138ae
address _borrower
decayBaseRateFromBorrowing 0x5dba4c4a
No parameters
decreaseTroveColl 0xd3d6f843
address _borrower
uint256 _collDecrease
returns: uint256
decreaseTroveDebt 0x12610e92
address _borrower
uint256 _debtDecrease
returns: uint256
increaseTroveColl 0x72423c17
address _borrower
uint256 _collIncrease
returns: uint256
increaseTroveDebt 0x9976cf45
address _borrower
uint256 _debtIncrease
returns: uint256
liquidate 0x2f865568
address _borrower
liquidateTroves 0x653d46e7
uint256 _n
redeemCollateral 0xbcd37526
uint256 _THUSDamount
address _firstRedemptionHint
address _upperPartialRedemptionHint
address _lowerPartialRedemptionHint
uint256 _partialRedemptionHintNICR
uint256 _maxIterations
uint256 _maxFeePercentage
removeStake 0xfe2ba848
address _borrower
setAddresses 0x6c37a4af
address _borrowerOperationsAddress
address _activePoolAddress
address _defaultPoolAddress
address _stabilityPoolAddress
address _gasPoolAddress
address _collSurplusPoolAddress
address _priceFeedAddress
address _thusdTokenAddress
address _sortedTrovesAddress
address _pcvAddress
setTroveStatus 0x953f0bb1
address _borrower
uint8 _status
transferOwnership 0xf2fde38b
address newOwner
updateStakeAndTotalStakes 0x18f2817a
address _borrower
returns: uint256
updateTroveRewardSnapshots 0x82fe3eb9
address _borrower

Recent Transactions

No transactions found for this address