Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xCC7ed2ab6c3396DdBc4316D2d7C1b59ff9d2091F
Balance 0 ETH
Nonce 1
Code Size 14481 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

14481 bytes
0x608060405260043610610387575f3560e01c8063707f6dda116101d3578063c50312ad116100fd578063e45fa5a51161009d578063f2fde38b1161006d578063f2fde38b14610a8f578063f80b0cfb14610aae578063fe73e37914610ac2578063ffb75cab14610ad5575f80fd5b8063e45fa5a514610a0c578063e745646114610a20578063e805217414610a34578063e90225cb14610a68575f80fd5b8063d9af94af116100d8578063d9af94af14610970578063dd62ed3e14610982578063dff96e9a146109c6578063e3af6d0a146109da575f80fd5b8063c50312ad146108fc578063d1f7363814610930578063d819e19814610944575f80fd5b80638e449fdc11610173578063a1bbd7b911610143578063a1bbd7b91461088d578063a9059cbb146108aa578063baf20eef146108c9578063bb57ad20146108e8575f80fd5b80638e449fdc1461083357806392c1df541461084657806395d89b411461085a5780639ed992201461086e575f80fd5b80637b763a2c116101ae5780637b763a2c146107c25780637c872f87146107e15780637d6b3253146108005780637ec26dca1461081f575f80fd5b8063707f6dda1461075b57806370a082311461077a578063715018a6146107ae575f80fd5b8063313ce567116102b45780634d235aa2116102545780635c5ef4b6116102245780635c5ef4b6146106f6578063635d70f41461071557806367921d68146107285780636f6096331461073c575f80fd5b80634d235aa21461064a5780635109c57c14610669578063544a6c59146106a057806354f5d028146106e2575f80fd5b806337c4f8c41161028f57806337c4f8c4146105d95780633a9693e1146105f85780633c34267f146106175780633cef7cc314610636575f80fd5b8063313ce5671461058057806333f3fd781461059b5780633765d723146105ba575f80fd5b80631ce392b11161032a57806329b70d7a116102fa57806329b70d7a146104ee5780632d02347a146105225780632f7719511461054d578063300284f214610561575f80fd5b80631ce392b114610493578063216630b4146104a757806323639385146104bb57806323b872dd146104cf575f80fd5b806313aad5101161036557806313aad5101461043657806318160ddd1461044c5780631ae409c0146104605780631c911f2b14610474575f80fd5b806306fdde031461038b578063095ea7b3146103b55780631371bb40146103e4575b5f80fd5b348015610396575f80fd5b5061039f610b01565b6040516103ac91906133df565b60405180910390f35b3480156103c0575f80fd5b506103d46103cf36600461342c565b610b91565b60405190151581526020016103ac565b3480156103ef575f80fd5b506104286103fe366004613454565b6001600160a01b039182165f90815260166020908152604080832093909416825291909152205490565b6040519081526020016103ac565b348015610441575f80fd5b5061044a610baa565b005b348015610457575f80fd5b50600254610428565b34801561046b575f80fd5b50600654610428565b34801561047f575f80fd5b5061044a61048e366004613485565b610bb4565b34801561049e575f80fd5b50601e54610428565b3480156104b2575f80fd5b50600c54610428565b3480156104c6575f80fd5b50610428610c03565b3480156104da575f80fd5b506103d46104e936600461349e565b610c26565b3480156104f9575f80fd5b50610428610508366004613485565b6001600160a01b03165f9081526015602052604090205490565b34801561052d575f80fd5b5061042861053c3660046134d7565b5f9081526008602052604090205490565b348015610558575f80fd5b50600b54610428565b34801561056c575f80fd5b506103d461057b36600461342c565b610c49565b34801561058b575f80fd5b50604051601281526020016103ac565b3480156105a6575f80fd5b5061044a6105b53660046134ee565b610cc6565b3480156105c5575f80fd5b5061044a6105d43660046134d7565b610cec565b3480156105e4575f80fd5b5061044a6105f33660046134d7565b610d61565b348015610603575f80fd5b5061044a610612366004613485565b610d93565b348015610622575f80fd5b5061044a6106313660046134d7565b610e07565b348015610641575f80fd5b5061044a610e45565b348015610655575f80fd5b5061044a6106643660046134d7565b610f57565b348015610674575f80fd5b506103d4610683366004613485565b6001600160a01b03165f908152601a602052604090205460ff1690565b3480156106ab575f80fd5b506104286106ba366004613454565b6001600160a01b039182165f9081526020808052604080832093909416825291909152205490565b3480156106ed575f80fd5b50601354610428565b348015610701575f80fd5b5061044a610710366004613485565b610f98565b61044a61072336600461353a565b610ff7565b348015610733575f80fd5b50601c54610428565b348015610747575f80fd5b506c01431e0fae6d7217caa0000000610428565b348015610766575f80fd5b5061044a6107753660046134d7565b6110dd565b348015610785575f80fd5b50610428610794366004613485565b6001600160a01b03165f9081526020819052604090205490565b3480156107b9575f80fd5b5061044a61117c565b3480156107cd575f80fd5b5061044a6107dc36600461342c565b61118d565b3480156107ec575f80fd5b5061044a6107fb3660046134d7565b6111b1565b34801561080b575f80fd5b5061044a61081a366004613485565b611242565b34801561082a575f80fd5b50600954610428565b61044a61084136600461355a565b6113a6565b348015610851575f80fd5b50600e54610428565b348015610865575f80fd5b5061039f6114c8565b348015610879575f80fd5b5061044a610888366004613591565b6114d7565b348015610898575f80fd5b506007546001600160601b0316610428565b3480156108b5575f80fd5b506103d46108c436600461342c565b6114fc565b3480156108d4575f80fd5b5061044a6108e33660046134d7565b611509565b3480156108f3575f80fd5b5061044a61152c565b348015610907575f80fd5b50610428610916366004613485565b6001600160a01b03165f9081526010602052604090205490565b34801561093b575f80fd5b50601f54610428565b34801561094f575f80fd5b5061096361095e36600461342c565b6116b1565b6040516103ac9190613681565b34801561097b575f80fd5b5042610428565b34801561098d575f80fd5b5061042861099c366004613454565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b3480156109d1575f80fd5b50600d54610428565b3480156109e5575f80fd5b507f0000000000000000000000000000000000000000000000000000000066ea7bfb610428565b348015610a17575f80fd5b50600f54610428565b348015610a2b575f80fd5b50601d54610428565b348015610a3f575f80fd5b50610428610a4e366004613485565b6001600160a01b03165f9081526014602052604090205490565b348015610a73575f80fd5b506019546040516001600160a01b0390911681526020016103ac565b348015610a9a575f80fd5b5061044a610aa9366004613485565b61179c565b348015610ab9575f80fd5b50600a54610428565b61044a610ad03660046134d7565b6117ad565b348015610ae0575f80fd5b50610af4610aef366004613485565b6117ef565b6040516103ac9190613690565b606060038054610b1090613700565b80601f0160208091040260200160405190810160405280929190818152602001828054610b3c90613700565b8015610b875780601f10610b5e57610100808354040283529160200191610b87565b820191905f5260205f20905b815481529060010190602001808311610b6a57829003601f168201915b5050505050905090565b5f33610b9e8185856118ff565b60019150505b92915050565b610bb2611a28565b565b610bbc611b4f565b6001600160a01b038116610be357604051633692283d60e21b815260040160405180910390fd5b6001600160a01b03165f908152601a60205260409020805460ff19169055565b5f600b54600a54600954610c17919061374c565b610c21919061374c565b905090565b5f33610c33858285611b7a565b610c3e858585611c04565b506001949350505050565b5f6001600160a01b038316610c7157604051633692283d60e21b815260040160405180910390fd5b335f818152602080805260408083206001600160a01b038816808552925280832086905551859391927ff8e109bcddf5e12132b7cd8a8517d97498f50c7ac595874d6f513243098b079891a450600192915050565b610cce611da6565b610cdb8585858585611dff565b610ce56001601755565b5050505050565b6019546001600160a01b03163314610d1757604051637b28105560e11b815260040160405180910390fd5b601b5460ff1615610d3b576040516371cad2dd60e11b815260040160405180910390fd5b601b805460ff19166001179055601954610d5e906001600160a01b031682611e4c565b50565b610d69611a28565b610d71611da6565b610d89335f610d8233856001611f09565b6001612133565b610d5e6001601755565b6018546001600160a01b03163314610dbe57604051637b28105560e11b815260040160405180910390fd5b6001600160a01b038116610de557604051633692283d60e21b815260040160405180910390fd5b601880546001600160a01b0319166001600160a01b0392909216919091179055565b610e0f611da6565b805f03610e2f57604051637133514760e11b815260040160405180910390fd5b610e393382612241565b610d89335f835f612133565b610e4d611a28565b610e55611da6565b5f610e5f60065490565b60625f5260086020527f3eae06187ba0198e7de2db0a13c35f0d744282a7db7e055bcdaaf6ee7879e66954909150811015610e9a5750610f4d565b610ea4606261236e565b601e548015610edf575f601e55601954610edf9073f19308f923582a6f7c465e5ce7a9dc1bec6665b19030906001600160a01b0316846123d7565b601f548015610f1a575f601f55601954610f1a907396a5399d07896f757bd4c6ef56461f58db9518629030906001600160a01b0316846123d7565b8082847f10c98735795e0571a6953e9af4a856a063c221909e07483422446ccbf710af4060405160405180910390a45050505b610bb26001601755565b610f5f611a28565b610f67611b4f565b801580610f75575061271081115b15610f9357604051637133514760e11b815260040160405180910390fd5b601d55565b6001600160a01b0381165f908152601a602052604090205460ff16610fd057604051630a5cfbdb60e11b815260040160405180910390fd5b610d5e81610ff2836001600160a01b03165f9081526020819052604090205490565b612241565b610fff611a28565b611007611da6565b335f908152601060205260409020546103e89061102590600161375f565b1115611044576040516335aabe6960e11b815260040160405180910390fd5b5f8261104f600e5490565b611059919061375f565b90505f61106560095490565b61107090600161375f565b90505f61109d33868661108b6007546001600160601b031690565b87876110988c60016124dc565b612513565b600c546110aa919061375f565b90506110c0828483600992909255600e55600c55565b6110cc8560015f6127ae565b5050506110d96001601755565b5050565b6110e5611a28565b6110ed611da6565b61110d7396a5399d07896f757bd4c6ef56461f58db9518623330846123d7565b80601f5f82825461111e919061375f565b909155505060065481906040517396a5399d07896f757bd4c6ef56461f58db951862815233907fc0eadb8b1414d09241a441cb47087f29af42c6b281bc64bc445a6aed0c9bff24906020015b60405180910390a4610d5e6001601755565b611184611b4f565b610bb25f61285c565b611195611a28565b61119d611da6565b6111a7828261287e565b6110d96001601755565b6111b9611a28565b6111c1611da6565b6111e173f19308f923582a6f7c465e5ce7a9dc1bec6665b13330846123d7565b80601e5f8282546111f2919061375f565b9091555050600654819060405173f19308f923582a6f7c465e5ce7a9dc1bec6665b1815233907fc0eadb8b1414d09241a441cb47087f29af42c6b281bc64bc445a6aed0c9bff249060200161116a565b61124a611b4f565b6001600160a01b03811661127157604051633692283d60e21b815260040160405180910390fd5b6040516301ffc9a760e01b80825260048201526001600160a01b038216906301ffc9a7906024016020604051808303815f875af11580156112b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112d89190613772565b158061134f57506040516301ffc9a760e01b815263663ca86f60e01b60048201526001600160a01b038216906301ffc9a7906024016020604051808303815f875af1158015611329573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061134d9190613772565b155b1561136d5760405163a3600d0960e01b815260040160405180910390fd5b601980546001600160a01b039092166001600160a01b0319909216821790555f908152601a60205260409020805460ff19166001179055565b6113ae611da6565b6113b6611a28565b815f036113d6576040516301742a3360e21b815260040160405180910390fd5b838310806113e2575083155b806113ed5750605883115b1561140b57604051634741150960e11b815260040160405180910390fd5b5f611418858585856128df565b90508015806114275750606481115b15611445576040516373c1ffd360e11b815260040160405180910390fd5b335f908152601060205260409020546103e89061146390839061375f565b1115611482576040516335aabe6960e11b815260040160405180910390fd5b5f61148e8760016124dc565b90506114b13388888888886114ab6007546001600160601b031690565b8861291e565b6114bc87835f6127ae565b5050610ce56001601755565b606060048054610b1090613700565b6114df611da6565b6114ec8484848433611dff565b6114f66001601755565b50505050565b5f33610b9e818585611c04565b611511611a28565b611519611da6565b610d8961152733835f611f09565b6129b0565b611534611a28565b61153c611da6565b601c545f81900361156057604051636d8af89f60e11b815260040160405180910390fd5b5f601c819055601d54620f4240906115789084613791565b61158291906137a8565b905061158e818361374c565b91506115b073f19308f923582a6f7c465e5ce7a9dc1bec6665b13033846123d7565b5f620186a06115c161271085613791565b6115cb91906137a8565b905080601e5f8282546115de919061375f565b909155505f9050620186a06115f5614e2086613791565b6115ff91906137a8565b905061163573f19308f923582a6f7c465e5ce7a9dc1bec6665b1307396a5399d07896f757bd4c6ef56461f58db951862846123d7565b6019546116779073f19308f923582a6f7c465e5ce7a9dc1bec6665b19030906001600160a01b031684611668878a61374c565b611672919061374c565b6123d7565b604051849033907f63be20cc905d5ba0cf65db405c4999f99f2575e521875f13a8009d6eed7a09d9905f90a350505050610bb26001601755565b6116b9613354565b6001600160a01b0383165f90815260116020908152604080832085845282528083205483526012825291829020825161010081018452815461ffff8116825260ff6201000082048116948301949094526001600160681b036301000000820481169583019590955265ffffffffffff600160801b820481166060840152600160b01b909104166080820152600182015480851660a0830152600160681b810490941660c082015292909160e0840191600160d01b9004166003811115611781576117816135c7565b6003811115611792576117926135c7565b9052509392505050565b6117a4611b4f565b610d5e8161285c565b6117b5611da6565b6117bd611a28565b5f806117c933846129da565b915091506117da81600160036127ae565b6117e3826129b0565b5050610d5e6001601755565b6001600160a01b0381165f908152601060205260409020546060908067ffffffffffffffff811115611823576118236137c7565b60405190808252806020026020018201604052801561185c57816020015b611849613398565b8152602001906001900390816118415790505b50915060015b8181116118f857604080516080810182528281526001600160a01b0386165f908152601160209081528382208583528082528483208054838601529286905290526001015491810191909152606081016118bc86846116b1565b9052836118ca60018461374c565b815181106118da576118da6137db565b602002602001018190525080806118f0906137ef565b915050611862565b5050919050565b6001600160a01b0383166119665760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084015b60405180910390fd5b6001600160a01b0382166119c75760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161195d565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6006545f62015180611a5a7f0000000000000000000000000000000000000000000000000000000066ea7bfb4261374c565b611a6491906137a8565b611a6f90600161375f565b9050818111156110d9576007546001600160601b03165f611a90848461374c565b90505f5b81811015611b2257620186a0611aad6201868485613791565b611ab791906137a8565b92506910f0cf064dd592000000831015611ad9576910f0cf064dd59200000092505b82611ae3866137ef565b60405190965086907f502806f7cf43435d83b251173d47f99095d52c72115be741aa35f763db4a8140905f90a380611b1a816137ef565b915050611a94565b5050600780546bffffffffffffffffffffffff19166001600160601b039290921691909117905560065550565b6005546001600160a01b03163314610bb257604051637550a7dd60e11b815260040160405180910390fd5b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f1981146114f65781811015611bf75760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161195d565b6114f684848484036118ff565b6001600160a01b038316611c685760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161195d565b6001600160a01b038216611cca5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161195d565b6001600160a01b0383165f9081526020819052604090205481811015611d415760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161195d565b6001600160a01b038481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36114f6565b600260175403611df85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161195d565b6002601755565b835f03611e1f57604051637133514760e11b815260040160405180910390fd5b611e2a853386611b7a565b611e348383612d42565b611e3e8585612241565b610ce585858585855f612e55565b6001600160a01b038216611ea25760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161195d565b8060025f828254611eb3919061375f565b90915550506001600160a01b0382165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383165f908152601160209081526040808320858452909152812054808203611f4c57604051632e914a1360e01b815260040160405180910390fd5b5f818152601260209081526040808320815161010081018352815461ffff8116825260ff6201000082048116958301959095526001600160681b036301000000820481169483019490945265ffffffffffff600160801b820481166060840152600160b01b909104166080820152600182015480841660a0830152600160681b810490931660c082015292909160e0840191600160d01b909104166003811115611ff857611ff86135c7565b6003811115612009576120096135c7565b905250905060018160e001516003811115612026576120266135c7565b036120445760405163130a46d560e21b815260040160405180910390fd5b60028160e00151600381111561205c5761205c6135c7565b0361207a57604051638c64dced60e01b815260040160405180910390fd5b60038160e001516003811115612092576120926135c7565b036120b0576040516351852dd960e11b815260040160405180910390fd5b42816080015165ffffffffffff161180156120db57505f8460018111156120d9576120d96135c7565b145b156120f957604051631cc0f42b60e01b815260040160405180910390fd5b80604001516001600160681b0316600c5f828254612117919061374c565b90915550612129905086838387612f5a565b9695505050505050565b6001600160a01b0384165f908152601460205260408120805484929061215a90849061375f565b925050819055508160135f828254612172919061375f565b90915550506001600160a01b038316156121ee576001600160a01b0383165f90815260156020526040812080548492906121ad90849061375f565b90915550506001600160a01b038084165f908152601660209081526040808320938816835292905290812080548492906121e890849061375f565b90915550505b826001600160a01b0316846001600160a01b03167f083dcb7ef00c446abbb046896579428741dad18e02f48b3b9706129ee06707a38484604051612233929190613807565b60405180910390a350505050565b6001600160a01b0382166122a15760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161195d565b6001600160a01b0382165f90815260208190526040902054818110156123145760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161195d565b6001600160a01b0383165f818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101611a1b565b505050565b5f818152600860205260409020546006548181106123695782612391838361374c565b61239b91906137a8565b6123a690600161375f565b6123b09084613791565b5f84815260086020526040812080549091906123cd90849061375f565b9091555050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291515f9283929088169161243a919061382b565b5f604051808303815f865af19150503d805f8114612473576040519150601f19603f3d011682016040523d82523d5f602084013e612478565b606091505b50915091508180156124a25750805115806124a25750808060200190518101906124a29190613772565b6124d45760405162461bcd60e51b815260206004820152600360248201526229aa2360e91b604482015260640161195d565b505050505050565b5f612710826124f8856c01431e0fae6d7217caa0000000613791565b6125029190613791565b61250c91906137a8565b9392505050565b5f8515806125215750605886115b1561253f5760405163ad314fd960e01b815260040160405180910390fd5b86158061254d575061271087115b1561256b5760405163282d398b60e11b815260040160405180910390fd5b612576878787613150565b90505f6040518061010001604052808961ffff1681526020018860ff168152602001836001600160681b031681526020014265ffffffffffff16815260200162015180896125c49190613791565b6125ce904261375f565b65ffffffffffff1681525f602082018190526001600160681b03861660408301526060909101526001600160a01b038a165f90815260106020526040812080549293509091829061261e906137ef565b91829055506001600160a01b038b165f908152601160209081526040808320848452825280832089815560019081018b90558984526012835292819020865181549388015192880151606089015160808a015161ffff90931662ffffff19909616959095176201000060ff909516949094029390931775ffffffffffffffffffffffffffffffffffffff000000191663010000006001600160681b039485160265ffffffffffff60801b191617600160801b65ffffffffffff958616021765ffffffffffff60b01b1916600160b01b949091169390930292909217825560a0860151928201805460c08801519483166001600160d01b031990911617600160681b94909216939093021780835560e0860151939450859391929060ff60d01b1916600160d01b836003811115612756576127566135c7565b021790555090505085858b6001600160a01b03167fa695be83040d0421ae4df4376ee65ef8a31c127f7115482b231953ec35869a07856040516127999190613681565b60405180910390a45050979650505050505050565b5f6127b984846124dc565b905060038260038111156127cf576127cf6135c7565b036127f157620186a06127e461c35083613791565b6127ee91906137a8565b90505b61281173f19308f923582a6f7c465e5ce7a9dc1bec6665b13330846123d7565b80601c5f828254612822919061375f565b9091555050600654819060405133907fd833e83f161e4ddfb1306cdf11a374a0a23393f008f9394b85999b988c232e36905f90a450505050565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b612887826131c1565b6128915f80612d42565b5f61289e83836001611f09565b6018549091506128d0906001600160a01b0316620186a06128c184611f40613791565b6128cb91906137a8565b611e4c565b61236983825f80336001612e55565b5f848411156129165781836128f4878761374c565b6128fe91906137a8565b61290990600161375f565b6129139190613791565b90505b949350505050565b600e54600954600c545b87891161298f575f5b8681101561297d576129438b8561375f565b935061295f8c8c8c8988612956896137ef565b9850888b612513565b612969908361375f565b915080612975816137ef565b915050612931565b50612988878a61375f565b9850612928565b6129a3828483600992909255600e55600c55565b5050505050505050505050565b6129ba3382611e4c565b601854610d5e906001600160a01b0316620186a06128c184611f40613791565b6001600160a01b0382165f9081526011602090815260408083208484529091528120548190808203612a1f57604051632e914a1360e01b815260040160405180910390fd5b5f818152601260209081526040808320815161010081018352815461ffff8116825260ff6201000082048116958301959095526001600160681b036301000000820481169483019490945265ffffffffffff600160801b820481166060840152600160b01b909104166080820152600182015480841660a0830152600160681b810490931660c082015292909160e0840191600160d01b909104166003811115612acb57612acb6135c7565b6003811115612adc57612adc6135c7565b905250905060018160e001516003811115612af957612af96135c7565b03612b175760405163130a46d560e21b815260040160405180910390fd5b60028160e001516003811115612b2f57612b2f6135c7565b03612b4d57604051638c64dced60e01b815260040160405180910390fd5b60038160e001516003811115612b6557612b656135c7565b03612b83576040516351852dd960e11b815260040160405180910390fd5b5f62015180826060015165ffffffffffff1642612ba0919061374c565b612baa91906137a8565b905042826080015165ffffffffffff16111580612bc75750600381105b15612be5576040516305d8f6dd60e01b815260040160405180910390fd5b5f838152601260209081526040808320600101805460ff60d01b1916600360d01b179055840151908401516001600160681b03909116919060ff16612c2a8484613791565b612c3491906137a8565b90505f620186a0612c4761c35085613791565b612c5191906137a8565b905080821115612c5f578091505b5f86815260126020526040812060010180546cffffffffffffffffffffffffff19166001600160681b038516179055600c8054859290612ca090849061374c565b90915550612cb09050828461374c565b600d5f828254612cc0919061375f565b9091555050600f80545f90612cd4906137ef565b90915550845191975061ffff90911695508690612cf1828461374c565b868b6001600160a01b03167f0a4a5a6b9bb9ce91ec0775f7273d9386e58600280767378ff02bb28b0e3202458b604051612d2d91815260200190565b60405180910390a45050505050509250929050565b6008612d4e838361375f565b1115612d6d57604051630cb7a9ff60e01b815260040160405180910390fd5b6040516301ffc9a760e01b808252600482015233906301ffc9a790602401602060405180830381865afa158015612da6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612dca9190613772565b1580612e3757506040516301ffc9a760e01b81526311686e4b60e21b600482015233906301ffc9a790602401602060405180830381865afa158015612e11573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e359190613772565b155b156110d95760405163a3600d0960e01b815260040160405180910390fd5b612e6186338784612133565b5f808415612e9c57612e77620186a06064613791565b620186a0612e85878a613791565b612e8f9190613791565b612e9991906137a8565b91505b8515612ed557612eb0620186a06064613791565b620186a0612ebe888a613791565b612ec89190613791565b612ed291906137a8565b90505b8115612ee557612ee58483611e4c565b8015612ef557612ef58882611e4c565b6040516311686e4b60e21b81526001600160a01b03891660048201526024810188905233906345a1b92c906044015f604051808303815f87803b158015612f3a575f80fd5b505af1158015612f4c573d5f803e3d5ffd5b505050505050505050505050565b5f80826001811115612f6e57612f6e6135c7565b03612f95575f848152601260205260409020600101805460ff60d01b1916600160d01b1790555b6001826001811115612fa957612fa96135c7565b03612fd0575f848152601260205260409020600101805460ff60d01b1916600160d11b1790555b5f80846080015165ffffffffffff1642111561300957613006856080015165ffffffffffff1642613001919061374c565b613240565b90505b60408501516001600160681b031660646130238383613791565b61302d91906137a8565b9250613039838261374c565b93505f85600181111561304e5761304e6135c7565b0361306657600a5f8154613061906137ef565b909155505b600185600181111561307a5761307a6135c7565b0361309257600b5f815461308d906137ef565b909155505b82156130af5782600d5f8282546130a9919061375f565b90915550505b5f8560018111156130c2576130c26135c7565b036130f7575f87815260126020526040902060010180546cffffffffffffffffffffffffff19166001600160681b0386161790555b8187896001600160a01b03167fbd866a3fbf35e201f790e87581b1afbb3165e879df5d35313a4875a70b9f3b36878760405161313d929190918252602082015260400190565b60405180910390a4505050949350505050565b5f808361315d8685613791565b6131679190613791565b9050836001146131ac57620186a061318060018661374c565b61318b606e84613791565b6131959190613791565b61319f91906137a8565b6131a9908261374c565b90505b6131b8612710826137a8565b95945050505050565b6001600160a01b0381165f908152602080805260408083203384529091529020545f1981146110d957805f0361320a576040516301fb15f960e31b815260040160405180910390fd5b6001600160a01b0382165f908152602080805260408083203384529091528120805490919061323890613846565b909155505050565b5f61324f620151806007613791565b821161325c57505f919050565b6201518061326c6007600161375f565b6132769190613791565b821161328457506001919050565b620151806132946007600261375f565b61329e9190613791565b82116132ac57506003919050565b620151806132bc6007600361375f565b6132c69190613791565b82116132d457506008919050565b620151806132e46007600461375f565b6132ee9190613791565b82116132fc57506011919050565b6201518061330c6007600561375f565b6133169190613791565b821161332457506023919050565b620151806133346007600661375f565b61333e9190613791565b821161334c57506048919050565b506063919050565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290529060e08201905b905290565b60405180608001604052805f81526020015f81526020015f8152602001613393613354565b5f5b838110156133d75781810151838201526020016133bf565b50505f910152565b602081525f82518060208401526133fd8160408501602087016133bd565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114613427575f80fd5b919050565b5f806040838503121561343d575f80fd5b61344683613411565b946020939093013593505050565b5f8060408385031215613465575f80fd5b61346e83613411565b915061347c60208401613411565b90509250929050565b5f60208284031215613495575f80fd5b61250c82613411565b5f805f606084860312156134b0575f80fd5b6134b984613411565b92506134c760208501613411565b9150604084013590509250925092565b5f602082840312156134e7575f80fd5b5035919050565b5f805f805f60a08688031215613502575f80fd5b61350b86613411565b945060208601359350604086013592506060860135915061352e60808701613411565b90509295509295909350565b5f806040838503121561354b575f80fd5b50508035926020909101359150565b5f805f805f60a0868803121561356e575f80fd5b505083359560208501359550604085013594606081013594506080013592509050565b5f805f80608085870312156135a4575f80fd5b6135ad85613411565b966020860135965060408601359560600135945092505050565b634e487b7160e01b5f52602160045260245ffd5b600481106135eb576135eb6135c7565b9052565b61ffff815116825260ff60208201511660208301526001600160681b036040820151166040830152606081015165ffffffffffff8082166060850152806080840151166080850152505060a081015161365360a08401826001600160681b03169052565b5060c081015161366e60c08401826001600160681b03169052565b5060e081015161236960e08401826135db565b6101008101610ba482846135ef565b602080825282518282018190525f919060409081850190868401855b828110156136f35781518051855286810151878601528581015186860152606090810151906136dd818701836135ef565b50506101609390930192908501906001016136ac565b5091979650505050505050565b600181811c9082168061371457607f821691505b60208210810361373257634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610ba457610ba4613738565b80820180821115610ba457610ba4613738565b5f60208284031215613782575f80fd5b8151801515811461250c575f80fd5b8082028115828204841417610ba457610ba4613738565b5f826137c257634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f6001820161380057613800613738565b5060010190565b828152604081016002831061381e5761381e6135c7565b8260208301529392505050565b5f825161383c8184602087016133bd565b9190910192915050565b5f8161385457613854613738565b505f19019056fea264697066735822122027b6d5c3dbb94d275b5dbfa3e47907441fadf2e70216925877ef9b3ac05e5e8364736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: shanghai Optimization: Yes (200 runs)
enum.sol 18 lines
//Enum
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

enum MintAction {
    CLAIM,
    BURN
}
enum MintStatus {
    ACTIVE,
    CLAIMED,
    BURNED,
    EARLYENDED
}
enum BurnSource {
    LIQUID,
    MINT
}
constant.sol 36 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

address constant TITANX = 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1;
address constant DRAGONX = 0x96a5399D07896f757Bd4c6eF56461F58DB951862;

// ===================== common ==========================================
uint256 constant DAY98 = 98;

uint256 constant SCALING_FACTOR_1e6 = 1e6;

uint256 constant SECONDS_IN_DAY = 86400;

uint256 constant TITANX_VORTEX_PERCENT = 10_000;
uint256 constant DRAGONX_VAULT_PERCENT = 20_000;
uint256 constant PERCENT_BPS = 100_000;
uint256 constant INCENTIVE_FEE_PERCENT_BASE = 1_000_000;

//Hydra Supply Variables
uint256 constant START_MAX_MINTABLE_PER_DAY = 800_000_000 ether;
uint256 constant CAPPED_MIN_DAILY_TITAN_MINTABLE = 80_000 ether;
uint256 constant DAILY_SUPPLY_MINTABLE_REDUCTION = 99_972;
uint256 constant START_MAX_MINT_COST = 1e11 ether;
uint256 constant EARLY_END_MINT_COST_PERCENT = 50_000;
uint256 constant EARLYEND_MAX_CAP_PERCENT = 50_000;

// ===================== mintInfo ==========================================
uint256 constant MAX_MINT_POWER_CAP = 10_000;
uint256 constant MAX_MINT_LENGTH = 88;
uint256 constant CLAIM_MINT_GRACE_PERIOD = 7;
uint256 constant MAX_BATCH_MINT_COUNT = 100;
uint256 constant MAX_MINT_PER_WALLET = 1000;
uint256 constant MINT_DAILY_REDUCTION = 11_0;

// ===================== burnInfo ==========================================
uint256 constant MAX_BURN_REWARD_PERCENT = 8;
Hydra.sol 580 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "../libs/constant.sol";

import "./openzeppelin/security/ReentrancyGuard.sol";
import "./openzeppelin/token/ERC20/ERC20.sol";
import "./openzeppelin/interfaces/IERC165.sol";

import "../interfaces/IHydra.sol";
import "../interfaces/IHydraOnBurn.sol";

import "../libs/TransferHelper.sol";

import "./OwnerInfo.sol";
import "./GlobalInfo.sol";
import "./MintInfo.sol";
import "./BurnInfo.sol";

//custom errors
error Hydra_InvalidCaller();
error Hydra_InsufficientProtocolFees();
error Hydra_NothingToDistribute();
error Hydra_InvalidAmount();
error Hydra_UnregisteredCA();
error Hydra_LPTokensHasMinted();
error Hydra_NotSupportedContract();
error Hydra_InvalidAddress();
error Hydra_MaxedWalletMints();
error Hydra_InvalidMintLadderInterval();
error Hydra_InvalidMintLadderRange();
error Hydra_InvalidBatchCount();
error Hydra_InvalidBurnRewardPercent();
error Hydra_InsufficientBurnAllowance();

/** @title HYDRA */
contract HYDRA is ERC20, OwnerInfo, GlobalInfo, MintInfo, BurnInfo, ReentrancyGuard {
    /** Storage Variables */
    /** @dev stores genesis wallet address */
    address private s_genesisAddress;

    /** @dev Current Hydra buy and burn contract address */
    address private s_buyAndBurnAddress;

    /** @dev Tracks Hydra buy and burn contract addresses status
     * Specifically used for burning Hydra in registered CA */
    mapping(address => bool) s_buyAndBurnAddressRegistry;

    /** @dev tracks if initial LP tokens has minted or not */
    bool private s_initialLPMinted;

    /** @dev tracks collected protocol fees until it is distributed */
    uint256 private s_undistributedFees;

    /** @dev TitanX incentive fee dividend amount */
    uint256 private s_TitanXIncentiveDividend;

    /** @dev tracks funds for vortex */
    uint256 private s_vortexTitanX;
    uint256 private s_vortexDragonX;

    /** @dev tracks user + project burn mints allowance */
    mapping(address => mapping(address => uint256)) private s_allowanceBurnMints;

    /** Events */
    event ProtocolFeeRecevied(address indexed user, uint256 indexed day, uint256 indexed fee);
    event FeesDistributed(address indexed caller, uint256 indexed amount);
    event VortexReceived(
        address indexed from,
        uint256 indexed day,
        address project,
        uint256 indexed amount
    );
    event VortexTriggered(
        uint256 indexed day,
        uint256 indexed vortexTitanX,
        uint256 indexed vortexDragonX
    );
    event ApproveBurnMints(address indexed user, address indexed project, uint256 indexed amount);

    constructor(address genesisAddress, address buyAndBurnAddress) ERC20("HYDRA", "HYDRA") {
        if (genesisAddress == address(0)) revert Hydra_InvalidAddress();
        if (buyAndBurnAddress == address(0)) revert Hydra_InvalidAddress();
        s_genesisAddress = genesisAddress;
        s_buyAndBurnAddress = buyAndBurnAddress;
        s_buyAndBurnAddressRegistry[buyAndBurnAddress] = true;
        s_TitanXIncentiveDividend = 3300;
        IERC20(TITANX).approve(address(this), type(uint256).max);
        IERC20(DRAGONX).approve(address(this), type(uint256).max);
    }

    /** @notice Set BuyAndBurn Contract Address.
     * Only owner can call this function
     * @param contractAddress BuyAndBurn contract address
     */
    function setBuyAndBurnContractAddress(address contractAddress) external onlyOwner {
        if (contractAddress == address(0)) revert Hydra_InvalidAddress();
        /* Only able to change to supported buyandburn contract address.
         * Also prevents owner from registering EOA address into s_buyAndBurnAddressRegistry and call burnHydra to burn user's tokens.
         */
        if (
            !IHydra(contractAddress).supportsInterface(IERC165.supportsInterface.selector) ||
            !IHydra(contractAddress).supportsInterface(type(IHydra).interfaceId)
        ) revert Hydra_NotSupportedContract();
        s_buyAndBurnAddress = contractAddress;
        s_buyAndBurnAddressRegistry[contractAddress] = true;
    }

    /** @notice Remove BuyAndBurn Contract Address from registry.
     * Only owner can call this function
     * @param contractAddress BuyAndBurn contract address
     */
    function unRegisterBuyAndBurnContractAddress(address contractAddress) external onlyOwner {
        if (contractAddress == address(0)) revert Hydra_InvalidAddress();
        s_buyAndBurnAddressRegistry[contractAddress] = false;
    }

    /** @notice Set to new genesis wallet. Only genesis wallet can call this function
     * @param newAddress new genesis wallet address
     */
    function setNewGenesisAddress(address newAddress) external {
        if (msg.sender != s_genesisAddress) revert Hydra_InvalidCaller();
        if (newAddress == address(0)) revert Hydra_InvalidAddress();
        s_genesisAddress = newAddress;
    }

    /** @notice set incentive fee percentage callable by owner only
     * amount is in 10000 scaling factor, which means 0.33 is 0.33 * 10000 = 3300
     * @param amount amount between 1 - 10000
     */
    function setTitanXIncentiveFeeDividend(uint256 amount) external dailyUpdate onlyOwner {
        if (amount == 0 || amount > 10000) revert Hydra_InvalidAmount();
        s_TitanXIncentiveDividend = amount;
    }

    /** @notice One-time function to mint inital LP tokens. Only callable by BuyAndBurn contract address.
     * @param amount tokens amount
     */
    function mintLPTokens(uint256 amount) external {
        if (msg.sender != s_buyAndBurnAddress) revert Hydra_InvalidCaller();
        if (s_initialLPMinted) revert Hydra_LPTokensHasMinted();
        s_initialLPMinted = true;
        _mint(s_buyAndBurnAddress, amount);
    }

    /** @notice burn Hydra in BuyAndBurn contract.
     * Only burns registered contract address
     */
    function burnCAHydra(address contractAddress) external {
        if (!s_buyAndBurnAddressRegistry[contractAddress]) revert Hydra_UnregisteredCA();
        _burn(contractAddress, balanceOf(contractAddress));
    }

    /** @notice Collect liquid TitanX as protocol fee to start mint
     * @param mintPower 1 - 100k
     * @param numOfDays mint length of 1 - 88
     */
    function startMint(
        uint256 mintPower,
        uint256 numOfDays
    ) external payable dailyUpdate nonReentrant {
        if (getUserLatestMintId(msg.sender) + 1 > MAX_MINT_PER_WALLET)
            revert Hydra_MaxedWalletMints();

        uint256 gMintPower = getGlobalMintPower() + mintPower;
        uint256 currentHRank = getGlobalHRank() + 1;
        uint256 gMinting = getTotalMinting() +
            _startMint(
                msg.sender,
                mintPower,
                numOfDays,
                getCurrentMintableHydra(),
                gMintPower,
                currentHRank,
                getBatchMintCost(mintPower, 1)
            );
        _updateMintStats(currentHRank, gMintPower, gMinting);
        _protocolFees(mintPower, 1, MintStatus.ACTIVE);
    }

    /** @notice create new mints in ladder up to 100 mints
     * @param mintPower 1 - 100k
     * @param minDay minimum mint length
     * @param maxDay maximum mint lenght
     * @param dayInterval day increase from previous mint length
     * @param countPerInterval how many mints per mint length
     */
    function batchMintLadder(
        uint256 mintPower,
        uint256 minDay,
        uint256 maxDay,
        uint256 dayInterval,
        uint256 countPerInterval
    ) external payable nonReentrant dailyUpdate {
        if (dayInterval == 0) revert Hydra_InvalidMintLadderInterval();
        if (maxDay < minDay || minDay == 0 || maxDay > MAX_MINT_LENGTH)
            revert Hydra_InvalidMintLadderRange();

        uint256 count = getBatchMintLadderCount(minDay, maxDay, dayInterval, countPerInterval);
        if (count == 0 || count > MAX_BATCH_MINT_COUNT) revert Hydra_InvalidBatchCount();
        if (getUserLatestMintId(msg.sender) + count > MAX_MINT_PER_WALLET)
            revert Hydra_MaxedWalletMints();

        uint256 mintCost = getBatchMintCost(mintPower, 1); //only need 1 mint cost for all mints info

        _startbatchMintLadder(
            msg.sender,
            mintPower,
            minDay,
            maxDay,
            dayInterval,
            countPerInterval,
            getCurrentMintableHydra(),
            mintCost
        );
        _protocolFees(mintPower, count, MintStatus.ACTIVE);
    }

    /** @notice claim a matured mint
     * @param id mint id
     */
    function claimMint(uint256 id) external dailyUpdate nonReentrant {
        _mintReward(_claimMint(msg.sender, id, MintAction.CLAIM));
    }

    /** @notice early end a mint
     * @param id mint id
     */
    function earlyEndMint(uint256 id) external payable nonReentrant dailyUpdate {
        (uint256 reward, uint256 mintPower) = _earlyEndMint(msg.sender, id);
        _protocolFees(mintPower, 1, MintStatus.EARLYENDED);
        _mintReward(reward);
    }

    /** @notice distribute collected fees to different pools, caller receive a small incentive fee  */
    function distributeFees() public dailyUpdate nonReentrant {
        uint256 accumulatedFees = s_undistributedFees;
        if (accumulatedFees == 0) revert Hydra_NothingToDistribute();
        s_undistributedFees = 0;

        //caller incentive fee
        uint256 incentiveFee = (accumulatedFees * s_TitanXIncentiveDividend) /
            INCENTIVE_FEE_PERCENT_BASE;
        accumulatedFees -= incentiveFee;
        TransferHelper.safeTransferFrom(TITANX, address(this), msg.sender, incentiveFee);

        //TitanX vortex
        uint256 vortex = (accumulatedFees * TITANX_VORTEX_PERCENT) / PERCENT_BPS;
        s_vortexTitanX += vortex;

        //DragonX vault
        uint256 vaultAmount = (accumulatedFees * DRAGONX_VAULT_PERCENT) / PERCENT_BPS;
        TransferHelper.safeTransferFrom(TITANX, address(this), DRAGONX, vaultAmount);

        //Buy and Burn funds
        TransferHelper.safeTransferFrom(
            TITANX,
            address(this),
            s_buyAndBurnAddress,
            accumulatedFees - vortex - vaultAmount
        );

        emit FeesDistributed(msg.sender, accumulatedFees);
    }

    /** @notice callable by anyone to fund the Vortex TitanX
     */
    function fundVortexTitanX(uint256 amount) external dailyUpdate nonReentrant {
        TransferHelper.safeTransferFrom(TITANX, msg.sender, address(this), amount);
        s_vortexTitanX += amount;

        emit VortexReceived(msg.sender, getCurrentContractDay(), TITANX, amount);
    }

    /** @notice callable by anyone to fund the Vortex DragonX
     */
    function fundVortexDragonX(uint256 amount) external dailyUpdate nonReentrant {
        TransferHelper.safeTransferFrom(DRAGONX, msg.sender, address(this), amount);
        s_vortexDragonX += amount;

        emit VortexReceived(msg.sender, getCurrentContractDay(), DRAGONX, amount);
    }

    //send accumulated TitanX and DragonX to buyandburn contract
    function triggerVortex() public dailyUpdate nonReentrant {
        uint256 currentContractDay = getCurrentContractDay();
        //check against cylce payout maturity day
        if (currentContractDay < getNextCyclePayoutDay(DAY98)) return;

        //update the next cycle payout day regardless of payout triggered succesfully or not
        _setNextCyclePayoutDay(DAY98);

        //TitanX vortex
        uint256 vortexTitanX = s_vortexTitanX;
        if (vortexTitanX != 0) {
            s_vortexTitanX = 0;
            TransferHelper.safeTransferFrom(
                TITANX,
                address(this),
                s_buyAndBurnAddress,
                vortexTitanX
            );
        }

        //DragonX vortex
        uint256 vortexDragonX = s_vortexDragonX;
        if (vortexDragonX != 0) {
            s_vortexDragonX = 0;
            TransferHelper.safeTransferFrom(
                DRAGONX,
                address(this),
                s_buyAndBurnAddress,
                vortexDragonX
            );
        }

        emit VortexTriggered(currentContractDay, vortexTitanX, vortexDragonX);
    }

    //Private Functions
    /** @dev calcualte required protocol fees */
    function _protocolFees(uint256 mintPower, uint256 count, MintStatus status) private {
        uint256 protocolFee = getBatchMintCost(mintPower, count);
        if (status == MintStatus.EARLYENDED)
            protocolFee = (protocolFee * EARLY_END_MINT_COST_PERCENT) / PERCENT_BPS;

        TransferHelper.safeTransferFrom(TITANX, msg.sender, address(this), protocolFee);
        s_undistributedFees += protocolFee;

        emit ProtocolFeeRecevied(msg.sender, getCurrentContractDay(), protocolFee);
    }

    /** @dev burn liquid Hydra through other project.
     * called by other contracts for proof of burn 2.0 with up to 8% for both builder fee and user rebate
     * @param user user address
     * @param amount liquid Hydra amount
     * @param userRebatePercentage percentage for user rebate in liquid Hydra (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid Hydra (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function _burnLiquidHydra(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) private {
        if (amount == 0) revert Hydra_InvalidAmount();
        _spendAllowance(user, msg.sender, amount);
        _burnbefore(userRebatePercentage, rewardPaybackPercentage);
        _burn(user, amount);
        _burnAfter(
            user,
            amount,
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress,
            BurnSource.LIQUID
        );
    }

    /** @dev burn mint through other project.
     * called by other contracts for proof of burn 2.0
     * burn mint has no builder reward and no user rebate
     * @param user user address
     * @param id mint id
     */
    function _burnMint(address user, uint256 id) private {
        _spendBurnMintAllowance(user);
        _burnbefore(0, 0);
        uint256 amount = _claimMint(user, id, MintAction.BURN);
        _mint(s_genesisAddress, (amount * 8_000) / PERCENT_BPS);
        _burnAfter(user, amount, 0, 0, msg.sender, BurnSource.MINT);
    }

    /** @dev perform checks before burning starts.
     * check reward percentage and check if called by supported contract
     * @param userRebatePercentage percentage for user rebate
     * @param rewardPaybackPercentage percentage for builder fee
     */
    function _burnbefore(
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage
    ) private view {
        if (rewardPaybackPercentage + userRebatePercentage > MAX_BURN_REWARD_PERCENT)
            revert Hydra_InvalidBurnRewardPercent();

        //Only supported contracts is allowed to call this function
        if (
            !IERC165(msg.sender).supportsInterface(IERC165.supportsInterface.selector) ||
            !IERC165(msg.sender).supportsInterface(type(IHydraOnBurn).interfaceId)
        ) revert Hydra_NotSupportedContract();
    }

    /** @dev update burn stats and mint reward to builder or user if applicable
     * @param user user address
     * @param amount Hydra amount burned
     * @param userRebatePercentage percentage for user rebate in liquid Hydra (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid Hydra (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     * @param source liquid/mint
     */
    function _burnAfter(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress,
        BurnSource source
    ) private {
        _updateBurnAmount(user, msg.sender, amount, source);

        uint256 devFee;
        uint256 userRebate;
        if (rewardPaybackPercentage != 0)
            devFee = (amount * rewardPaybackPercentage * PERCENT_BPS) / (100 * PERCENT_BPS);
        if (userRebatePercentage != 0)
            userRebate = (amount * userRebatePercentage * PERCENT_BPS) / (100 * PERCENT_BPS);

        if (devFee != 0) _mint(rewardPaybackAddress, devFee);
        if (userRebate != 0) _mint(user, userRebate);

        IHydraOnBurn(msg.sender).onBurn(user, amount);
    }

    /** @dev mint reward to user
     * @param reward Hydra amount
     */
    function _mintReward(uint256 reward) private {
        _mint(msg.sender, reward);
        _mint(s_genesisAddress, (reward * 8_000) / PERCENT_BPS);
    }

    /** @dev reduce user's allowance for caller (spender/project) by 1 (burn 1 mint at a time)
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     * @param user user address
     */
    function _spendBurnMintAllowance(address user) private {
        uint256 currentAllowance = allowanceBurnMints(user, msg.sender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance == 0) revert Hydra_InsufficientBurnAllowance();
            --s_allowanceBurnMints[user][msg.sender];
        }
    }

    //views
    /** @notice returns user's burn mints allowance of a project
     * @param user user address
     * @param spender project address
     */
    function allowanceBurnMints(address user, address spender) public view returns (uint256) {
        return s_allowanceBurnMints[user][spender];
    }

    /** @notice Returns current buy and burn contract address
     * @return address current buy and burn contract address
     */
    function getBuyAndBurnAddress() public view returns (address) {
        return s_buyAndBurnAddress;
    }

    /** @notice Returns status of the given address
     * @return status 0 (INACTIVE) or 1 (Active)
     */
    function getBuyAndBurnAddressRegistry(address contractAddress) public view returns (bool) {
        return s_buyAndBurnAddressRegistry[contractAddress];
    }

    /** @notice get current incentive fee dividend
     * @return amount
     */
    function getTitanXIncentiveDividend() public view returns (uint256) {
        return s_TitanXIncentiveDividend;
    }

    /** @notice get undistributed TitanX balance
     * @return amount TitanX
     */
    function getUndistributedFees() public view returns (uint256) {
        return s_undistributedFees;
    }

    /** @notice get vortex TitanX balance
     * @return amount TitanX
     */
    function getVortexTitanX() public view returns (uint256) {
        return s_vortexTitanX;
    }

    /** @notice get vortex DragonX balance
     * @return amount DragonX
     */
    function getVortexDragonX() public view returns (uint256) {
        return s_vortexDragonX;
    }

    //Public functions for devs to intergrate with Hydra
    /** @notice allow anyone to sync dailyUpdate manually */
    function manualDailyUpdate() public dailyUpdate {}

    /** @notice Burn Hydra tokens and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to specified address
     * @param user user address
     * @param amount Hydra amount
     * @param userRebatePercentage percentage for user rebate in liquid Hydra (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid Hydra (0 - 8)
     * @param rewardPaybackAddress builder can opt to receive fee in another address
     */
    function burnTokensToPayAddress(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage,
        address rewardPaybackAddress
    ) public nonReentrant {
        _burnLiquidHydra(
            user,
            amount,
            userRebatePercentage,
            rewardPaybackPercentage,
            rewardPaybackAddress
        );
    }

    /** @notice Burn Hydra tokens and creates Proof-Of-Burn record to be used by connected DeFi and fee is paid to specified address
     * @param user user address
     * @param amount Hydra amount
     * @param userRebatePercentage percentage for user rebate in liquid Hydra (0 - 8)
     * @param rewardPaybackPercentage percentage for builder fee in liquid Hydra (0 - 8)
     */
    function burnTokens(
        address user,
        uint256 amount,
        uint256 userRebatePercentage,
        uint256 rewardPaybackPercentage
    ) public nonReentrant {
        _burnLiquidHydra(user, amount, userRebatePercentage, rewardPaybackPercentage, msg.sender);
    }

    /** @notice allows user to burn liquid Hydra directly from contract
     * @param amount Hydra amount
     */
    function userBurnTokens(uint256 amount) public nonReentrant {
        if (amount == 0) revert Hydra_InvalidAmount();
        _burn(msg.sender, amount);
        _updateBurnAmount(msg.sender, address(0), amount, BurnSource.LIQUID);
    }

    /** @notice Burn mint and creates Proof-Of-Burn record to be used by connected DeFi.
     * Burn mint has no project reward or user rebate
     * @param user user address
     * @param id mint id
     */
    function burnMint(address user, uint256 id) public dailyUpdate nonReentrant {
        _burnMint(user, id);
    }

    /** @notice allows user to burn mint directly from contract
     * @param id mint id
     */
    function userBurnMint(uint256 id) public dailyUpdate nonReentrant {
        _updateBurnAmount(
            msg.sender,
            address(0),
            _claimMint(msg.sender, id, MintAction.BURN),
            BurnSource.MINT
        );
    }

    /** @notice Sets `amount` as the allowance of `spender` over the caller's (user) mints.
     * @param spender contract address
     * @param amount allowance amount
     */
    function approveBurnMints(address spender, uint256 amount) public returns (bool) {
        if (spender == address(0)) revert Hydra_InvalidAddress();
        s_allowanceBurnMints[msg.sender][spender] = amount;
        emit ApproveBurnMints(msg.sender, spender, amount);
        return true;
    }
}
IHydra.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IHydra {
    function mintLPTokens(uint256 amount) external;

    function burnCAHydra(address contractAddress) external;

    function fundVortexTitanX(uint256 amount) external;

    function fundVortexDragonX(uint256 amount) external;

    function supportsInterface(bytes4 interfaceId) external returns (bool);
}
BurnInfo.sol 95 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "../libs/constant.sol";
import "../libs/enum.sol";

/**
 * @title BurnInfo
 * @dev this contract is meant to be inherited into main contract
 * @notice It has the variables and functions specifically for tracking burn amount
 */

abstract contract BurnInfo {
    //Variables
    //track the total Hydra burn amount
    uint256 private s_totalHydraBurned;

    //mappings
    //track wallet address -> total Hydra burn amount
    mapping(address => uint256) private s_userBurnAmount;
    //track contract/project address -> total Hydra burn amount
    mapping(address => uint256) private s_project_BurnAmount;
    //track contract/project address, wallet address -> total Hydra burn amount
    mapping(address => mapping(address => uint256)) private s_projectUser_BurnAmount;

    //events
    /** @dev log user burn Hydra event
     * project can be address(0) if user burns Hydra directly from Hydra contract
     * Source 0=Liquid, 1=Mint
     */
    event HydraBurned(
        address indexed user,
        address indexed project,
        uint256 amount,
        BurnSource source
    );

    //functions
    /** @dev update the burn amount
     * @param user wallet address
     * @param project contract address
     * @param amount Hydra amount burned
     * @param source burn source LIQUID/MINT
     */
    function _updateBurnAmount(
        address user,
        address project,
        uint256 amount,
        BurnSource source
    ) internal {
        s_userBurnAmount[user] += amount;
        s_totalHydraBurned += amount;

        if (project != address(0)) {
            s_project_BurnAmount[project] += amount;
            s_projectUser_BurnAmount[project][user] += amount;
        }

        emit HydraBurned(user, project, amount, source);
    }

    //views
    /** @notice return total burned Hydra amount from all users burn or projects burn
     * @return totalBurnAmount returns entire burned Hydra
     */
    function getTotalBurnTotal() public view returns (uint256) {
        return s_totalHydraBurned;
    }

    /** @notice return user address total burned Hydra
     * @return userBurnAmount returns user address total burned Hydra
     */
    function getUserBurnTotal(address user) public view returns (uint256) {
        return s_userBurnAmount[user];
    }

    /** @notice return project address total burned Hydra amount
     * @return projectTotalBurnAmount returns project total burned Hydra
     */
    function getProjectBurnTotal(address contractAddress) public view returns (uint256) {
        return s_project_BurnAmount[contractAddress];
    }

    /** @notice return user address total burned Hydra amount via a project address
     * @param contractAddress project address
     * @param user user address
     * @return projectUserTotalBurnAmount returns user address total burned Hydra via a project address
     */
    function getProjectUserBurnTotal(
        address contractAddress,
        address user
    ) public view returns (uint256) {
        return s_projectUser_BurnAmount[contractAddress][user];
    }
}
MintInfo.sol 400 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "../libs/enum.sol";
import "../libs/constant.sol";
import "../libs/calcFunctions.sol";

//custom errors
error Hydra_InvalidMintLength();
error Hydra_InvalidMintPower();
error Hydra_NoMintExists();
error Hydra_MintHasClaimed();
error Hydra_MintNotMature();
error Hydra_MintHasBurned();
error Hydra_MintMaturityNotMet();
error Hydra_MintHasEnded();

abstract contract MintInfo {
    //variables
    /** @dev track global hRank */
    uint256 private s_globalHRank;
    /** @dev track total mint claimed */
    uint256 private s_globalMintClaim;
    /** @dev track total mint burned */
    uint256 private s_globalMintBurn;
    /** @dev track total Hydra minting */
    uint256 private s_globalHydraMinting;
    /** @dev track total Hydra penalty */
    uint256 private s_globalHydraMintPenalty;
    /** @dev track global mint power */
    uint256 private s_globalMintPower;
    /** @dev track total mint early ended */
    uint256 private s_globalMintEarlyEnded;

    //mappings
    /** @dev track address => mintId */
    mapping(address => uint256) private s_addressMId;
    /** @dev track address, mintId => hRank info (gTrank, gMintPower) */
    mapping(address => mapping(uint256 => HRankInfo)) private s_addressMIdToHRankInfo;
    /** @dev track global hRank => mintInfo*/
    mapping(uint256 => UserMintInfo) private s_hRankToMintInfo;

    //structs
    struct UserMintInfo {
        uint16 mintPower;
        uint8 numOfDays;
        uint104 mintableHydra;
        uint48 mintStartTs;
        uint48 maturityTs;
        uint104 mintedHydra;
        uint104 mintCost;
        MintStatus status;
    }

    struct HRankInfo {
        uint256 hRank;
        uint256 gMintPower;
    }

    struct UserMint {
        uint256 mId;
        uint256 hRank;
        uint256 gMintPower;
        UserMintInfo mintInfo;
    }

    //events
    event MintStarted(
        address indexed user,
        uint256 indexed hRank,
        uint256 indexed gMintpower,
        UserMintInfo userMintInfo
    );

    event MintClaimed(
        address indexed user,
        uint256 indexed hRank,
        uint256 rewardMinted,
        uint256 indexed penalty,
        uint256 mintPenalty
    );

    event MintEarlyEnded(
        address indexed user,
        uint256 indexed hRank,
        uint256 rewardMinted,
        uint256 indexed penalty
    );

    //functions
    /** @dev create a new mint
     * @param user user address
     * @param mintPower mint power
     * @param numOfDays mint lenght
     * @param mintableHydra mintable Hydra
     * @param gMintPower global mint power
     * @param currentHRank current global hRank
     * @param mintCost actual mint cost paid for a mint
     */
    function _startMint(
        address user,
        uint256 mintPower,
        uint256 numOfDays,
        uint256 mintableHydra,
        uint256 gMintPower,
        uint256 currentHRank,
        uint256 mintCost
    ) internal returns (uint256 mintable) {
        if (numOfDays == 0 || numOfDays > MAX_MINT_LENGTH) revert Hydra_InvalidMintLength();
        if (mintPower == 0 || mintPower > MAX_MINT_POWER_CAP) revert Hydra_InvalidMintPower();

        //calculate mint reward up front with the provided params
        mintable = calculateMintReward(mintPower, numOfDays, mintableHydra);

        //store variables into mint info
        UserMintInfo memory userMintInfo = UserMintInfo({
            mintPower: uint16(mintPower),
            numOfDays: uint8(numOfDays),
            mintableHydra: uint104(mintable),
            mintStartTs: uint48(block.timestamp),
            maturityTs: uint48(block.timestamp + (numOfDays * SECONDS_IN_DAY)),
            mintedHydra: 0,
            mintCost: uint104(mintCost),
            status: MintStatus.ACTIVE
        });

        /** s_addressMId[user] tracks mintId for each addrress
         * s_addressMIdToHRankInfo[user][id] tracks current mint hRank and gPowerMint
         *  s_hRankToMintInfo[currentHRank] stores mint info
         */
        uint256 id = ++s_addressMId[user];
        s_addressMIdToHRankInfo[user][id].hRank = currentHRank;
        s_addressMIdToHRankInfo[user][id].gMintPower = gMintPower;
        s_hRankToMintInfo[currentHRank] = userMintInfo;

        emit MintStarted(user, currentHRank, gMintPower, userMintInfo);
    }

    /** @dev create new mint in a batch of up to max 100 mints with different mint length
     * @param user user address
     * @param mintPower mint power
     * @param minDay minimum start day
     * @param maxDay maximum end day
     * @param dayInterval days interval between each new mint length
     * @param countPerInterval number of mint(s) to create in each mint length interval
     * @param mintableHydra mintable Hydra
     * @param mintCost actual mint cost paid for a mint
     */
    function _startbatchMintLadder(
        address user,
        uint256 mintPower,
        uint256 minDay,
        uint256 maxDay,
        uint256 dayInterval,
        uint256 countPerInterval,
        uint256 mintableHydra,
        uint256 mintCost
    ) internal {
        uint256 gMintPower = s_globalMintPower;
        uint256 currentHRank = s_globalHRank;
        uint256 gMinting = s_globalHydraMinting;

        /**first for loop is used to determine mint length
         * minDay is the starting mint length
         * maxDay is the max mint length where it stops
         * dayInterval increases the minDay for the next mint
         */
        for (; minDay <= maxDay; minDay += dayInterval) {
            /**first for loop is used to determine mint length
             * second for loop is to create number mints per mint length
             */
            for (uint256 j = 0; j < countPerInterval; j++) {
                gMintPower += mintPower;
                gMinting += _startMint(
                    user,
                    mintPower,
                    minDay,
                    mintableHydra,
                    gMintPower,
                    ++currentHRank,
                    mintCost
                );
            }
        }
        _updateMintStats(currentHRank, gMintPower, gMinting);
    }

    /** @dev update variables
     * @param currentHRank current hRank
     * @param gMintPower current global mint power
     * @param gMinting current global minting
     */
    function _updateMintStats(uint256 currentHRank, uint256 gMintPower, uint256 gMinting) internal {
        s_globalHRank = currentHRank;
        s_globalMintPower = gMintPower;
        s_globalHydraMinting = gMinting;
    }

    /** @dev calculate reward for claim mint or burn mint.
     * Claim mint has maturity check while burn mint would bypass maturity check.
     * @param user user address
     * @param id mint id
     * @param action claim mint or burn mint
     * @return reward calculated final reward after all bonuses and penalty (if any)
     */
    function _claimMint(
        address user,
        uint256 id,
        MintAction action
    ) internal returns (uint256 reward) {
        uint256 hRank = s_addressMIdToHRankInfo[user][id].hRank;
        if (hRank == 0) revert Hydra_NoMintExists();

        UserMintInfo memory mint = s_hRankToMintInfo[hRank];
        if (mint.status == MintStatus.CLAIMED) revert Hydra_MintHasClaimed();
        if (mint.status == MintStatus.BURNED) revert Hydra_MintHasBurned();
        if (mint.status == MintStatus.EARLYENDED) revert Hydra_MintHasEnded();

        //Only check maturity for claim mint action, burn mint bypass this check
        if (mint.maturityTs > block.timestamp && action == MintAction.CLAIM)
            revert Hydra_MintNotMature();

        s_globalHydraMinting -= mint.mintableHydra;
        reward = _calculateClaimReward(user, hRank, mint, action);
    }

    /** @dev calculate final reward with bonuses and penalty (if any)
     * @param user user address
     * @param hRank mint's hRank
     * @param userMintInfo mint's info
     * @param action claim mint or burn mint
     * @return reward calculated final reward after all bonuses and penalty (if any)
     */
    function _calculateClaimReward(
        address user,
        uint256 hRank,
        UserMintInfo memory userMintInfo,
        MintAction action
    ) private returns (uint256 reward) {
        if (action == MintAction.CLAIM) s_hRankToMintInfo[hRank].status = MintStatus.CLAIMED;
        if (action == MintAction.BURN) s_hRankToMintInfo[hRank].status = MintStatus.BURNED;

        uint256 penaltyAmount;
        uint256 penalty;

        //only calculate penalty when current block timestamp > maturity timestamp
        if (block.timestamp > userMintInfo.maturityTs) {
            penalty = calculateClaimMintPenalty(block.timestamp - userMintInfo.maturityTs);
        }

        uint256 mintableSupply = uint256(userMintInfo.mintableHydra);
        penaltyAmount = (mintableSupply * penalty) / 100;
        reward = mintableSupply - penaltyAmount;

        if (action == MintAction.CLAIM) ++s_globalMintClaim;
        if (action == MintAction.BURN) ++s_globalMintBurn;
        if (penaltyAmount != 0) s_globalHydraMintPenalty += penaltyAmount;

        //only stored minted amount for claim mint
        if (action == MintAction.CLAIM) s_hRankToMintInfo[hRank].mintedHydra = uint104(reward);

        emit MintClaimed(user, hRank, reward, penalty, penaltyAmount);
    }

    /**
     * @dev early end a mint that hasn't mature and already matured at least 8 days
     * @param user user address
     * @param id mint id
     * @return reward calculated reward based on number of days matured, capped at 50% max
     * @return mintPower mint's power
     */
    function _earlyEndMint(
        address user,
        uint256 id
    ) internal returns (uint256 reward, uint256 mintPower) {
        uint256 hRank = s_addressMIdToHRankInfo[user][id].hRank;
        if (hRank == 0) revert Hydra_NoMintExists();

        UserMintInfo memory mint = s_hRankToMintInfo[hRank];
        if (mint.status == MintStatus.CLAIMED) revert Hydra_MintHasClaimed();
        if (mint.status == MintStatus.BURNED) revert Hydra_MintHasBurned();
        if (mint.status == MintStatus.EARLYENDED) revert Hydra_MintHasEnded();

        //revert if miner has matured or less than 3 days
        uint256 daysMatured = (block.timestamp - mint.mintStartTs) / 1 days;
        if (mint.maturityTs <= block.timestamp || daysMatured < 3)
            revert Hydra_MintMaturityNotMet();

        s_hRankToMintInfo[hRank].status = MintStatus.EARLYENDED;

        uint256 mintableSupply = mint.mintableHydra;
        uint256 earlyEndSupply = (mintableSupply * daysMatured) / mint.numOfDays;
        uint256 maxCapAmount = (mintableSupply * EARLYEND_MAX_CAP_PERCENT) / PERCENT_BPS;
        if (earlyEndSupply > maxCapAmount) earlyEndSupply = maxCapAmount;

        s_hRankToMintInfo[hRank].mintedHydra = uint104(earlyEndSupply);
        s_globalHydraMinting -= mintableSupply;
        s_globalHydraMintPenalty += mintableSupply - earlyEndSupply;
        ++s_globalMintEarlyEnded;
        reward = earlyEndSupply;
        mintPower = mint.mintPower;

        emit MintEarlyEnded(user, hRank, reward, mintableSupply - earlyEndSupply);
    }

    //views
    /** @notice Returns the latest Mint Id of an address
     * @param user address
     * @return mId latest mint id
     */
    function getUserLatestMintId(address user) public view returns (uint256) {
        return s_addressMId[user];
    }

    /** @notice Returns mint info of an address + mint id
     * @param user address
     * @param id mint id
     * @return mintInfo user mint info
     */
    function getUserMintInfo(
        address user,
        uint256 id
    ) public view returns (UserMintInfo memory mintInfo) {
        return s_hRankToMintInfo[s_addressMIdToHRankInfo[user][id].hRank];
    }

    /** @notice Return all mints info of an address
     * @param user address
     * @return mintInfos all mints info of an address including mint id, hRank and gMintPower
     */
    function getUserMints(address user) public view returns (UserMint[] memory mintInfos) {
        uint256 count = s_addressMId[user];
        mintInfos = new UserMint[](count);

        for (uint256 i = 1; i <= count; i++) {
            mintInfos[i - 1] = UserMint({
                mId: i,
                hRank: s_addressMIdToHRankInfo[user][i].hRank,
                gMintPower: s_addressMIdToHRankInfo[user][i].gMintPower,
                mintInfo: getUserMintInfo(user, i)
            });
        }
    }

    /** @notice Return total mints burned
     * @return totalMintBurned total mints burned
     */
    function getTotalMintBurn() public view returns (uint256) {
        return s_globalMintBurn;
    }

    /** @notice Return current gobal hRank
     * @return globalHRank global hRank
     */
    function getGlobalHRank() public view returns (uint256) {
        return s_globalHRank;
    }

    /** @notice Return current gobal mint power
     * @return globalMintPower global mint power
     */
    function getGlobalMintPower() public view returns (uint256) {
        return s_globalMintPower;
    }

    /** @notice Return total mints claimed
     * @return totalMintClaimed total mints claimed
     */
    function getTotalMintClaim() public view returns (uint256) {
        return s_globalMintClaim;
    }

    /** @notice Return total active mints (exluded claimed and burned mints)
     * @return totalActiveMints total active mints
     */
    function getTotalActiveMints() public view returns (uint256) {
        return s_globalHRank - s_globalMintClaim - s_globalMintBurn;
    }

    /** @notice Return total minting Hydra
     * @return totalMinting total minting Hydra
     */
    function getTotalMinting() public view returns (uint256) {
        return s_globalHydraMinting;
    }

    /** @notice Return total mint penalty
     * @return totalHydraPenalty total mint penalty
     */
    function getTotalMintPenalty() public view returns (uint256) {
        return s_globalHydraMintPenalty;
    }

    /** @notice Return total early ended mint
     * @return totalHydraPenalty total Hydra penalty
     */
    function getTotalMintEarlyEnded() public view returns (uint256) {
        return s_globalMintEarlyEnded;
    }
}
calcFunctions.sol 68 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "./constant.sol";

//Hydra
/**@notice get batch mint ladder total count
 * @param minDay minimum mint length
 * @param maxDay maximum mint length, cap at 280
 * @param dayInterval day increase from previous mint length
 * @param countPerInterval number of mints per minth length
 * @return count total mints
 */
function getBatchMintLadderCount(
    uint256 minDay,
    uint256 maxDay,
    uint256 dayInterval,
    uint256 countPerInterval
) pure returns (uint256 count) {
    if (maxDay > minDay) {
        count = (((maxDay - minDay) / dayInterval) + 1) * countPerInterval;
    }
}

/** @notice get batch mint cost
 * @param mintPower mint power (1 - 100)
 * @param count number of mints
 * @return mintCost total mint cost
 */
function getBatchMintCost(uint256 mintPower, uint256 count) pure returns (uint256) {
    return (START_MAX_MINT_COST * mintPower * count) / MAX_MINT_POWER_CAP;
}

//MintInfo
/** @notice the formula to calculate mint reward at create new mint
 * @param mintPower mint power 1 - 10000
 * @param numOfDays mint length 1 - 88
 * @param mintableHydra current contract day mintable Hydra
 * @return reward base Hydra amount
 */
function calculateMintReward(
    uint256 mintPower,
    uint256 numOfDays,
    uint256 mintableHydra
) pure returns (uint256 reward) {
    uint256 baseReward = (mintableHydra * mintPower * numOfDays);

    if (numOfDays != 1)
        baseReward -= (baseReward * MINT_DAILY_REDUCTION * (numOfDays - 1)) / PERCENT_BPS;

    reward = baseReward / MAX_MINT_POWER_CAP;
}

/**
 * @dev Return penalty percentage based on number of days late after the grace period of 7 days
 * @param secsLate seconds late (block timestamp - maturity timestamp)
 * @return penalty penalty in percentage
 */
function calculateClaimMintPenalty(uint256 secsLate) pure returns (uint256 penalty) {
    if (secsLate <= CLAIM_MINT_GRACE_PERIOD * SECONDS_IN_DAY) return 0;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 1) * SECONDS_IN_DAY) return 1;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 2) * SECONDS_IN_DAY) return 3;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 3) * SECONDS_IN_DAY) return 8;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 4) * SECONDS_IN_DAY) return 17;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 5) * SECONDS_IN_DAY) return 35;
    if (secsLate <= (CLAIM_MINT_GRACE_PERIOD + 6) * SECONDS_IN_DAY) return 72;
    return 99;
}
OwnerInfo.sol 55 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "./openzeppelin/utils/Context.sol";

error Hydra_NotOnwer();

abstract contract OwnerInfo is Context {
    address private s_owner;

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        s_owner = msg.sender;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (s_owner != msg.sender) revert Hydra_NotOnwer();
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        _setOwner(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 onlyOwner {
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        s_owner = newOwner;
    }
}
TransferHelper.sol 71 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

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

library TransferHelper {
    /// @notice Transfers tokens from the targeted address to the given destination
    /// @notice Errors with 'STF' if transfer fails
    /// @param token The contract address of the token to be transferred
    /// @param from The originating address from which the tokens will be transferred
    /// @param to The destination address of the transfer
    /// @param value The amount to be transferred
    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(
                IERC20.transferFrom.selector,
                from,
                to,
                value
            )
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "STF"
        );
    }

    /// @notice Transfers tokens from msg.sender to a recipient
    /// @dev Errors with ST if transfer fails
    /// @param token The contract address of the token which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(IERC20.transfer.selector, to, value)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "ST"
        );
    }

    /// @notice Approves the stipulated contract to spend the given allowance in the given token
    /// @dev Errors with 'SA' if transfer fails
    /// @param token The contract address of the token to be approved
    /// @param to The target of the approval
    /// @param value The amount of the given token the target will be allowed to spend
    function safeApprove(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(IERC20.approve.selector, to, value)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "SA"
        );
    }

    /// @notice Transfers ETH to the recipient address
    /// @dev Fails with `STE`
    /// @param to The destination of the transfer
    /// @param value The value to be transferred
    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, "STE");
    }
}
GlobalInfo.sol 134 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import "../libs/constant.sol";

abstract contract GlobalInfo {
    //Variables
    //deployed timestamp
    uint256 private immutable i_genesisTs;

    /** @dev track current contract day */
    uint256 private s_currentContractDay;
    /** @dev mintableHydra starts 800m ether decreases and capped at 80k ether, uint96 has enough size */
    uint96 private s_currentMintableHydra;

    /** @dev track when is the next cycle payout day for each cycle day
     * eg. s_nextCyclePayoutDay[DAY98] = 98
     *     s_nextCyclePayoutDay[DAY369] = 369
     */
    mapping(uint256 => uint256) s_nextCyclePayoutDay;

    //event
    event GlobalDailyUpdateStats(uint256 indexed day, uint256 indexed mintableHydra);

    /** @dev Update variables in terms of day, modifier is used in all external/public functions (exclude view)
     * Every interaction to the contract would run this function to update variables
     */
    modifier dailyUpdate() {
        _dailyUpdate();
        _;
    }

    constructor() {
        i_genesisTs = block.timestamp;
        s_currentContractDay = 1;
        s_currentMintableHydra = uint96(START_MAX_MINTABLE_PER_DAY);
        s_nextCyclePayoutDay[DAY98] = DAY98;
    }

    /** @dev calculate and update variables daily and reset triggers flag */
    function _dailyUpdate() private {
        uint256 currentContractDay = s_currentContractDay;
        uint256 currentBlockDay = ((block.timestamp - i_genesisTs) / 1 days) + 1;

        if (currentBlockDay > currentContractDay) {
            //get last day info ready for calculation
            uint256 newMintableHydra = s_currentMintableHydra;
            uint256 dayDifference = currentBlockDay - currentContractDay;

            /** Reason for a for loop to update Mint supply
             * Ideally, user interaction happens daily, so Mint supply is synced in every day
             *      (cylceDifference = 1)
             * However, if there's no interaction for more than 1 day, then
             *      Mint supply isn't updated correctly due to cylceDifference > 1 day
             * Eg. 2 days of no interaction, then interaction happens in 3rd day.
             *     It's incorrect to only decrease the Mint supply one time as now it's in 3rd day.
             *   And if this happens, there will be no tracked data for the skipped days as not needed
             */
            for (uint256 i; i < dayDifference; i++) {
                newMintableHydra =
                    (newMintableHydra * DAILY_SUPPLY_MINTABLE_REDUCTION) /
                    PERCENT_BPS;

                if (newMintableHydra < CAPPED_MIN_DAILY_TITAN_MINTABLE) {
                    newMintableHydra = CAPPED_MIN_DAILY_TITAN_MINTABLE;
                }

                emit GlobalDailyUpdateStats(++currentContractDay, newMintableHydra);
            }

            s_currentMintableHydra = uint96(newMintableHydra);
            s_currentContractDay = currentBlockDay;
        }
    }

    /** @dev calculate and update the next payout day for specified cycleNo
     * the formula will update the payout day based on current contract day
     * this is to make sure the value is correct when for some reason has skipped more than one cycle payout
     * @param cycleNo cycle day 98
     */
    function _setNextCyclePayoutDay(uint256 cycleNo) internal {
        uint256 maturityDay = s_nextCyclePayoutDay[cycleNo];
        uint256 currentContractDay = s_currentContractDay;
        if (currentContractDay >= maturityDay) {
            s_nextCyclePayoutDay[cycleNo] +=
                cycleNo *
                (((currentContractDay - maturityDay) / cycleNo) + 1);
        }
    }

    /** Views */
    /** @notice Returns contract deployment block timestamp
     * @return timestamp in seconds
     */
    function genesisTs() public view returns (uint256) {
        return i_genesisTs;
    }

    /** @notice Returns current block timestamp
     * @return currentBlockTs current block timestamp
     */
    function getCurrentBlockTimeStamp() public view returns (uint256) {
        return block.timestamp;
    }

    /** @notice Returns current contract day
     * @return currentContractDay current contract day
     */
    function getCurrentContractDay() public view returns (uint256) {
        return s_currentContractDay;
    }

    /** @notice Returns current mint cost
     * @return currentMintCost current block timestamp
     */
    function getCurrentMintCost() public pure returns (uint256) {
        return START_MAX_MINT_COST;
    }

    /** @notice Returns current mintable Hydra
     * @return currentMintableHydra current mintable Hydra
     */
    function getCurrentMintableHydra() public view returns (uint256) {
        return s_currentMintableHydra;
    }

    /** @notice Returns next payout day for the specified cycle day
     * @param cycleNo cycle day 98
     * @return nextPayoutDay next payout day
     */
    function getNextCyclePayoutDay(uint256 cycleNo) public view returns (uint256) {
        return s_nextCyclePayoutDay[cycleNo];
    }
}
IHydraOnBurn.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IHydraOnBurn {
    function onBurn(address user, uint256 amount) external;
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @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 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 Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
ERC20.sol 329 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(
        address owner,
        address spender
    ) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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);
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view 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

allowance 0xdd62ed3e → uint256
allowanceBurnMints 0x544a6c59 → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
genesisTs 0xe3af6d0a → uint256
getBuyAndBurnAddress 0xe90225cb → address
getBuyAndBurnAddressRegistry 0x5109c57c → bool
getCurrentBlockTimeStamp 0xd9af94af → uint256
getCurrentContractDay 0x1ae409c0 → uint256
getCurrentMintCost 0x6f609633 → uint256
getCurrentMintableHydra 0xa1bbd7b9 → uint256
getGlobalHRank 0x7ec26dca → uint256
getGlobalMintPower 0x92c1df54 → uint256
getNextCyclePayoutDay 0x2d02347a → uint256
getProjectBurnTotal 0x29b70d7a → uint256
getProjectUserBurnTotal 0x1371bb40 → uint256
getTitanXIncentiveDividend 0xe7456461 → uint256
getTotalActiveMints 0x23639385 → uint256
getTotalBurnTotal 0x54f5d028 → uint256
getTotalMintBurn 0x2f771951 → uint256
getTotalMintClaim 0xf80b0cfb → uint256
getTotalMintEarlyEnded 0xe45fa5a5 → uint256
getTotalMintPenalty 0xdff96e9a → uint256
getTotalMinting 0x216630b4 → uint256
getUndistributedFees 0x67921d68 → uint256
getUserBurnTotal 0xe8052174 → uint256
getUserLatestMintId 0xc50312ad → uint256
getUserMintInfo 0xd819e198 → tuple
getUserMints 0xffb75cab → tuple[]
getVortexDragonX 0xd1f73638 → uint256
getVortexTitanX 0x1ce392b1 → uint256
name 0x06fdde03 → string
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256

Write Contract 26 functions

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

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
approveBurnMints 0x300284f2
address spender
uint256 amount
returns: bool
batchMintLadder 0x8e449fdc
uint256 mintPower
uint256 minDay
uint256 maxDay
uint256 dayInterval
uint256 countPerInterval
burnCAHydra 0x5c5ef4b6
address contractAddress
burnMint 0x7b763a2c
address user
uint256 id
burnTokens 0x9ed99220
address user
uint256 amount
uint256 userRebatePercentage
uint256 rewardPaybackPercentage
burnTokensToPayAddress 0x33f3fd78
address user
uint256 amount
uint256 userRebatePercentage
uint256 rewardPaybackPercentage
address rewardPaybackAddress
claimMint 0xbaf20eef
uint256 id
distributeFees 0xbb57ad20
No parameters
earlyEndMint 0xfe73e379
uint256 id
fundVortexDragonX 0x707f6dda
uint256 amount
fundVortexTitanX 0x7c872f87
uint256 amount
manualDailyUpdate 0x13aad510
No parameters
mintLPTokens 0x3765d723
uint256 amount
renounceOwnership 0x715018a6
No parameters
setBuyAndBurnContractAddress 0x7d6b3253
address contractAddress
setNewGenesisAddress 0x3a9693e1
address newAddress
setTitanXIncentiveFeeDividend 0x4d235aa2
uint256 amount
startMint 0x635d70f4
uint256 mintPower
uint256 numOfDays
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
transferOwnership 0xf2fde38b
address newOwner
triggerVortex 0x3cef7cc3
No parameters
unRegisterBuyAndBurnContractAddress 0x1c911f2b
address contractAddress
userBurnMint 0x37c4f8c4
uint256 id
userBurnTokens 0x3c34267f
uint256 amount

Recent Transactions

No transactions found for this address