Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xe71B0Bde957C46bC4c0f610147b05735C6BF3580
Balance 0 ETH
Nonce 1
Code Size 17975 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

17975 bytes
0x6080604052600436106101f25760003560e01c8063715018a61161010d5780639d217534116100a0578063b95cc9f61161006f578063b95cc9f61461070b578063d820901514610736578063f14210a614610774578063f2fde38b1461079d578063f7c618c1146107c6576101f9565b80639d21753414610665578063a8fe76ae1461068e578063a971deb6146106b9578063ad5240f4146106e2576101f9565b80638da5cb5b116100dc5780638da5cb5b14610595578063937ed288146105c0578063956d1175146105eb578063978aa66614610628576101f9565b8063715018a6146104ec5780637755cc55146105035780638620e207146105415780638aee81271461056c576101f9565b80635187c09111610185578063660e1f8c11610154578063660e1f8c14610408578063661cdbfa1461044657806366e305fd1461048457806368c43a44146104c1576101f9565b80635187c09114610360578063573761981461038b5780635fa1d1f7146103b457806361ee798b146103dd576101f9565b80631135e2fe116101c15780631135e2fe146102b05780631fc94a9a146102e15780633bd694d81461030a5780634aa4a4fc14610335576101f9565b806303249bea146101fe5780630411f4b714610229578063050aac8b146102545780630faac4f31461027f576101f9565b366101f957005b600080fd5b34801561020a57600080fd5b506102136107f1565b604051610220919061318b565b60405180910390f35b34801561023557600080fd5b5061023e6107f7565b60405161024b9190613225565b60405180910390f35b34801561026057600080fd5b5061026961081b565b604051610276919061318b565b60405180910390f35b610299600480360381019061029491906132ef565b610821565b6040516102a7929190613356565b60405180910390f35b6102ca60048036038101906102c591906133e4565b610b44565b6040516102d8929190613356565b60405180910390f35b3480156102ed57600080fd5b506103086004803603810190610303919061346c565b610ecf565b005b34801561031657600080fd5b5061031f610f1b565b60405161032c91906134ba565b60405180910390f35b34801561034157600080fd5b5061034a610f41565b60405161035791906134e4565b60405180910390f35b34801561036c57600080fd5b50610375610f65565b6040516103829190613520565b60405180910390f35b34801561039757600080fd5b506103b260048036038101906103ad919061353b565b610f89565b005b3480156103c057600080fd5b506103db60048036038101906103d6919061357b565b610fc0565b005b3480156103e957600080fd5b506103f2610fd2565b6040516103ff919061318b565b60405180910390f35b34801561041457600080fd5b5061042f600480360381019061042a91906135a8565b611029565b60405161043d929190613356565b60405180910390f35b34801561045257600080fd5b5061046d600480360381019061046891906135a8565b611497565b60405161047b929190613356565b60405180910390f35b34801561049057600080fd5b506104ab60048036038101906104a6919061346c565b611828565b6040516104b8919061365d565b60405180910390f35b3480156104cd57600080fd5b506104d661185c565b6040516104e3919061318b565b60405180910390f35b3480156104f857600080fd5b50610501611862565b005b34801561050f57600080fd5b5061052a60048036038101906105259190613678565b611876565b604051610538929190613356565b60405180910390f35b34801561054d57600080fd5b50610556611c54565b604051610563919061318b565b60405180910390f35b34801561057857600080fd5b50610593600480360381019061058e919061346c565b611c5a565b005b3480156105a157600080fd5b506105aa611ca6565b6040516105b791906134e4565b60405180910390f35b3480156105cc57600080fd5b506105d5611ccf565b6040516105e2919061318b565b60405180910390f35b3480156105f757600080fd5b50610612600480360381019061060d919061346c565b611cd5565b60405161061f919061318b565b60405180910390f35b34801561063457600080fd5b5061064f600480360381019061064a919061346c565b611dcf565b60405161065c919061318b565b60405180910390f35b34801561067157600080fd5b5061068c6004803603810190610687919061357b565b611edb565b005b34801561069a57600080fd5b506106a3611f38565b6040516106b0919061318b565b60405180910390f35b3480156106c557600080fd5b506106e060048036038101906106db919061357b565b611f3e565b005b3480156106ee57600080fd5b506107096004803603810190610704919061357b565b611f50565b005b34801561071757600080fd5b50610720611fad565b60405161072d919061318b565b60405180910390f35b34801561074257600080fd5b5061075d6004803603810190610758919061346c565b612027565b60405161076b929190613783565b60405180910390f35b34801561078057600080fd5b5061079b6004803603810190610796919061357b565b6124e5565b005b3480156107a957600080fd5b506107c460048036038101906107bf919061346c565b612537565b005b3480156107d257600080fd5b506107db6125ba565b6040516107e891906137d4565b60405180910390f35b60075481565b7f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4581565b60065481565b60008061082c6125e0565b60003490507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561089957600080fd5b505af11580156108ad573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45836040518363ffffffff1660e01b815260040161092d9291906137ef565b6020604051808303816000875af115801561094c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109709190613844565b5060006040518061010001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1681526020018973ffffffffffffffffffffffffffffffffffffffff1681526020018862ffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff168152602001428152602001838152602001878152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090507f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4573ffffffffffffffffffffffffffffffffffffffff1663414bf389826040518263ffffffff1660e01b8152600401610a86919061394f565b6020604051808303816000875af1158015610aa5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac99190613980565b9350610ad43361262f565b92503373ffffffffffffffffffffffffffffffffffffffff167f26dbf6cb0da2f78338b5a9b7505033653a45310876abe921e7aa75a81d7da521838a89888a896003604051610b2997969594939291906139f5565b60405180910390a25050610b3b612aaa565b94509492505050565b600080610b4f6125e0565b60028787905010158015610bd657507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1687876000818110610ba957610ba8613a64565b5b9050602002016020810190610bbe919061346c565b73ffffffffffffffffffffffffffffffffffffffff16145b610c15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c0c90613adf565b60405180910390fd5b60003490506000888860018b8b9050610c2e9190613b2e565b818110610c3e57610c3d613a64565b5b9050602002016020810190610c53919061346c565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b8152600401610c8b91906134e4565b602060405180830381865afa158015610ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccc9190613980565b90507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d73ffffffffffffffffffffffffffffffffffffffff1663b6f9de9583898c8c8b8b6040518763ffffffff1660e01b8152600401610d30959493929190613c16565b6000604051808303818588803b158015610d4957600080fd5b505af1158015610d5d573d6000803e3d6000fd5b505050505080898960018c8c9050610d759190613b2e565b818110610d8557610d84613a64565b5b9050602002016020810190610d9a919061346c565b73ffffffffffffffffffffffffffffffffffffffff166370a08231886040518263ffffffff1660e01b8152600401610dd291906134e4565b602060405180830381865afa158015610def573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e139190613980565b610e1d9190613b2e565b9350610e283361262f565b92503373ffffffffffffffffffffffffffffffffffffffff167f26dbf6cb0da2f78338b5a9b7505033653a45310876abe921e7aa75a81d7da521838b8b60018e8e9050610e759190613b2e565b818110610e8557610e84613a64565b5b9050602002016020810190610e9a919061346c565b8a888b896002604051610eb39796959493929190613c9f565b60405180910390a25050610ec5612aaa565b9550959350505050565b610ed7612ab3565b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b7f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81565b610f91612ab3565b610fbc33828473ffffffffffffffffffffffffffffffffffffffff16612b319092919063ffffffff16565b5050565b610fc8612ab3565b8060048190555050565b6000806201518042610fe49190613d3d565b9050600a60009054906101000a900467ffffffffffffffff1667ffffffffffffffff168167ffffffffffffffff161461101f57600554611023565b6006545b91505090565b6000806110346125e0565b600286869050101561107b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161107290613dba565b60405180910390fd5b6110d033308a8989600081811061109557611094613a64565b5b90506020020160208101906110aa919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612bb7909392919063ffffffff16565b6111447f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d60008888600081811061110a57611109613a64565b5b905060200201602081019061111f919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b6111b77f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d898888600081811061117d5761117c613a64565b5b9050602002016020810190611192919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b600086866001898990506111cb9190613b2e565b8181106111db576111da613a64565b5b90506020020160208101906111f0919061346c565b73ffffffffffffffffffffffffffffffffffffffff166370a08231866040518263ffffffff1660e01b815260040161122891906134e4565b602060405180830381865afa158015611245573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112699190613980565b90507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d73ffffffffffffffffffffffffffffffffffffffff16635c11d7958a8a8a8a8a8a6040518763ffffffff1660e01b81526004016112ce96959493929190613dda565b600060405180830381600087803b1580156112e857600080fd5b505af11580156112fc573d6000803e3d6000fd5b5050505080878760018a8a90506113139190613b2e565b81811061132357611322613a64565b5b9050602002016020810190611338919061346c565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b815260040161137091906134e4565b602060405180830381865afa15801561138d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b19190613980565b6113bb9190613b2e565b92506113c63361262f565b91503373ffffffffffffffffffffffffffffffffffffffff167fe96a563086e8fe2becee85466a9a524b949d1b2f3ff57c86d0d13f0e65ab4f3c8888600081811061141457611413613a64565b5b9050602002016020810190611429919061346c565b8b8a8a60018d8d905061143c9190613b2e565b81811061144c5761144b613a64565b5b9050602002016020810190611461919061346c565b8c888b89600260405161147b989796959493929190613e36565b60405180910390a25061148c612aaa565b965096945050505050565b6000806114a26125e0565b6002868690501015801561153757507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1686866001898990506114fa9190613b2e565b81811061150a57611509613a64565b5b905060200201602081019061151f919061346c565b73ffffffffffffffffffffffffffffffffffffffff16145b611576576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161156d90613f00565b60405180910390fd5b6115cb33308a898960008181106115905761158f613a64565b5b90506020020160208101906115a5919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612bb7909392919063ffffffff16565b61163f7f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d60008888600081811061160557611604613a64565b5b905060200201602081019061161a919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b6116b27f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d898888600081811061167857611677613a64565b5b905060200201602081019061168d919061346c565b73ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b60008473ffffffffffffffffffffffffffffffffffffffff163190507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d73ffffffffffffffffffffffffffffffffffffffff1663791ac9478a8a8a8a8a8a6040518763ffffffff1660e01b815260040161173196959493929190613dda565b600060405180830381600087803b15801561174b57600080fd5b505af115801561175f573d6000803e3d6000fd5b50505050808573ffffffffffffffffffffffffffffffffffffffff16316117869190613b2e565b92506117913361262f565b91503373ffffffffffffffffffffffffffffffffffffffff167fb967243bb67bd2f754914eddd2e1bdfccc89f9169e2664527a96c406f08c802b888860008181106117df576117de613a64565b5b90506020020160208101906117f4919061346c565b8b868987600260405161180c96959493929190613f20565b60405180910390a25061181d612aaa565b965096945050505050565b60008061183483611dcf565b9050600081141580156118545750600454816118509190613f81565b4210155b915050919050565b60055481565b61186a612ab3565b6118746000612d8f565b565b6000806118816125e0565b6118ae3330878a73ffffffffffffffffffffffffffffffffffffffff16612bb7909392919063ffffffff16565b6118fa7f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4560008973ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b6119457f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45868973ffffffffffffffffffffffffffffffffffffffff16612c409092919063ffffffff16565b60006040518061010001604052808973ffffffffffffffffffffffffffffffffffffffff1681526020017f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1681526020018862ffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff168152602001428152602001878152602001868152602001600073ffffffffffffffffffffffffffffffffffffffff16815250905060007f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc4573ffffffffffffffffffffffffffffffffffffffff1663414bf389836040518263ffffffff1660e01b8152600401611a5c919061394f565b6020604051808303816000875af1158015611a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9f9190613980565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b8152600401611afa919061318b565b600060405180830381600087803b158015611b1457600080fd5b505af1158015611b28573d6000803e3d6000fd5b5050505060008573ffffffffffffffffffffffffffffffffffffffff1682604051611b5290613fe6565b60006040518083038185875af1925050503d8060008114611b8f576040519150601f19603f3d011682016040523d82523d6000602084013e611b94565b606091505b5050905080611bd8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bcf90614047565b60405180910390fd5b819450611be43361262f565b93503373ffffffffffffffffffffffffffffffffffffffff167fb967243bb67bd2f754914eddd2e1bdfccc89f9169e2664527a96c406f08c802b8b8a858a896003604051611c3796959493929190614067565b60405180910390a2505050611c4a612aaa565b9550959350505050565b60095481565b611c62612ab3565b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60045481565b6000806201518042611ce79190613d3d565b90506000600b60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1667ffffffffffffffff168267ffffffffffffffff1614611d60576000611da1565b600c60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020545b90508060095411611db757600092505050611dca565b80600954611dc59190613b2e565b925050505b919050565b60008073ffffffffffffffffffffffffffffffffffffffff16600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603611e2f5760009050611ed6565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637b6014e3836040518263ffffffff1660e01b8152600401611e8a91906134e4565b602060405180830381865afa925050508015611ec457506040513d601f19601f82011682018060405250810190611ec19190613980565b60015b611ed15760009050611ed6565b809150505b919050565b611ee3612ab3565b60008111611f26576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f1d90614114565b60405180910390fd5b80600781905550611f35612e53565b50565b60085481565b611f46612ab3565b8060098190555050565b611f58612ab3565b60008111611f9b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f9290614180565b60405180910390fd5b80600581905550611faa612e53565b50565b6000806201518042611fbf9190613d3d565b90506000600a60009054906101000a900467ffffffffffffffff1667ffffffffffffffff168267ffffffffffffffff1614611ffb576000611fff565b6008545b90508060075411612011576000612020565b8060075461201f9190613b2e565b5b9250505090565b6000606061203483611828565b6120795760006040518060400160405280601e81526020017f6e6f7420656c696769626c6520286475726174696f6e2f616d6f756e74290000815250915091506124e0565b6000620151804261208a9190613d3d565b90506000600a60009054906101000a900467ffffffffffffffff1667ffffffffffffffff168267ffffffffffffffff16146120c7576005546120cb565b6006545b9050600081036121185760006040518060400160405280601681526020017f6461696c792062756467657420657868617573746564000000000000000000008152509350935050506124e0565b6000600a60009054906101000a900467ffffffffffffffff1667ffffffffffffffff168367ffffffffffffffff1614612152576000612156565b6008545b905060075481106121a55760006040518060400160405280601081526020017f7377617020636170207265616368656400000000000000000000000000000000815250945094505050506124e0565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161220291906134e4565b602060405180830381865afa15801561221f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122439190613980565b9050600081036122925760006040518060400160405280601681526020017f7772617070657220686173206e6f20726577617264730000000000000000000081525095509550505050506124e0565b60006007546005546122a49190613d3d565b9050600081036122f45760006040518060400160405280601781526020017f7065722d7377617020616d6f756e74206973207a65726f0000000000000000008152509650965050505050506124e0565b6000600b60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1667ffffffffffffffff168667ffffffffffffffff161461236b5760006123ac565b600c60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020545b9050600081600954116123c05760006123cf565b816009546123ce9190613b2e565b5b9050600081036124215760006040518060400160405280601881526020017f77616c6c6574206461696c79206361702072656163686564000000000000000081525098509850505050505050506124e0565b600083905081811115612432578190505b8681111561243e578690505b8481111561244a578490505b6000810361249b5760006040518060400160405280601281526020017f6e6f207061796f757420706f737369626c6500000000000000000000000000008152509950995050505050505050506124e0565b60016040518060400160405280600381526020017f79657300000000000000000000000000000000000000000000000000000000008152509950995050505050505050505b915091565b6124ed612ab3565b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015612533573d6000803e3d6000fd5b5050565b61253f612ab3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036125ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125a590614212565b60405180910390fd5b6125b781612d8f565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260015403612625576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161261c9061427e565b60405180910390fd5b6002600181905550565b60008073ffffffffffffffffffffffffffffffffffffffff16600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361268f5760009050612aa5565b612697612e53565b600754600854106126ab5760009050612aa5565b6126b482611828565b6126c15760009050612aa5565b6000600654036126d45760009050612aa5565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161273191906134e4565b602060405180830381865afa15801561274e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127729190613980565b905060008103612786576000915050612aa5565b600062015180426127979190613d3d565b90508067ffffffffffffffff16600b60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1667ffffffffffffffff16146128b35780600b60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000600c60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b6000600c60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460095411612904576000612952565b600c60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546009546129519190613b2e565b5b905060006007540361296a5760009350505050612aa5565b600060075460055461297c9190613d3d565b905060008103612993576000945050505050612aa5565b809450818511156129a2578194505b6006548511156129b25760065494505b838511156129be578394505b600085036129d3576000945050505050612aa5565b84600660008282546129e59190613b2e565b9250508190555084600c60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612a3b9190613f81565b925050819055506001600860008282540192505081905550612aa08686600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16612b319092919063ffffffff16565b505050505b919050565b60018081905550565b612abb612ed5565b73ffffffffffffffffffffffffffffffffffffffff16612ad9611ca6565b73ffffffffffffffffffffffffffffffffffffffff1614612b2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b26906142ea565b60405180910390fd5b565b612bb28363a9059cbb60e01b8484604051602401612b509291906137ef565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612edd565b505050565b612c3a846323b872dd60e01b858585604051602401612bd89392919061430a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612edd565b50505050565b6000811480612cca575060008373ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30856040518363ffffffff1660e01b8152600401612c87929190614341565b602060405180830381865afa158015612ca4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cc89190613980565b145b612d09576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d00906143dc565b60405180910390fd5b612d8a8363095ea7b360e01b8484604051602401612d289291906137ef565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612edd565b505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60006201518042612e649190613d3d565b90508067ffffffffffffffff16600a60009054906101000a900467ffffffffffffffff1667ffffffffffffffff1614612ed25780600a60006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060055460068190555060006008819055505b50565b600033905090565b6000612f3f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612fa59092919063ffffffff16565b9050600081511480612f61575080806020019051810190612f609190613844565b5b612fa0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612f979061446e565b60405180910390fd5b505050565b6060612fb48484600085612fbd565b90509392505050565b606082471015613002576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ff990614500565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161302b919061455c565b60006040518083038185875af1925050503d8060008114613068576040519150601f19603f3d011682016040523d82523d6000602084013e61306d565b606091505b509150915061307e8783838761308a565b92505050949350505050565b606083156130ec5760008351036130e4576130a4856130ff565b6130e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130da906145bf565b60405180910390fd5b5b8290506130f7565b6130f68383613122565b5b949350505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000825111156131355781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161316991906145df565b60405180910390fd5b6000819050919050565b61318581613172565b82525050565b60006020820190506131a0600083018461317c565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006131eb6131e66131e1846131a6565b6131c6565b6131a6565b9050919050565b60006131fd826131d0565b9050919050565b600061320f826131f2565b9050919050565b61321f81613204565b82525050565b600060208201905061323a6000830184613216565b92915050565b600080fd5b600080fd5b6000613255826131a6565b9050919050565b6132658161324a565b811461327057600080fd5b50565b6000813590506132828161325c565b92915050565b600062ffffff82169050919050565b6132a081613288565b81146132ab57600080fd5b50565b6000813590506132bd81613297565b92915050565b6132cc81613172565b81146132d757600080fd5b50565b6000813590506132e9816132c3565b92915050565b6000806000806080858703121561330957613308613240565b5b600061331787828801613273565b9450506020613328878288016132ae565b9350506040613339878288016132da565b925050606061334a87828801613273565b91505092959194509250565b600060408201905061336b600083018561317c565b613378602083018461317c565b9392505050565b600080fd5b600080fd5b600080fd5b60008083601f8401126133a4576133a361337f565b5b8235905067ffffffffffffffff8111156133c1576133c0613384565b5b6020830191508360208202830111156133dd576133dc613389565b5b9250929050565b600080600080600060808688031215613400576133ff613240565b5b600086013567ffffffffffffffff81111561341e5761341d613245565b5b61342a8882890161338e565b9550955050602061343d888289016132da565b935050604061344e88828901613273565b925050606061345f888289016132da565b9150509295509295909350565b60006020828403121561348257613481613240565b5b600061349084828501613273565b91505092915050565b60006134a4826131f2565b9050919050565b6134b481613499565b82525050565b60006020820190506134cf60008301846134ab565b92915050565b6134de8161324a565b82525050565b60006020820190506134f960008301846134d5565b92915050565b600061350a826131f2565b9050919050565b61351a816134ff565b82525050565b60006020820190506135356000830184613511565b92915050565b6000806040838503121561355257613551613240565b5b600061356085828601613273565b9250506020613571858286016132da565b9150509250929050565b60006020828403121561359157613590613240565b5b600061359f848285016132da565b91505092915050565b60008060008060008060a087890312156135c5576135c4613240565b5b60006135d389828a016132da565b96505060206135e489828a016132da565b955050604087013567ffffffffffffffff81111561360557613604613245565b5b61361189828a0161338e565b9450945050606061362489828a01613273565b925050608061363589828a016132da565b9150509295509295509295565b60008115159050919050565b61365781613642565b82525050565b6000602082019050613672600083018461364e565b92915050565b600080600080600060a0868803121561369457613693613240565b5b60006136a288828901613273565b95505060206136b3888289016132ae565b94505060406136c4888289016132da565b93505060606136d5888289016132da565b92505060806136e688828901613273565b9150509295509295909350565b600081519050919050565b600082825260208201905092915050565b60005b8381101561372d578082015181840152602081019050613712565b60008484015250505050565b6000601f19601f8301169050919050565b6000613755826136f3565b61375f81856136fe565b935061376f81856020860161370f565b61377881613739565b840191505092915050565b6000604082019050613798600083018561364e565b81810360208301526137aa818461374a565b90509392505050565b60006137be826131f2565b9050919050565b6137ce816137b3565b82525050565b60006020820190506137e960008301846137c5565b92915050565b600060408201905061380460008301856134d5565b613811602083018461317c565b9392505050565b61382181613642565b811461382c57600080fd5b50565b60008151905061383e81613818565b92915050565b60006020828403121561385a57613859613240565b5b60006138688482850161382f565b91505092915050565b61387a8161324a565b82525050565b61388981613288565b82525050565b61389881613172565b82525050565b6138a7816131a6565b82525050565b610100820160008201516138c46000850182613871565b5060208201516138d76020850182613871565b5060408201516138ea6040850182613880565b5060608201516138fd6060850182613871565b506080820151613910608085018261388f565b5060a082015161392360a085018261388f565b5060c082015161393660c085018261388f565b5060e082015161394960e085018261389e565b50505050565b60006101008201905061396560008301846138ad565b92915050565b60008151905061397a816132c3565b92915050565b60006020828403121561399657613995613240565b5b60006139a48482850161396b565b91505092915050565b6000819050919050565b600060ff82169050919050565b60006139df6139da6139d5846139ad565b6131c6565b6139b7565b9050919050565b6139ef816139c4565b82525050565b600060e082019050613a0a600083018a61317c565b613a1760208301896134d5565b613a24604083018861317c565b613a31606083018761317c565b613a3e60808301866134d5565b613a4b60a083018561317c565b613a5860c08301846139e6565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f56323a20706174685b305d206d75737420626520574554480000000000000000600082015250565b6000613ac96018836136fe565b9150613ad482613a93565b602082019050919050565b60006020820190508181036000830152613af881613abc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613b3982613172565b9150613b4483613172565b9250828203905081811115613b5c57613b5b613aff565b5b92915050565b600082825260208201905092915050565b6000819050919050565b6000613b898383613871565b60208301905092915050565b6000613ba46020840184613273565b905092915050565b6000602082019050919050565b6000613bc58385613b62565b9350613bd082613b73565b8060005b85811015613c0957613be68284613b95565b613bf08882613b7d565b9750613bfb83613bac565b925050600181019050613bd4565b5085925050509392505050565b6000608082019050613c2b600083018861317c565b8181036020830152613c3e818688613bb9565b9050613c4d60408301856134d5565b613c5a606083018461317c565b9695505050505050565b6000819050919050565b6000613c89613c84613c7f84613c64565b6131c6565b6139b7565b9050919050565b613c9981613c6e565b82525050565b600060e082019050613cb4600083018a61317c565b613cc160208301896134d5565b613cce604083018861317c565b613cdb606083018761317c565b613ce860808301866134d5565b613cf560a083018561317c565b613d0260c0830184613c90565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000613d4882613172565b9150613d5383613172565b925082613d6357613d62613d0e565b5b828204905092915050565b7f56323a2062616420706174680000000000000000000000000000000000000000600082015250565b6000613da4600c836136fe565b9150613daf82613d6e565b602082019050919050565b60006020820190508181036000830152613dd381613d97565b9050919050565b600060a082019050613def600083018961317c565b613dfc602083018861317c565b8181036040830152613e0f818688613bb9565b9050613e1e60608301856134d5565b613e2b608083018461317c565b979650505050505050565b600061010082019050613e4c600083018b6134d5565b613e59602083018a61317c565b613e6660408301896134d5565b613e73606083018861317c565b613e80608083018761317c565b613e8d60a08301866134d5565b613e9a60c083018561317c565b613ea760e0830184613c90565b9998505050505050505050565b7f56323a206c6173742070617468206d7573742062652057455448000000000000600082015250565b6000613eea601a836136fe565b9150613ef582613eb4565b602082019050919050565b60006020820190508181036000830152613f1981613edd565b9050919050565b600060c082019050613f3560008301896134d5565b613f42602083018861317c565b613f4f604083018761317c565b613f5c60608301866134d5565b613f69608083018561317c565b613f7660a0830184613c90565b979650505050505050565b6000613f8c82613172565b9150613f9783613172565b9250828201905080821115613faf57613fae613aff565b5b92915050565b600081905092915050565b50565b6000613fd0600083613fb5565b9150613fdb82613fc0565b600082019050919050565b6000613ff182613fc3565b9150819050919050565b7f4554482073656e64206661696c65640000000000000000000000000000000000600082015250565b6000614031600f836136fe565b915061403c82613ffb565b602082019050919050565b6000602082019050818103600083015261406081614024565b9050919050565b600060c08201905061407c60008301896134d5565b614089602083018861317c565b614096604083018761317c565b6140a360608301866134d5565b6140b0608083018561317c565b6140bd60a08301846139e6565b979650505050505050565b7f6d6178207377617073203d203000000000000000000000000000000000000000600082015250565b60006140fe600d836136fe565b9150614109826140c8565b602082019050919050565b6000602082019050818103600083015261412d816140f1565b9050919050565b7f6461696c7920627564676574203d203000000000000000000000000000000000600082015250565b600061416a6010836136fe565b915061417582614134565b602082019050919050565b600060208201905081810360008301526141998161415d565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006141fc6026836136fe565b9150614207826141a0565b604082019050919050565b6000602082019050818103600083015261422b816141ef565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000614268601f836136fe565b915061427382614232565b602082019050919050565b600060208201905081810360008301526142978161425b565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006142d46020836136fe565b91506142df8261429e565b602082019050919050565b60006020820190508181036000830152614303816142c7565b9050919050565b600060608201905061431f60008301866134d5565b61432c60208301856134d5565b614339604083018461317c565b949350505050565b600060408201905061435660008301856134d5565b61436360208301846134d5565b9392505050565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60008201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000602082015250565b60006143c66036836136fe565b91506143d18261436a565b604082019050919050565b600060208201905081810360008301526143f5816143b9565b9050919050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b6000614458602a836136fe565b9150614463826143fc565b604082019050919050565b600060208201905081810360008301526144878161444b565b9050919050565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b60006144ea6026836136fe565b91506144f58261448e565b604082019050919050565b60006020820190508181036000830152614519816144dd565b9050919050565b600081519050919050565b600061453682614520565b6145408185613fb5565b935061455081856020860161370f565b80840191505092915050565b6000614568828461452b565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006145a9601d836136fe565b91506145b482614573565b602082019050919050565b600060208201905081810360008301526145d88161459c565b9050919050565b600060208201905081810360008301526145f9818461374a565b90509291505056fea26469706673582212204a46e87dbe5cf0eb63eb404e43cedcc4b473069bfb82b89907d04390dec41f9d64736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: No
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @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 virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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;
    }
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
IUniswapV2Router01.sol 95 lines
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
IUniswapV2Router02.sol 44 lines
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}
IUniswapV3SwapCallback.sol 21 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}
ISwapRouter.sol 67 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
HolySwap.sol 400 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

interface IWETH9 {
    function deposit() external payable;
    function withdraw(uint256) external;
    function approve(address, uint256) external returns (bool);
    function balanceOf(address) external view returns (uint256);
}

interface IEligibilityToken is IERC20 {
    function enoughSupplyForRewardSince(address account) external view returns (uint256);
}

contract HolySwap is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    IUniswapV2Router02 public immutable uniswapV2;
    ISwapRouter public immutable uniswapV3;
    IERC20 public rewardToken;
    address public immutable WETH9;

    // Eligibility (token encodes min-balance; wrapper enforces min duration)
    IEligibilityToken public eligibilityToken;
    uint256 public minHoldDuration = 7 days;

    // Global daily budget (token units)
    uint256 public dailyBudgetAmount = 250_000e18;
    uint256 public dailyBudgetRemaining = dailyBudgetAmount;

    // First-N rewarded swaps (daily)
    uint256 public maxRewardedSwaps = 1000;
    uint256 public rewardedSwaps;

    // Per-wallet daily cap (token units)
    uint256 public perWalletDailyCapAmount = 1000 * 1e18;

    // Day index used to reset daily budget and swap counter
    uint64 private _lastDay;

    // Per-wallet tracking
    mapping(address => uint64) private _lastRewardDay;
    mapping(address => uint256) private _paidToday;

    /* =========================
                EVENTS
       ========================= */

    event SwapETHForTokens(
        address indexed user,
        uint256 ethIn,
        address tokenOut,
        uint256 amountOutMin,
        uint256 amountOut,
        address to,
        uint256 rewardAmount,
        uint8 protocolVersion
    );

    event SwapTokensForETH(
        address indexed user,
        address tokenIn,
        uint256 amountIn,
        uint256 ethOut,
        address to,
        uint256 rewardAmount,
        uint8 protocolVersion
    );

    event SwapTokensForTokens(
        address indexed user,
        address tokenIn,
        uint256 amountIn,
        address tokenOut,
        uint256 amountOutMin,
        uint256 amountOut,
        address to,
        uint256 rewardAmount,
        uint8 protocolVersion
    );

    constructor(address _v2Router, address _v3Router) Ownable() {
        uniswapV2 = IUniswapV2Router02(_v2Router);
        uniswapV3 = ISwapRouter(_v3Router);
        WETH9 = IUniswapV2Router02(_v2Router).WETH();
        _lastDay = 0; // force refresh on first use
    }

    /* =========================
         REWARD SYSTEM (VIEWS)
       ========================= */

    function eligibleSince(address user) public view returns (uint256) {
        if (address(eligibilityToken) == address(0)) return 0;
        try eligibilityToken.enoughSupplyForRewardSince(user) returns (uint256 ts) {
            return ts;
        } catch {
            return 0;
        }
    }

    function isEligible(address user) public view returns (bool) {
        uint256 ts = eligibleSince(user);
        return ts != 0 && block.timestamp >= ts + minHoldDuration;
    }

    function remainingDailyAllowance(address user) external view returns (uint256) {
        uint64 day = uint64(block.timestamp / 1 days);
        uint256 spent = (day == _lastRewardDay[user]) ? _paidToday[user] : 0;
        if (perWalletDailyCapAmount <= spent) return 0;
        return perWalletDailyCapAmount - spent;
    }

    function remainingGlobalSwapsToday() external view returns (uint256) {
        uint64 today = uint64(block.timestamp / 1 days);
        uint256 used = (today == _lastDay) ? rewardedSwaps : 0;
        return (maxRewardedSwaps > used) ? (maxRewardedSwaps - used) : 0;
    }

    function remainingDailyBudget() external view returns (uint256) {
        uint64 today = uint64(block.timestamp / 1 days);
        return (today == _lastDay) ? dailyBudgetRemaining : dailyBudgetAmount;
    }

    // Preview whether the next tx from `user` would pay rewards, and why/why not.
    // If it would pay: (true, "yes")
    // If not: (false, "<reason>")
    function previewNextReward(address user) external view returns (bool ok, string memory reason) {
        if (!isEligible(user)) return (false, "not eligible (duration/amount)");

        uint64 today = uint64(block.timestamp / 1 days);

        uint256 budgetRemaining = (today == _lastDay) ? dailyBudgetRemaining : dailyBudgetAmount;
        if (budgetRemaining == 0) return (false, "daily budget exhausted");

        uint256 usedSwaps = (today == _lastDay) ? rewardedSwaps : 0;
        if (usedSwaps >= maxRewardedSwaps) return (false, "swap cap reached");

        uint256 bal = rewardToken.balanceOf(address(this));
        if (bal == 0) return (false, "wrapper has no rewards");

        uint256 perSwap = dailyBudgetAmount / maxRewardedSwaps;
        if (perSwap == 0) return (false, "per-swap amount is zero");

        uint256 spentToday = (today == _lastRewardDay[user]) ? _paidToday[user] : 0;
        uint256 remainingForWallet = perWalletDailyCapAmount > spentToday ? perWalletDailyCapAmount - spentToday : 0;
        if (remainingForWallet == 0) return (false, "wallet daily cap reached");

        uint256 possible = perSwap;
        if (possible > remainingForWallet) possible = remainingForWallet;
        if (possible > budgetRemaining) possible = budgetRemaining;
        if (possible > bal) possible = bal;

        if (possible == 0) return (false, "no payout possible");
        return (true, "yes");
    }

    /* =========================
        REWARD SYSTEM (INTERNAL)
       ========================= */

    function _refreshDailyGlobals() internal {
        uint64 today = uint64(block.timestamp / 1 days);
        if (_lastDay != today) {
            _lastDay = today;
            dailyBudgetRemaining = dailyBudgetAmount;
            rewardedSwaps = 0;
        }
    }

    function _maybeRewardTx(address user) internal returns (uint256 paid) {
        if (address(rewardToken) == address(0)) return 0;

        _refreshDailyGlobals();

        if (rewardedSwaps >= maxRewardedSwaps) return 0;
        if (!isEligible(user)) return 0;
        if (dailyBudgetRemaining == 0) return 0;

        uint256 bal = rewardToken.balanceOf(address(this));
        if (bal == 0) return 0;

        uint64 today = uint64(block.timestamp / 1 days);
        if (_lastRewardDay[user] != today) {
            _lastRewardDay[user] = today;
            _paidToday[user] = 0;
        }

        uint256 remainingForWallet = perWalletDailyCapAmount > _paidToday[user]
            ? perWalletDailyCapAmount - _paidToday[user]
            : 0;

        if (maxRewardedSwaps == 0) return 0;
        uint256 perSwap = dailyBudgetAmount / maxRewardedSwaps;
        if (perSwap == 0) return 0;

        paid = perSwap;
        if (paid > remainingForWallet)   paid = remainingForWallet;
        if (paid > dailyBudgetRemaining) paid = dailyBudgetRemaining;
        if (paid > bal)                  paid = bal;
        if (paid == 0) return 0;

        dailyBudgetRemaining -= paid;
        _paidToday[user] += paid;
        unchecked { rewardedSwaps += 1; }
        rewardToken.safeTransfer(user, paid);
        return paid;
    }

    /* =========================
            UNISWAP V2
       ========================= */

    function swapExactETHForTokensV2(
        address[] calldata path,
        uint256 amountOutMin,
        address to,
        uint256 deadline
    ) external payable nonReentrant returns (uint256 amountOut, uint256 rewardAmount) {
        require(path.length >= 2 && path[0] == WETH9, "V2: path[0] must be WETH");

        uint256 ethIn = msg.value;

        uint256 before = IERC20(path[path.length - 1]).balanceOf(to);

        uniswapV2.swapExactETHForTokensSupportingFeeOnTransferTokens{ value: ethIn }(
            amountOutMin, path, to, deadline
        );

        amountOut = IERC20(path[path.length - 1]).balanceOf(to) - before;
        rewardAmount = _maybeRewardTx(msg.sender);
        emit SwapETHForTokens(msg.sender, ethIn, path[path.length - 1], amountOutMin, amountOut, to, rewardAmount, 2);
    }


    function swapExactTokensForETHV2(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external nonReentrant returns (uint256 ethOut, uint256 rewardAmount) {
        require(path.length >= 2 && path[path.length - 1] == WETH9, "V2: last path must be WETH");

        IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn);
        IERC20(path[0]).safeApprove(address(uniswapV2), 0);
        IERC20(path[0]).safeApprove(address(uniswapV2), amountIn);

        uint256 before = to.balance;

        uniswapV2.swapExactTokensForETHSupportingFeeOnTransferTokens(
            amountIn, amountOutMin, path, to, deadline
        );

        ethOut = to.balance - before;
        rewardAmount = _maybeRewardTx(msg.sender);

        emit SwapTokensForETH(msg.sender, path[0], amountIn, ethOut, to, rewardAmount, 2);
    }


    function swapExactTokensForTokensV2(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external nonReentrant returns (uint256 amountOut, uint256 rewardAmount) {
        require(path.length >= 2, "V2: bad path");

        IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn);
        IERC20(path[0]).safeApprove(address(uniswapV2), 0);
        IERC20(path[0]).safeApprove(address(uniswapV2), amountIn);

        uint256 before = IERC20(path[path.length - 1]).balanceOf(to);

        uniswapV2.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            amountIn, amountOutMin, path, to, deadline
        );

        amountOut = IERC20(path[path.length - 1]).balanceOf(to) - before;
        rewardAmount = _maybeRewardTx(msg.sender);
        
        emit SwapTokensForTokens(msg.sender, path[0], amountIn, path[path.length - 1], amountOutMin, amountOut, to, rewardAmount, 2);
    }


    /* =========================
            UNISWAP V3
       ========================= */

    function swapExactETHForTokensV3(
        address tokenOut,
        uint24 fee,
        uint256 amountOutMin,
        address to
    ) external payable nonReentrant returns (uint256 amountOut, uint256 rewardAmount) {
        uint256 ethIn = msg.value;

        IWETH9(WETH9).deposit{value: ethIn}();
        IWETH9(WETH9).approve(address(uniswapV3), ethIn);

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: WETH9,
            tokenOut: tokenOut,
            fee: fee,
            recipient: to,
            deadline: block.timestamp,
            amountIn: ethIn,
            amountOutMinimum: amountOutMin,
            sqrtPriceLimitX96: 0
        });

        amountOut = uniswapV3.exactInputSingle(params);
        rewardAmount = _maybeRewardTx(msg.sender);
        emit SwapETHForTokens(msg.sender, ethIn, tokenOut, amountOutMin, amountOut, to, rewardAmount, 3);
    }

    function swapExactTokensForETHV3(
        address tokenIn,
        uint24 fee,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external nonReentrant returns (uint256 ethOut, uint256 rewardAmount) {
        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).safeApprove(address(uniswapV3), 0);
        IERC20(tokenIn).safeApprove(address(uniswapV3), amountIn);

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: tokenIn,
            tokenOut: WETH9,
            fee: fee,
            recipient: address(this),
            deadline: block.timestamp,
            amountIn: amountIn,
            amountOutMinimum: amountOutMin,
            sqrtPriceLimitX96: 0
        });

        uint256 wethOut = uniswapV3.exactInputSingle(params);

        IWETH9(WETH9).withdraw(wethOut);
        (bool ok, ) = to.call{value: wethOut}("");
        require(ok, "ETH send failed");

        ethOut = wethOut;
        rewardAmount = _maybeRewardTx(msg.sender);
        emit SwapTokensForETH(msg.sender, tokenIn, amountIn, wethOut, to, rewardAmount, 3);
    }

    /* =========================
            ADMIN / HELPERS
       ========================= */

    function setRewardToken(address _token) external onlyOwner {
        rewardToken = IERC20(_token);
    }

    function setEligibilityToken(address _token) external onlyOwner {
        eligibilityToken = IEligibilityToken(_token);
    }

    function setMinHoldDuration(uint256 duration) external onlyOwner {
        minHoldDuration = duration;
    }

    function setDailyBudgetAmount(uint256 amountPerDay) external onlyOwner {
        require(amountPerDay > 0, "daily budget = 0");
        dailyBudgetAmount = amountPerDay;
        _refreshDailyGlobals();
    }

    function setMaxRewardedSwaps(uint256 maxSwaps) external onlyOwner {
        require(maxSwaps > 0, "max swaps = 0");
        maxRewardedSwaps = maxSwaps;
        _refreshDailyGlobals();
    }

    function setPerWalletDailyCapAmount(uint256 amount) external onlyOwner {
        perWalletDailyCapAmount = amount;
    }

    receive() external payable {}

    function rescueTokens(address token, uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(msg.sender, amount);
    }

    function withdrawETH(uint256 amount) external onlyOwner {
        payable(msg.sender).transfer(amount);
    }
}

Read Contract

WETH9 0x4aa4a4fc → address
dailyBudgetAmount 0x68c43a44 → uint256
dailyBudgetRemaining 0x050aac8b → uint256
eligibilityToken 0x3bd694d8 → address
eligibleSince 0x978aa666 → uint256
isEligible 0x66e305fd → bool
maxRewardedSwaps 0x03249bea → uint256
minHoldDuration 0x937ed288 → uint256
owner 0x8da5cb5b → address
perWalletDailyCapAmount 0x8620e207 → uint256
previewNextReward 0xd8209015 → bool, string
remainingDailyAllowance 0x956d1175 → uint256
remainingDailyBudget 0x61ee798b → uint256
remainingGlobalSwapsToday 0xb95cc9f6 → uint256
rewardToken 0xf7c618c1 → address
rewardedSwaps 0xa8fe76ae → uint256
uniswapV2 0x5187c091 → address
uniswapV3 0x0411f4b7 → address

Write Contract 15 functions

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

renounceOwnership 0x715018a6
No parameters
rescueTokens 0x57376198
address token
uint256 amount
setDailyBudgetAmount 0xad5240f4
uint256 amountPerDay
setEligibilityToken 0x1fc94a9a
address _token
setMaxRewardedSwaps 0x9d217534
uint256 maxSwaps
setMinHoldDuration 0x5fa1d1f7
uint256 duration
setPerWalletDailyCapAmount 0xa971deb6
uint256 amount
setRewardToken 0x8aee8127
address _token
swapExactETHForTokensV2 0x1135e2fe
address[] path
uint256 amountOutMin
address to
uint256 deadline
returns: uint256, uint256
swapExactETHForTokensV3 0x0faac4f3
address tokenOut
uint24 fee
uint256 amountOutMin
address to
returns: uint256, uint256
swapExactTokensForETHV2 0x661cdbfa
uint256 amountIn
uint256 amountOutMin
address[] path
address to
uint256 deadline
returns: uint256, uint256
swapExactTokensForETHV3 0x7755cc55
address tokenIn
uint24 fee
uint256 amountIn
uint256 amountOutMin
address to
returns: uint256, uint256
swapExactTokensForTokensV2 0x660e1f8c
uint256 amountIn
uint256 amountOutMin
address[] path
address to
uint256 deadline
returns: uint256, uint256
transferOwnership 0xf2fde38b
address newOwner
withdrawETH 0xf14210a6
uint256 amount

Recent Transactions

No transactions found for this address