Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xE64b336B5dF8318Fa485A1a96AF5f8a553555285
Balance 0 ETH
Nonce 1
Code Size 14312 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

14312 bytes
0x608060405234801561001057600080fd5b50600436106102485760003560e01c80638da5cb5b1161013b578063d7c41c79116100b8578063f2fde38b1161007c578063f2fde38b14610719578063f3fef3a31461073f578063fa09e6301461076b578063fb93210814610791578063fea097d8146107bd57610248565b8063d7c41c7914610668578063dd035223146106b4578063e36a6b83146106d7578063e4830153146106f4578063f11f77f91461071157610248565b8063b6b55f25116100ff578063b6b55f25146105ee578063b76719b81461060b578063c1a70d1414610613578063c9f670721461061b578063cdd78cfc1461066057610248565b80638da5cb5b1461058a578063a293da0f14610592578063ad3e942f146105b8578063b2cc1739146105c0578063b42652e9146105c857610248565b80634bde38c8116101c95780636a4237271161018d5780636a423727146104c5578063715018a6146104cd57806374b2e90b146104d5578063794dca89146104dd5780637d1c58191461058257610248565b80634bde38c8146104165780635ef533291461041e57806362f384ad1461043b5780636386c1c71461046157806365728728146104a057610248565b80632ba9b8da116102105780632ba9b8da146103495780632f4f21e21461036657806335c807e6146103925780633af32abf146103b85780633fb266fc146103f257610248565b8063018ee9b71461024d5780630c340a241461028b5780631959a002146102af5780631e83409a14610319578063262d615214610341575b600080fd5b6102796004803603604081101561026357600080fd5b506001600160a01b0381351690602001356107c5565b60408051918252519081900360200190f35b610293610ace565b604080516001600160a01b039092168252519081900360200190f35b6102d5600480360360208110156102c557600080fd5b50356001600160a01b0316610add565b604080516001600160801b0395861681529390941660208401526001600160c01b039091168284015267ffffffffffffffff16606082015290519081900360800190f35b61033f6004803603602081101561032f57600080fd5b50356001600160a01b0316610b27565b005b610293610b3c565b61033f6004803603602081101561035f57600080fd5b5035610b4b565b61033f6004803603604081101561037c57600080fd5b506001600160a01b038135169060200135610c4f565b61033f600480360360208110156103a857600080fd5b50356001600160a01b0316610cca565b6103de600480360360208110156103ce57600080fd5b50356001600160a01b0316610ddb565b604080519115158252519081900360200190f35b6103fa610df0565b604080516001600160801b039092168252519081900360200190f35b610293610dff565b61033f6004803603602081101561043457600080fd5b5035610e0e565b61033f6004803603602081101561045157600080fd5b50356001600160a01b0316610eb9565b6104876004803603602081101561047757600080fd5b50356001600160a01b0316610fce565b6040805192835260208301919091528051918290030190f35b6104a86110f1565b6040805167ffffffffffffffff9092168252519081900360200190f35b610279611108565b61033f61110e565b6104a86111ba565b61033f600480360360408110156104f357600080fd5b81019060208101813564010000000081111561050e57600080fd5b82018360208201111561052057600080fd5b8035906020019184602083028401116401000000008311171561054257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050505035151590506111d1565b61033f61136d565b610293611377565b61033f600480360360208110156105a857600080fd5b50356001600160a01b0316611386565b61029361148d565b6103fa61149c565b61033f600480360360208110156105de57600080fd5b50356001600160a01b03166114ab565b61033f6004803603602081101561060457600080fd5b50356114d8565b6103fa61154f565b610279611565565b61062361156b565b604080516001600160801b03909516855263ffffffff909316602085015265ffffffffffff91821684840152166060830152519081900360800190f35b6102796115a3565b61033f600480360360c081101561067e57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a001356115a9565b61033f600480360360208110156106ca57600080fd5b503563ffffffff166118b9565b61033f600480360360208110156106ed57600080fd5b5035611983565b61033f6004803603602081101561070a57600080fd5b5035611a72565b610279611b61565b61033f6004803603602081101561072f57600080fd5b50356001600160a01b0316611b67565b61033f6004803603604081101561075557600080fd5b506001600160a01b038135169060200135611c6a565b61033f6004803603602081101561078157600080fd5b50356001600160a01b0316611cd2565b61033f600480360360408110156107a757600080fd5b506001600160a01b038135169060200135611cff565b610279611dde565b336000908152606a602052604081205460ff16610825576040805162461bcd60e51b8152602060048201526019602482015278119d5c9b9858d94e881bdb9b1e481dda1a5d195b1a5cdd1959603a1b604482015290519081900360640190fd5b6040805163a4698feb60e01b8152600060048201819052915173cf50b810e57ac33b91dcf525c6ddd9881b1393329263a4698feb926024808201939182900301818387803b15801561087657600080fd5b505af115801561088a573d6000803e3d6000fd5b5050604080516370a0823160e01b81523060048201529051600093507362b9c7356a2dc64a1969e19c23e4f579f9810aa792506370a0823191602480820192602092909190829003018186803b1580156108e357600080fd5b505afa1580156108f7573d6000803e3d6000fd5b505050506040513d602081101561090d57600080fd5b5051905080156109fb57606d54610943907362b9c7356a2dc64a1969e19c23e4f579f9810aa7906001600160a01b031683611f95565b606d54604080516349df439160e01b81527362b9c7356a2dc64a1969e19c23e4f579f9810aa7600482015260248101849052734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b60448201526064810186905290516001600160a01b03909216916349df4391916084808201926020929091908290030181600087803b1580156109cc57600080fd5b505af11580156109e0573d6000803e3d6000fd5b505050506040513d60208110156109f657600080fd5b505190505b60408051828152905133917fc9695243a805adb74c91f28311176c65b417e842d5699893cef56d18bfa48cba919081900360200190a28015610ac557606e5481908015610a8157633b9aca008183026070549190049150610a7b90734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b906001600160a01b031683611f95565b80820391505b606f548015610ab757633b9aca00908302049182900391610ab7734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8883611f95565b610ac13084611fe7565b5050505b90505b92915050565b6065546001600160a01b031681565b606960205260009081526040902080546001909101546001600160801b0380831692600160801b900416906001600160c01b03811690600160c01b900467ffffffffffffffff1684565b610b303361234d565b610b39816125ad565b50565b606d546001600160a01b031681565b6065546001600160a01b0316331480610b7c5750610b67611377565b6001600160a01b0316336001600160a01b0316145b610bbb576040805162461bcd60e51b815260206004820152601f60248201526000805160206136c4833981519152604482015290519081900360640190fd5b633b9aca00811115610c14576040805162461bcd60e51b815260206004820152601d60248201527f4675726e6163653a2070657263656e7461676520746f6f206c61726765000000604482015290519081900360640190fd5b606b8190556040805182815290517f85f120208ed619cf9517b5fb6dfd9fad97a798fe695ec83380f10e2d8b44e9619181900360200190a150565b60008111610ca4576040805162461bcd60e51b815260206004820152601d60248201527f4675726e6163653a206465706f736974207a65726f20636c6576435658000000604482015290519081900360640190fd5b606654610cbc906001600160a01b03163330846127db565b610cc68282612835565b5050565b6065546001600160a01b0316331480610cfb5750610ce6611377565b6001600160a01b0316336001600160a01b0316145b610d3a576040805162461bcd60e51b815260206004820152601f60248201526000805160206136c4833981519152604482015290519081900360640190fd5b6001600160a01b038116610d91576040805162461bcd60e51b81526020600482015260196024820152784675726e6163653a207a65726f207a6170206164647265737360381b604482015290519081900360640190fd5b606d80546001600160a01b0319166001600160a01b0383169081179091556040517f03e59dbc22b06c47327d520cddc8bf2923ac525a1742732bf344562d7f72d0f590600090a250565b606a6020526000908152604090205460ff1681565b6067546001600160801b031681565b6070546001600160a01b031681565b6065546001600160a01b0316331480610e3f5750610e2a611377565b6001600160a01b0316336001600160a01b0316145b610e7e576040805162461bcd60e51b815260206004820152601f60248201526000805160206136c4833981519152604482015290519081900360640190fd5b606c8190556040805182815290517fe4a4b5c920e5332c619026c18ed306896516f6c8f4a2179ef37f364a452c1d839181900360200190a150565b6065546001600160a01b0316331480610eea5750610ed5611377565b6001600160a01b0316336001600160a01b0316145b610f29576040805162461bcd60e51b815260206004820152601f60248201526000805160206136c4833981519152604482015290519081900360640190fd5b6001600160a01b038116610f84576040805162461bcd60e51b815260206004820152601e60248201527f4675726e6163653a207a65726f20676f7665726e6f7220616464726573730000604482015290519081900360640190fd5b606580546001600160a01b0319166001600160a01b0383169081179091556040517f49e8ccdfbc26d458bf9e9a05311a53187820bf3c43fc5da574ddfb40e871353990600090a250565b6001600160a01b0381166000908152606960209081526040808320815160808101835281546001600160801b038082168352600160801b9091041693810193909352600101546001600160c01b0381169183019190915267ffffffffffffffff600160c01b918290048116606084018190526068548594939004909116111561106d576020810151905160009350016001600160801b031690506110ec565b60006110a86110a383600001516001600160801b0316606860009054906101000a90046001600160801b031685604001516129d3565b612a6c565b600101905081600001516001600160801b0316816001600160801b0316106110ce575080515b602082015191516001600160801b0380831695509190039091011690505b915091565b606854600160801b900467ffffffffffffffff1681565b606f5481565b611116612abe565b6001600160a01b0316611127611377565b6001600160a01b031614611170576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b606854600160c01b900467ffffffffffffffff1681565b6111d9612abe565b6001600160a01b03166111ea611377565b6001600160a01b031614611233576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b60005b82518110156113685760006001600160a01b031683828151811061125657fe5b60200260200101516001600160a01b031614156112ba576040805162461bcd60e51b815260206004820152601f60248201527f4675726e6163653a207a65726f2077686974656c697374206164647265737300604482015290519081900360640190fd5b81606a60008584815181106112cb57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555082818151811061131657fe5b60200260200101516001600160a01b03167f08b2c0469ecd1d7a21d7e1492f0fc75fc7e8e0fa4fdf4275949c90875f5ebdf58360405180821515815260200191505060405180910390a2600101611236565b505050565b611375612ac2565b565b6033546001600160a01b031690565b61138e612abe565b6001600160a01b031661139f611377565b6001600160a01b0316146113e8576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b6001600160a01b038116611443576040805162461bcd60e51b815260206004820152601e60248201527f4675726e6163653a207a65726f20706c6174666f726d20616464726573730000604482015290519081900360640190fd5b607080546001600160a01b0319166001600160a01b0383169081179091556040517f43412ddbc9c884a0be720e21657a878716d21710438ad24f74f7e99699df82fc90600090a250565b6066546001600160a01b031681565b6068546001600160801b031681565b6114b43361234d565b33600090815260696020526040902054610b309082906001600160801b0316612c06565b6000811161152d576040805162461bcd60e51b815260206004820152601d60248201527f4675726e6163653a206465706f736974207a65726f20636c6576435658000000604482015290519081900360640190fd5b606654611545906001600160a01b03163330846127db565b610b393382612835565b606754600160801b90046001600160801b031681565b606b5481565b6071546001600160801b0381169063ffffffff600160801b8204169065ffffffffffff600160a01b8204811691600160d01b90041684565b606e5481565b600054610100900460ff16806115c257506115c2612d18565b806115d0575060005460ff16155b61160b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806136e4602e913960400191505060405180910390fd5b600054610100900460ff16158015611636576000805460ff1961ff0019909116610100171660011790555b61163e612d29565b6001600160a01b038716611699576040805162461bcd60e51b815260206004820152601e60248201527f4675726e6163653a207a65726f20676f7665726e6f7220616464726573730000604482015290519081900360640190fd5b6001600160a01b0386166116f4576040805162461bcd60e51b815260206004820152601d60248201527f4675726e6163653a207a65726f20636c65764356582061646472657373000000604482015290519081900360640190fd5b6001600160a01b03851661174b576040805162461bcd60e51b81526020600482015260196024820152784675726e6163653a207a65726f207a6170206164647265737360381b604482015290519081900360640190fd5b6001600160a01b0384166117a6576040805162461bcd60e51b815260206004820152601e60248201527f4675726e6163653a207a65726f20706c6174666f726d20616464726573730000604482015290519081900360640190fd5b631dcd65008311156117f8576040805162461bcd60e51b81526020600482015260166024820152754675726e6163653a2066656520746f6f206c6172676560501b604482015290519081900360640190fd5b6305f5e10082111561184a576040805162461bcd60e51b81526020600482015260166024820152754675726e6163653a2066656520746f6f206c6172676560501b604482015290519081900360640190fd5b606580546001600160a01b03808a166001600160a01b03199283161790925560668054898416908316179055606d80548884169083161790556070805492871692909116919091179055606e839055606f82905580156118b0576000805461ff00191690555b50505050505050565b6065546001600160a01b03163314806118ea57506118d5611377565b6001600160a01b0316336001600160a01b0316145b611929576040805162461bcd60e51b815260206004820152601f60248201526000805160206136c4833981519152604482015290519081900360640190fd5b6071805463ffffffff8316600160801b810263ffffffff60801b199092169190911790915560408051918252517f4e8e44f9ae8346a3518fe6bfc29de80ad178df8c4db6d6f5cc91b7e79c8a64c89181900360200190a150565b61198b612abe565b6001600160a01b031661199c611377565b6001600160a01b0316146119e5576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b6305f5e100811115611a37576040805162461bcd60e51b81526020600482015260166024820152754675726e6163653a2066656520746f6f206c6172676560501b604482015290519081900360640190fd5b606f8190556040805182815290517fc69cbab065ddb19d4ee1f9cfe242c82b8facbd1e93c89e5830dfe0d4fb2598539181900360200190a150565b611a7a612abe565b6001600160a01b0316611a8b611377565b6001600160a01b031614611ad4576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b631dcd6500811115611b26576040805162461bcd60e51b81526020600482015260166024820152754675726e6163653a2066656520746f6f206c6172676560501b604482015290519081900360640190fd5b606e8190556040805182815290517f9f143d1158804dce75cd6feac5b8fde3c0d57c70d176355a6ab516a2524fcb9f9181900360200190a150565b606c5481565b611b6f612abe565b6001600160a01b0316611b80611377565b6001600160a01b031614611bc9576040805162461bcd60e51b81526020600482018190526024820152600080516020613733833981519152604482015290519081900360640190fd5b6001600160a01b038116611c0e5760405162461bcd60e51b81526004018080602001828103825260268152602001806136786026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b60008111611cbf576040805162461bcd60e51b815260206004820152601a60248201527f4675726e6163653a207769746864726177207a65726f20435658000000000000604482015290519081900360640190fd5b611cc83361234d565b610cc68282612c06565b611cdb3361234d565b33600090815260696020526040902054610b399082906001600160801b0316612c06565b336000908152606a602052604090205460ff16611d5f576040805162461bcd60e51b8152602060048201526019602482015278119d5c9b9858d94e881bdb9b1e481dda1a5d195b1a5cdd1959603a1b604482015290519081900360640190fd5b60008111611db4576040805162461bcd60e51b815260206004820152601c60248201527f4675726e6163653a2064697374726962757465207a65726f2043565800000000604482015290519081900360640190fd5b611dd4734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8330846127db565b610cc68282611fe7565b604080516080810182526071546001600160801b038116825263ffffffff600160801b8204166020830181905265ffffffffffff600160a01b8304811694840194909452600160d01b9091049092166060820152600091829015611e7157816060015165ffffffffffff16421015611e715781600001516001600160801b031642836060015165ffffffffffff16030290505b611f8e81611f8873cf50b810e57ac33b91dcf525c6ddd9881b1393326001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611ed957600080fd5b505afa158015611eed573d6000803e3d6000fd5b505050506040513d6020811015611f0357600080fd5b5051604080516370a0823160e01b81523060048201529051734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b916370a08231916024808301926020929190829003018186803b158015611f5657600080fd5b505afa158015611f6a573d6000803e3d6000fd5b505050506040513d6020811015611f8057600080fd5b505190612dda565b90612e34565b9250505090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611368908490612e91565b611fef612ac2565b604080516080810182526071546001600160801b038116825263ffffffff600160801b8204166020830181905265ffffffffffff600160a01b8304811694840194909452600160d01b9091049092166060820152906120565761205182612f42565b61216e565b806060015165ffffffffffff16421061209557612085816020015163ffffffff16838161207f57fe5b04612a6c565b6001600160801b031681526120e2565b600042826060015165ffffffffffff16039050600082600001516001600160801b0316820290506120d4836020015163ffffffff168286018161207f57fe5b6001600160801b0316835250505b4265ffffffffffff81811660408401819052602084015163ffffffff16928301909116606084018190528351607180546001600160801b0319166001600160801b039092169190911763ffffffff60801b1916600160801b9094029390931765ffffffffffff60a01b1916600160a01b909202919091176001600160d01b0316600160d01b9091021790555b6000612192633b9aca0061218c606b54612186611dde565b906130a2565b906130fb565b604080516370a0823160e01b8152306004820152905191925060009173cf50b810e57ac33b91dcf525c6ddd9881b139332916370a08231916024808301926020929190829003018186803b1580156121e957600080fd5b505afa1580156121fd573d6000803e3d6000fd5b505050506040513d602081101561221357600080fd5b5051905081811015612307578082039150606c54821061230757612261734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b73cf50b810e57ac33b91dcf525c6ddd9881b1393326000613162565b612294734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b73cf50b810e57ac33b91dcf525c6ddd9881b13933284613162565b73cf50b810e57ac33b91dcf525c6ddd9881b1393326001600160a01b031663a694fc3a836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156122ee57600080fd5b505af1158015612302573d6000803e3d6000fd5b505050505b6040805185815290516001600160a01b038716917fc1d32ad5cca423e7dda2123dbf8c482f8e77d00b631c06e903a47f2cec1334df919081900360200190a25050505050565b612355612ac2565b6001600160a01b038116600090815260696020908152604091829020825160808101845281546001600160801b038082168352600160801b918290048116948301949094526001909201546001600160c01b0381169482019490945267ffffffffffffffff600160c01b9485900481166060830181905260685492959483169493830482169390920416111561249f5760408051608081018252600080825260208681015187516001600160801b039101811682850190815287821685870190815267ffffffffffffffff888116606088019081526001600160a01b038d1687526069909552969094209451855491516001600160801b0319909216908316178216600160801b919092160217835590516001909201805491516001600160c01b03199092166001600160c01b0393841617909216600160c01b91909316029190911790556125a7565b60006124c06110a385600001516001600160801b03168587604001516129d3565b600101905083600001516001600160801b0316816001600160801b0316106124e6575082515b6020848101518551604080516080810182526001600160801b03868116825295909203909201841681840190815286851682840190815267ffffffffffffffff878116606085019081526001600160a01b038c1660009081526069909752949095209251835492516001600160801b0319909316908716178616600160801b929096169190910294909417815592516001909301805491516001600160c01b03199092166001600160c01b0394851617909316600160c01b91909216021790555b50505050565b336000908152606960205260409020546067546001600160801b03600160801b928390048116926125e092041682612e34565b606780546001600160801b03928316600160801b02908316179055336000908152606960209081526040808320805490941690935582516370a0823160e01b815230600482015292519192734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b926370a0823192602480840193919291829003018186803b15801561266457600080fd5b505afa158015612678573d6000803e3d6000fd5b505050506040513d602081101561268e57600080fd5b505190508181101561270c5760408051631c683a1b60e11b81528284036004820152600060248201819052915173cf50b810e57ac33b91dcf525c6ddd9881b139332926338d07436926044808201939182900301818387803b1580156126f357600080fd5b505af1158015612707573d6000803e3d6000fd5b505050505b61272b734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8484611f95565b60665460408051630852cd8d60e31b81526004810185905290516001600160a01b03909216916342966c689160248082019260009290919082900301818387803b15801561277857600080fd5b505af115801561278c573d6000803e3d6000fd5b5050604080516001600160a01b03871681526020810186905281513394507f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd98706893509081900390910190a2505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526125a7908590612e91565b61283e8261234d565b6067546001600160801b0380821691600160801b900416600061286382611f88611dde565b905060008085831061287a57506000905084612881565b5050808403815b6001600160a01b0387166000908152606960205260409020546128b9906110a3908390600160801b90046001600160801b0316612dda565b6001600160a01b038816600090815260696020526040902080546001600160801b03928316600160801b0290831617908190556128fc916110a391859116612dda565b6001600160a01b038816600090815260696020526040902080546001600160801b0319166001600160801b039290921691909117905561293f6110a38583612dda565b606780546001600160801b03928316600160801b0292169190911790556129696110a38684612dda565b606780546001600160801b03929092166001600160801b03199092169190911790556040805187815290516001600160a01b038916917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a250505050505050565b60006001600160801b038316612a12576001600160801b0382166129f8575082612a65565b816001600160801b03168481612a0a57fe5b049050612a65565b6001600160801b038216612a3f57600160801b612a38856001600160801b0386166130a2565b81612a0a57fe5b816001600160801b0316612a38846001600160801b0316866130a290919063ffffffff16565b9392505050565b6000600160801b8210612aba576040805162461bcd60e51b81526020600482015260116024820152704675726e6163653a206f766572666c6f7760781b604482015290519081900360640190fd5b5090565b3390565b604080516080810182526071546001600160801b038116825263ffffffff600160801b8204166020830181905265ffffffffffff600160a01b8304811694840194909452600160d01b90910490921660608201529015610b3957606081015165ffffffffffff1642811115612b345750425b6000826040015165ffffffffffff16821015612b51576000612b61565b826040015165ffffffffffff1682035b90508015611368574265ffffffffffff90811660408501819052845160718054602088015160608901516001600160801b03199092166001600160801b0390941693841763ffffffff60801b1916600160801b63ffffffff928316021765ffffffffffff60a01b1916600160a01b909502949094176001600160d01b0316600160d01b91909516029390931790925561136891612c01918491906130a216565b612f42565b336000908152606960205260409020546001600160801b0316811115612c73576040805162461bcd60e51b815260206004820152601b60248201527f4675726e6163653a20636c6576435658206e6f7420656e6f7567680000000000604482015290519081900360640190fd5b33600090815260696020526040902080546001600160801b03198082166001600160801b0392831685900383161790925560678054928316928216849003909116919091179055606654612cd1906001600160a01b03168383611f95565b604080516001600160a01b038416815260208101839052815133927f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb928290030190a25050565b6000612d2330613275565b15905090565b600054610100900460ff1680612d425750612d42612d18565b80612d50575060005460ff16155b612d8b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806136e4602e913960400191505060405180910390fd5b600054610100900460ff16158015612db6576000805460ff1961ff0019909116610100171660011790555b612dbe61327b565b612dc661331b565b8015610b39576000805461ff001916905550565b600082820183811015610ac5576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600082821115612e8b576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000612ee6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166134149092919063ffffffff16565b80519091501561136857808060200190516020811015612f0557600080fd5b50516113685760405162461bcd60e51b815260040180806020018281038252602a815260200180613753602a913960400191505060405180910390fd5b6068805467ffffffffffffffff60801b1981166001600160801b9283900467ffffffffffffffff9081169190910116820217918290556067546001600160801b0380821693929091048116911682841061300b57606780546001600160801b0319169055612fb1838301612a6c565b606780546001600160801b03908116600160801b9390911683021790556068805467ffffffffffffffff60801b81166001600160801b03199091169290920467ffffffffffffffff16600160c01b029190911790556125a7565b606780546001600160801b0319168585036001600160801b0316179055613033828501612a6c565b606760106101000a8154816001600160801b0302191690836001600160801b03160217905550600061306f84600160801b878703028161207f57fe5b905061307b828261342b565b606880546001600160801b0319166001600160801b03929092169190911790555050505050565b6000826130b157506000610ac8565b828202828482816130be57fe5b0414610ac55760405162461bcd60e51b81526004018080602001828103825260218152602001806137126021913960400191505060405180910390fd5b6000808211613151576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161315a57fe5b049392505050565b8015806131e8575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156131ba57600080fd5b505afa1580156131ce573d6000803e3d6000fd5b505050506040513d60208110156131e457600080fd5b5051155b6132235760405162461bcd60e51b815260040180806020018281038252603681526020018061377d6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611368908490612e91565b3b151590565b600054610100900460ff16806132945750613294612d18565b806132a2575060005460ff16155b6132dd5760405162461bcd60e51b815260040180806020018281038252602e8152602001806136e4602e913960400191505060405180910390fd5b600054610100900460ff16158015612dc6576000805460ff1961ff0019909116610100171660011790558015610b39576000805461ff001916905550565b600054610100900460ff16806133345750613334612d18565b80613342575060005460ff16155b61337d5760405162461bcd60e51b815260040180806020018281038252602e8152602001806136e4602e913960400191505060405180910390fd5b600054610100900460ff161580156133a8576000805460ff1961ff0019909116610100171660011790555b60006133b2612abe565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3508015610b39576000805461ff001916905550565b60606134238484600085613478565b949350505050565b60006001600160801b038316613442575080610ac8565b6001600160801b038216613457575081610ac8565b600160801b826001600160801b0316846001600160801b0316028161315a57fe5b6060824710156134b95760405162461bcd60e51b815260040180806020018281038252602681526020018061369e6026913960400191505060405180910390fd5b6134c285613275565b613513576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b602083106135515780518252601f199092019160209182019101613532565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146135b3576040519150601f19603f3d011682016040523d82523d6000602084013e6135b8565b606091505b50915091506135c88282866135d3565b979650505050505050565b606083156135e2575081612a65565b8251156135f25782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561363c578181015183820152602001613624565b50505050905090810190601f1680156136695780820380516001836020036101000a031916815260200191505b509250505060405180910390fdfe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c4675726e6163653a206f6e6c7920676f7665726e6f72206f72206f776e657200496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65725361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220241695b5b02f5e0057933933b0419d40bf30015392a48f875727786b6fc8216164736f6c63430007060033

Verified Source Code Full Match

Compiler: v0.7.6+commit.7338295f EVM: istanbul Optimization: Yes (200 runs)
OwnableUpgradeable.sol 75 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.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.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * 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.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    uint256[49] private __gap;
}
SafeMathUpgradeable.sol 214 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}
Initializable.sol 55 lines
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;

import "../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}
IERC20Upgradeable.sol 77 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @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);
}
SafeERC20Upgradeable.sol 75 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20Upgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;

    function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
AddressUpgradeable.sol 165 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
ContextUpgradeable.sol 32 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}
IERC20.sol 77 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @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);
}
Furnace.sol 613 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "../interfaces/clever/ICLeverToken.sol";
import "../interfaces/clever/IFurnace.sol";
import "../interfaces/IConvexCVXRewardPool.sol";
import "../interfaces/IZap.sol";

// solhint-disable reason-string, not-rely-on-time, max-states-count

contract Furnace is OwnableUpgradeable, IFurnace {
  using SafeMathUpgradeable for uint256;
  using SafeERC20Upgradeable for IERC20Upgradeable;

  event UpdateWhitelist(address indexed _whitelist, bool _status);
  event UpdateStakePercentage(uint256 _percentage);
  event UpdateStakeThreshold(uint256 _threshold);
  event UpdatePlatformFeePercentage(uint256 _feePercentage);
  event UpdateHarvestBountyPercentage(uint256 _percentage);
  event UpdatePlatform(address indexed _platform);
  event UpdateZap(address indexed _zap);
  event UpdateGovernor(address indexed _governor);
  event UpdatePeriodLength(uint256 _length);

  uint256 private constant E128 = 2**128;
  uint256 private constant FEE_PRECISION = 1e9;
  uint256 private constant MAX_PLATFORM_FEE = 5e8; // 50%
  uint256 private constant MAX_HARVEST_BOUNTY = 1e8; // 10%

  address private constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
  // The address of cvxCRV token.
  address private constant CVXCRV = 0x62B9c7356A2Dc64a1969e19C23e4f579F9810Aa7;
  address private constant CVX_REWARD_POOL = 0xCF50b810E57Ac33B91dCF525C6ddd9881B139332;

  /// @notice If the unrealised is not paid off,
  /// the realised token in n sequential distribute is
  ///    user_unrealised * (reward_1 / total_unrealised_1)
  ///  + user_unrealised * (reward_1 / total_unrealised_1) * (reward_2 / total_unrealised_2)
  ///  + ...
  /// the unrealised token in n sequential distribute is
  ///    user_unrealised * (total_unrealised_1 - reward_1) / total_unrealised_1 * (total_unrealised_2 - reward_2) / total_unrealised_2 * ...
  ///
  /// So we can maintain a variable `accUnrealisedFraction` which is a product of `(total_unrealised - reward) / total_unrealised`.
  /// And keep track of this variable on each deposit/withdraw/claim, the unrealised clevCVX of the user should be
  ///                                accUnrealisedFractionPaid
  ///                   unrealised * -------------------------
  ///                                  accUnrealisedFraction
  /// Also, the debt will paid off in some case, we record a global variable `lastPaidOffDistributeIndex` and an user
  /// specific variable `lastDistributeIndex` to check if the debt is paid off during `(lastDistributeIndex, distributeIndex]`.
  ///
  /// And to save the gas usage, an `uint128` is used to store `accUnrealisedFraction` and `accUnrealisedFractionPaid`.
  /// More specifically, it is in range [0, 2^128), means the real number `fraction / 2^128`. If the value is 0, it
  /// means the value of the faction is 1.
  struct UserInfo {
    // The total amount of clevCVX unrealised.
    uint128 unrealised;
    // The total amount of clevCVX realised.
    uint128 realised;
    // The checkpoint for global `accUnrealisedFraction`, multipled by 1e9.
    uint192 accUnrealisedFractionPaid;
    // The distribute index record when use interacted the contract.
    uint64 lastDistributeIndex;
  }

  /// @dev The address of governor
  address public governor;
  /// @dev The address of clevCVX
  address public clevCVX;
  /// @dev The total amount of clevCVX unrealised.
  uint128 public totalUnrealised;
  /// @dev The total amount of clevCVX realised.
  uint128 public totalRealised;
  /// @dev The accumulated unrealised fraction, multipled by 2^128.
  uint128 public accUnrealisedFraction;
  /// @dev The distriubed index, will be increased each time the function `distribute` is called.
  uint64 public distributeIndex;
  /// @dev The distriubed index when all clevCVX is paied off.
  uint64 public lastPaidOffDistributeIndex;
  /// @dev Mapping from user address to user info.
  mapping(address => UserInfo) public userInfo;
  /// @dev Mapping from user address to whether it is whitelisted.
  mapping(address => bool) public isWhitelisted;
  /// @dev The percentage of free CVX should be staked in CVXRewardPool, multipled by 1e9.
  uint256 public stakePercentage;
  /// @dev The minimum amount of CVX in each stake.
  uint256 public stakeThreshold;

  /// @dev The address of zap contract.
  address public zap;
  /// @dev The percentage of rewards to take for platform on harvest, multipled by 1e9.
  uint256 public platformFeePercentage;
  /// @dev The percentage of rewards to take for caller on harvest, multipled by 1e9.
  uint256 public harvestBountyPercentage;
  /// @dev The address of recipient of platform fee
  address public platform;

  /// @dev Compiler will pack this into single `uint256`.
  struct LinearReward {
    // The number of debt token to pay each second.
    uint128 ratePerSecond;
    // The length of reward period in seconds.
    // If the value is zero, the reward will be distributed immediately.
    uint32 periodLength;
    // The timesamp in seconds when reward is updated.
    uint48 lastUpdate;
    // The finish timestamp in seconds of current reward period.
    uint48 finishAt;
  }

  /// @notice The reward distribute information.
  LinearReward public rewardInfo;

  modifier onlyWhitelisted() {
    require(isWhitelisted[msg.sender], "Furnace: only whitelisted");
    _;
  }

  modifier onlyGovernorOrOwner() {
    require(msg.sender == governor || msg.sender == owner(), "Furnace: only governor or owner");
    _;
  }

  function initialize(
    address _governor,
    address _clevCVX,
    address _zap,
    address _platform,
    uint256 _platformFeePercentage,
    uint256 _harvestBountyPercentage
  ) external initializer {
    OwnableUpgradeable.__Ownable_init();

    require(_governor != address(0), "Furnace: zero governor address");
    require(_clevCVX != address(0), "Furnace: zero clevCVX address");
    require(_zap != address(0), "Furnace: zero zap address");
    require(_platform != address(0), "Furnace: zero platform address");
    require(_platformFeePercentage <= MAX_PLATFORM_FEE, "Furnace: fee too large");
    require(_harvestBountyPercentage <= MAX_HARVEST_BOUNTY, "Furnace: fee too large");

    governor = _governor;
    clevCVX = _clevCVX;
    zap = _zap;
    platform = _platform;
    platformFeePercentage = _platformFeePercentage;
    harvestBountyPercentage = _harvestBountyPercentage;
  }

  /********************************** View Functions **********************************/

  /// @dev Return the amount of clevCVX unrealised and realised of user.
  /// @param _account The address of user.
  /// @return unrealised The amount of clevCVX unrealised.
  /// @return realised The amount of clevCVX realised and can be claimed.
  function getUserInfo(address _account) external view override returns (uint256 unrealised, uint256 realised) {
    UserInfo memory _info = userInfo[_account];
    if (_info.lastDistributeIndex < lastPaidOffDistributeIndex) {
      // In this case, all unrealised is paid off since last operate.
      return (0, _info.unrealised + _info.realised);
    } else {
      // extra plus 1, make sure we round up in division
      uint128 _newUnrealised = _toU128(
        _muldiv128(_info.unrealised, accUnrealisedFraction, uint128(_info.accUnrealisedFractionPaid))
      ) + 1;
      if (_newUnrealised >= _info.unrealised) {
        _newUnrealised = _info.unrealised;
      }
      uint128 _newRealised = _info.unrealised - _newUnrealised + _info.realised; // never overflow here
      return (_newUnrealised, _newRealised);
    }
  }

  /// @dev Return the total amount of free CVX in this contract, including staked in CVXRewardPool.
  /// @return The amount of CVX in this contract now.
  function totalCVXInPool() public view returns (uint256) {
    LinearReward memory _info = rewardInfo;
    uint256 _leftover = 0;
    if (_info.periodLength != 0) {
      if (block.timestamp < _info.finishAt) {
        _leftover = (_info.finishAt - block.timestamp) * _info.ratePerSecond;
      }
    }
    return
      IERC20Upgradeable(CVX)
        .balanceOf(address(this))
        .add(IConvexCVXRewardPool(CVX_REWARD_POOL).balanceOf(address(this)))
        .sub(_leftover);
  }

  /********************************** Mutated Functions **********************************/

  /// @dev Deposit clevCVX in this contract to change for CVX.
  /// @param _amount The amount of clevCVX to deposit.
  function deposit(uint256 _amount) external override {
    require(_amount > 0, "Furnace: deposit zero clevCVX");

    // transfer token into contract
    IERC20Upgradeable(clevCVX).safeTransferFrom(msg.sender, address(this), _amount);

    _deposit(msg.sender, _amount);
  }

  /// @dev Deposit clevCVX in this contract to change for CVX for other user.
  /// @param _account The address of user you deposit for.
  /// @param _amount The amount of clevCVX to deposit.
  function depositFor(address _account, uint256 _amount) external override {
    require(_amount > 0, "Furnace: deposit zero clevCVX");

    // transfer token into contract
    IERC20Upgradeable(clevCVX).safeTransferFrom(msg.sender, address(this), _amount);

    _deposit(_account, _amount);
  }

  /// @dev Withdraw unrealised clevCVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the clevCVX.
  /// @param _amount The amount of clevCVX to withdraw.
  function withdraw(address _recipient, uint256 _amount) external override {
    require(_amount > 0, "Furnace: withdraw zero CVX");

    _updateUserInfo(msg.sender);
    _withdraw(_recipient, _amount);
  }

  /// @dev Withdraw all unrealised clevCVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the clevCVX.
  function withdrawAll(address _recipient) external override {
    _updateUserInfo(msg.sender);

    _withdraw(_recipient, userInfo[msg.sender].unrealised);
  }

  /// @dev Claim all realised CVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the CVX.
  function claim(address _recipient) external override {
    _updateUserInfo(msg.sender);

    _claim(_recipient);
  }

  /// @dev Exit the contract, withdraw all unrealised clevCVX and realised CVX of the caller.
  /// @param _recipient The address of user who will recieve the clevCVX and CVX.
  function exit(address _recipient) external override {
    _updateUserInfo(msg.sender);

    _withdraw(_recipient, userInfo[msg.sender].unrealised);
    _claim(_recipient);
  }

  /// @dev Distribute CVX from `origin` to pay clevCVX debt.
  /// @param _origin The address of the user who will provide CVX.
  /// @param _amount The amount of CVX will be provided.
  function distribute(address _origin, uint256 _amount) external override onlyWhitelisted {
    require(_amount > 0, "Furnace: distribute zero CVX");

    IERC20Upgradeable(CVX).safeTransferFrom(_origin, address(this), _amount);

    _distribute(_origin, _amount);
  }

  /// @dev Harvest the pending reward and convert to cvxCRV.
  /// @param _recipient - The address of account to receive harvest bounty.
  /// @param _minimumOut - The minimum amount of cvxCRV should get.
  /// @return the amount of CVX harvested.
  function harvest(address _recipient, uint256 _minimumOut) external onlyWhitelisted returns (uint256) {
    // 1. harvest from CVXRewardPool
    IConvexCVXRewardPool(CVX_REWARD_POOL).getReward(false);

    // 2. swap all reward to CVX (cvxCRV only currently)
    uint256 _amount = IERC20Upgradeable(CVXCRV).balanceOf(address(this));
    if (_amount > 0) {
      IERC20Upgradeable(CVXCRV).safeTransfer(zap, _amount);
      _amount = IZap(zap).zap(CVXCRV, _amount, CVX, _minimumOut);
    }

    emit Harvest(msg.sender, _amount);

    if (_amount > 0) {
      uint256 _distributeAmount = _amount;
      // 3. take platform fee and harvest bounty
      uint256 _platformFee = platformFeePercentage;
      if (_platformFee > 0) {
        _platformFee = (_platformFee * _distributeAmount) / FEE_PRECISION;
        IERC20Upgradeable(CVX).safeTransfer(platform, _platformFee);
        _distributeAmount = _distributeAmount - _platformFee; // never overflow here
      }
      uint256 _harvestBounty = harvestBountyPercentage;
      if (_harvestBounty > 0) {
        _harvestBounty = (_harvestBounty * _distributeAmount) / FEE_PRECISION;
        _distributeAmount = _distributeAmount - _harvestBounty; // never overflow here
        IERC20Upgradeable(CVX).safeTransfer(_recipient, _harvestBounty);
      }
      // 4. distribute harvest CVX to pay clevCVX
      // @note: we may distribute all rest CVX to AladdinConvexLocker
      _distribute(address(this), _distributeAmount);
    }
    return _amount;
  }

  /// @notice External helper function to update global debt.
  function updatePendingDistribution() external {
    _updatePendingDistribution();
  }

  /********************************** Restricted Functions **********************************/

  /// @dev Update the status of a list of whitelisted accounts.
  /// @param _whitelists The address list of whitelisted accounts.
  /// @param _status The status to update.
  function updateWhitelists(address[] memory _whitelists, bool _status) external onlyOwner {
    for (uint256 i = 0; i < _whitelists.length; i++) {
      // solhint-disable-next-line reason-string
      require(_whitelists[i] != address(0), "Furnace: zero whitelist address");
      isWhitelisted[_whitelists[i]] = _status;

      emit UpdateWhitelist(_whitelists[i], _status);
    }
  }

  /// @dev Update the address of governor.
  /// @param _governor The address to be updated
  function updateGovernor(address _governor) external onlyGovernorOrOwner {
    require(_governor != address(0), "Furnace: zero governor address");
    governor = _governor;

    emit UpdateGovernor(_governor);
  }

  /// @dev Update stake percentage for CVX in this contract.
  /// @param _percentage The stake percentage to be updated, multipled by 1e9.
  function updateStakePercentage(uint256 _percentage) external onlyGovernorOrOwner {
    require(_percentage <= FEE_PRECISION, "Furnace: percentage too large");
    stakePercentage = _percentage;

    emit UpdateStakePercentage(_percentage);
  }

  /// @dev Update stake threshold for CVX.
  /// @param _threshold The stake threshold to be updated.
  function updateStakeThreshold(uint256 _threshold) external onlyGovernorOrOwner {
    stakeThreshold = _threshold;

    emit UpdateStakeThreshold(_threshold);
  }

  /// @dev Update the platform fee percentage.
  /// @param _feePercentage The fee percentage to be updated, multipled by 1e9.
  function updatePlatformFeePercentage(uint256 _feePercentage) external onlyOwner {
    require(_feePercentage <= MAX_PLATFORM_FEE, "Furnace: fee too large");
    platformFeePercentage = _feePercentage;

    emit UpdatePlatformFeePercentage(_feePercentage);
  }

  /// @dev Update the harvest bounty percentage.
  /// @param _percentage - The fee percentage to be updated, multipled by 1e9.
  function updateHarvestBountyPercentage(uint256 _percentage) external onlyOwner {
    require(_percentage <= MAX_HARVEST_BOUNTY, "Furnace: fee too large");
    harvestBountyPercentage = _percentage;

    emit UpdateHarvestBountyPercentage(_percentage);
  }

  /// @dev Update the platform fee recipient
  /// @dev _platform The platform address to be updated.
  function updatePlatform(address _platform) external onlyOwner {
    require(_platform != address(0), "Furnace: zero platform address");
    platform = _platform;

    emit UpdatePlatform(_platform);
  }

  /// @dev Update the zap contract
  /// @param _zap The zap contract to be updated.
  function updateZap(address _zap) external onlyGovernorOrOwner {
    require(_zap != address(0), "Furnace: zero zap address");
    zap = _zap;

    emit UpdateZap(_zap);
  }

  /// @notice Update the reward period length.
  /// @dev The modification will be effictive after next reward distribution.
  /// @param _length The period length to be updated.
  function updatePeriodLength(uint32 _length) external onlyGovernorOrOwner {
    rewardInfo.periodLength = _length;

    emit UpdatePeriodLength(_length);
  }

  /********************************** Internal Functions **********************************/

  /// @dev Internal function to reduce global debt based on pending rewards.
  /// This function should be called before any mutable state change.
  function _updatePendingDistribution() internal {
    LinearReward memory _info = rewardInfo;
    if (_info.periodLength > 0) {
      uint256 _currentTime = _info.finishAt;
      if (_currentTime > block.timestamp) {
        _currentTime = block.timestamp;
      }
      uint256 _duration = _currentTime >= _info.lastUpdate ? _currentTime - _info.lastUpdate : 0;
      if (_duration > 0) {
        _info.lastUpdate = uint48(block.timestamp);
        rewardInfo = _info;

        _reduceGlobalDebt(_duration.mul(_info.ratePerSecond));
      }
    }
  }

  /// @dev Internal function called when user interacts with the contract.
  /// @param _account The address of user to update.
  function _updateUserInfo(address _account) internal {
    _updatePendingDistribution();

    UserInfo memory _info = userInfo[_account];
    uint128 _accUnrealisedFraction = accUnrealisedFraction;
    uint64 _distributeIndex = distributeIndex;
    if (_info.lastDistributeIndex < lastPaidOffDistributeIndex) {
      // In this case, all unrealised is paid off since last operate.
      userInfo[_account] = UserInfo({
        unrealised: 0,
        realised: _info.unrealised + _info.realised, // never overflow here
        accUnrealisedFractionPaid: _accUnrealisedFraction,
        lastDistributeIndex: _distributeIndex
      });
    } else {
      // extra plus 1, make sure we round up in division
      uint128 _newUnrealised = _toU128(
        _muldiv128(_info.unrealised, _accUnrealisedFraction, uint128(_info.accUnrealisedFractionPaid))
      ) + 1;
      if (_newUnrealised >= _info.unrealised) {
        _newUnrealised = _info.unrealised;
      }
      uint128 _newRealised = _info.unrealised - _newUnrealised + _info.realised; // never overflow here
      userInfo[_account] = UserInfo({
        unrealised: _newUnrealised,
        realised: _newRealised,
        accUnrealisedFractionPaid: _accUnrealisedFraction,
        lastDistributeIndex: _distributeIndex
      });
    }
  }

  /// @dev Internal function called by `deposit` and `depositFor`.
  ///      assume that clevCVX is already transfered into this contract.
  /// @param _account The address of the user.
  /// @param _amount The amount of clevCVX to deposit.
  function _deposit(address _account, uint256 _amount) internal {
    // 1. update user info
    _updateUserInfo(_account);

    // 2. compute realised and unrelised
    uint256 _totalUnrealised = totalUnrealised;
    uint256 _totalRealised = totalRealised;
    uint256 _freeCVX = totalCVXInPool().sub(_totalRealised);

    uint256 _newUnrealised;
    uint256 _newRealised;
    if (_freeCVX >= _amount) {
      // pay all the debt with CVX in contract directly.
      _newUnrealised = 0;
      _newRealised = _amount;
    } else {
      // pay part of the debt with CVX in contract directly
      // and part of the debt with future CVX distributed to the contract.
      _newUnrealised = _amount - _freeCVX;
      _newRealised = _freeCVX;
    }

    // 3. update user and global state
    userInfo[_account].realised = _toU128(_newRealised.add(userInfo[_account].realised));
    userInfo[_account].unrealised = _toU128(_newUnrealised.add(userInfo[_account].unrealised));

    totalRealised = _toU128(_totalRealised.add(_newRealised));
    totalUnrealised = _toU128(_totalUnrealised.add(_newUnrealised));

    emit Deposit(_account, _amount);
  }

  /// @dev Internal function called by `withdraw` and `withdrawAll`.
  /// @param _recipient The address of user who will recieve the clevCVX.
  /// @param _amount The amount of clevCVX to withdraw.
  function _withdraw(address _recipient, uint256 _amount) internal {
    require(_amount <= userInfo[msg.sender].unrealised, "Furnace: clevCVX not enough");

    userInfo[msg.sender].unrealised = uint128(uint256(userInfo[msg.sender].unrealised) - _amount); // never overflow here
    totalUnrealised = uint128(uint256(totalUnrealised) - _amount); // never overflow here

    IERC20Upgradeable(clevCVX).safeTransfer(_recipient, _amount);

    emit Withdraw(msg.sender, _recipient, _amount);
  }

  /// @dev Internal function called by `claim`.
  /// @param _recipient The address of user who will recieve the CVX.
  function _claim(address _recipient) internal {
    uint256 _amount = userInfo[msg.sender].realised;
    // should not overflow, but just in case, we use safe math.
    totalRealised = uint128(uint256(totalRealised).sub(_amount));
    userInfo[msg.sender].realised = 0;

    uint256 _balanceInContract = IERC20Upgradeable(CVX).balanceOf(address(this));
    if (_balanceInContract < _amount) {
      // balance is not enough, with from reward pool
      IConvexCVXRewardPool(CVX_REWARD_POOL).withdraw(_amount - _balanceInContract, false);
    }
    IERC20Upgradeable(CVX).safeTransfer(_recipient, _amount);
    // burn realised clevCVX
    ICLeverToken(clevCVX).burn(_amount);

    emit Claim(msg.sender, _recipient, _amount);
  }

  /// @dev Internal function called by `distribute` and `harvest`.
  /// @param _origin The address of the user who will provide CVX.
  /// @param _amount The amount of CVX will be provided.
  function _distribute(address _origin, uint256 _amount) internal {
    // reduct pending debt
    _updatePendingDistribution();

    // distribute clevCVX rewards
    LinearReward memory _info = rewardInfo;
    if (_info.periodLength == 0) {
      _reduceGlobalDebt(_amount);
    } else {
      if (block.timestamp >= _info.finishAt) {
        _info.ratePerSecond = _toU128(_amount / _info.periodLength);
      } else {
        uint256 _remaining = _info.finishAt - block.timestamp;
        uint256 _leftover = _remaining * _info.ratePerSecond;
        _info.ratePerSecond = _toU128((_amount + _leftover) / _info.periodLength);
      }

      _info.lastUpdate = uint48(block.timestamp);
      _info.finishAt = uint48(block.timestamp + _info.periodLength);

      rewardInfo = _info;
    }

    // 2. stake extra CVX to cvxRewardPool
    uint256 _toStake = totalCVXInPool().mul(stakePercentage).div(FEE_PRECISION);
    uint256 _balanceStaked = IConvexCVXRewardPool(CVX_REWARD_POOL).balanceOf(address(this));
    if (_balanceStaked < _toStake) {
      _toStake = _toStake - _balanceStaked;
      if (_toStake >= stakeThreshold) {
        IERC20Upgradeable(CVX).safeApprove(CVX_REWARD_POOL, 0);
        IERC20Upgradeable(CVX).safeApprove(CVX_REWARD_POOL, _toStake);
        IConvexCVXRewardPool(CVX_REWARD_POOL).stake(_toStake);
      }
    }

    emit Distribute(_origin, _amount);
  }

  /// @dev Internal function to reduce global debt based on CVX rewards.
  /// @param _amount The new paid clevCVX debt.
  function _reduceGlobalDebt(uint256 _amount) internal {
    distributeIndex += 1;

    uint256 _totalUnrealised = totalUnrealised;
    uint256 _totalRealised = totalRealised;
    uint128 _accUnrealisedFraction = accUnrealisedFraction;
    // 1. distribute clevCVX rewards
    if (_amount >= _totalUnrealised) {
      // In this case, all unrealised clevCVX are paid off.
      totalUnrealised = 0;
      totalRealised = _toU128(_totalUnrealised + _totalRealised);

      accUnrealisedFraction = 0;
      lastPaidOffDistributeIndex = distributeIndex;
    } else {
      totalUnrealised = uint128(_totalUnrealised - _amount);
      totalRealised = _toU128(_totalRealised + _amount);

      uint128 _fraction = _toU128(((_totalUnrealised - _amount) * E128) / _totalUnrealised); // mul never overflow
      accUnrealisedFraction = _mul128(_accUnrealisedFraction, _fraction);
    }
  }

  /// @dev Convert uint256 value to uint128 value.
  function _toU128(uint256 _value) internal pure returns (uint128) {
    require(_value < 340282366920938463463374607431768211456, "Furnace: overflow");
    return uint128(_value);
  }

  /// @dev Compute the value of (_a / 2^128) * (_b / 2^128) with precision 2^128.
  function _mul128(uint128 _a, uint128 _b) internal pure returns (uint128) {
    if (_a == 0) return _b;
    if (_b == 0) return _a;
    return uint128((uint256(_a) * uint256(_b)) / E128);
  }

  /// @dev Compute the value of _a * (_b / 2^128) / (_c / 2^128).
  function _muldiv128(
    uint256 _a,
    uint128 _b,
    uint128 _c
  ) internal pure returns (uint256) {
    if (_b == 0) {
      if (_c == 0) return _a;
      else return _a / _c;
    } else {
      if (_c == 0) return _a.mul(_b) / E128;
      else return _a.mul(_b) / _c;
    }
  }
}
ICLeverToken.sol 13 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ICLeverToken is IERC20 {
  function mint(address _recipient, uint256 _amount) external;

  function burn(uint256 _amount) external;

  function burnFrom(address _account, uint256 _amount) external;
}
IFurnace.sol 48 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

interface IFurnace {
  event Deposit(address indexed _account, uint256 _amount);
  event Withdraw(address indexed _account, address _recipient, uint256 _amount);
  event Claim(address indexed _account, address _recipient, uint256 _amount);
  event Distribute(address indexed _origin, uint256 _amount);
  event Harvest(address indexed _caller, uint256 _amount);

  /// @dev Return the amount of clevCVX unrealised and realised of user.
  /// @param _account The address of user.
  /// @return unrealised The amount of clevCVX unrealised.
  /// @return realised The amount of clevCVX realised and can be claimed.
  function getUserInfo(address _account) external view returns (uint256 unrealised, uint256 realised);

  /// @dev Deposit clevCVX in this contract to change for CVX.
  /// @param _amount The amount of clevCVX to deposit.
  function deposit(uint256 _amount) external;

  /// @dev Deposit clevCVX in this contract to change for CVX for other user.
  /// @param _account The address of user you deposit for.
  /// @param _amount The amount of clevCVX to deposit.
  function depositFor(address _account, uint256 _amount) external;

  /// @dev Withdraw unrealised clevCVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the clevCVX.
  /// @param _amount The amount of clevCVX to withdraw.
  function withdraw(address _recipient, uint256 _amount) external;

  /// @dev Withdraw all unrealised clevCVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the clevCVX.
  function withdrawAll(address _recipient) external;

  /// @dev Claim all realised CVX of the caller from this contract.
  /// @param _recipient The address of user who will recieve the CVX.
  function claim(address _recipient) external;

  /// @dev Exit the contract, withdraw all unrealised clevCVX and realised CVX of the caller.
  /// @param _recipient The address of user who will recieve the clevCVX and CVX.
  function exit(address _recipient) external;

  /// @dev Distribute CVX from `origin` to pay clevCVX debt.
  /// @param _origin The address of the user who will provide CVX.
  /// @param _amount The amount of CVX will be provided.
  function distribute(address _origin, uint256 _amount) external;
}
IConvexCVXRewardPool.sol 27 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

interface IConvexCVXRewardPool {
  function balanceOf(address account) external view returns (uint256);

  function earned(address account) external view returns (uint256);

  function withdraw(uint256 _amount, bool claim) external;

  function withdrawAll(bool claim) external;

  function stake(uint256 _amount) external;

  function stakeAll() external;

  function stakeFor(address _for, uint256 _amount) external;

  function getReward(
    address _account,
    bool _claimExtras,
    bool _stake
  ) external;

  function getReward(bool _stake) external;
}
IZap.sol 27 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0 || ^0.8.0;

interface IZap {
  function zap(
    address _fromToken,
    uint256 _amountIn,
    address _toToken,
    uint256 _minOut
  ) external payable returns (uint256);

  function zapWithRoutes(
    address _fromToken,
    uint256 _amountIn,
    address _toToken,
    uint256[] calldata _routes,
    uint256 _minOut
  ) external payable returns (uint256);

  function zapFrom(
    address _fromToken,
    uint256 _amountIn,
    address _toToken,
    uint256 _minOut
  ) external payable returns (uint256);
}

Read Contract

accUnrealisedFraction 0xb2cc1739 → uint128
clevCVX 0xad3e942f → address
distributeIndex 0x65728728 → uint64
getUserInfo 0x6386c1c7 → uint256, uint256
governor 0x0c340a24 → address
harvestBountyPercentage 0x6a423727 → uint256
isWhitelisted 0x3af32abf → bool
lastPaidOffDistributeIndex 0x74b2e90b → uint64
owner 0x8da5cb5b → address
platform 0x4bde38c8 → address
platformFeePercentage 0xcdd78cfc → uint256
rewardInfo 0xc9f67072 → uint128, uint32, uint48, uint48
stakePercentage 0xc1a70d14 → uint256
stakeThreshold 0xf11f77f9 → uint256
totalCVXInPool 0xfea097d8 → uint256
totalRealised 0xb76719b8 → uint128
totalUnrealised 0x3fb266fc → uint128
userInfo 0x1959a002 → uint128, uint128, uint192, uint64
zap 0x262d6152 → address

Write Contract 21 functions

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

claim 0x1e83409a
address _recipient
deposit 0xb6b55f25
uint256 _amount
depositFor 0x2f4f21e2
address _account
uint256 _amount
distribute 0xfb932108
address _origin
uint256 _amount
exit 0xb42652e9
address _recipient
harvest 0x018ee9b7
address _recipient
uint256 _minimumOut
returns: uint256
initialize 0xd7c41c79
address _governor
address _clevCVX
address _zap
address _platform
uint256 _platformFeePercentage
uint256 _harvestBountyPercentage
renounceOwnership 0x715018a6
No parameters
transferOwnership 0xf2fde38b
address newOwner
updateGovernor 0x62f384ad
address _governor
updateHarvestBountyPercentage 0xe36a6b83
uint256 _percentage
updatePendingDistribution 0x7d1c5819
No parameters
updatePeriodLength 0xdd035223
uint32 _length
updatePlatform 0xa293da0f
address _platform
updatePlatformFeePercentage 0xe4830153
uint256 _feePercentage
updateStakePercentage 0x2ba9b8da
uint256 _percentage
updateStakeThreshold 0x5ef53329
uint256 _threshold
updateWhitelists 0x794dca89
address[] _whitelists
bool _status
updateZap 0x35c807e6
address _zap
withdraw 0xf3fef3a3
address _recipient
uint256 _amount
withdrawAll 0xfa09e630
address _recipient

Recent Transactions

No transactions found for this address