Address Contract Verified
Address
0xe71B0Bde957C46bC4c0f610147b05735C6BF3580
Balance
0 ETH
Nonce
1
Code Size
17975 bytes
Creator
0x7F69b4aD...aB78 at tx 0xb1c1bda3...e4bab0
Indexed Transactions
0
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