Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x7078c4537C04c2b2E52ddBa06074dBdACF23cA15
Balance 30.0000 ETH
Nonce 1
Code Size 12203 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

12203 bytes
0x60806040526004361061012d5760003560e01c806382cb6b72116100a55780639bc94d0111610074578063edee623911610059578063edee6239146103c4578063f1d42b47146103d7578063fc3e3eba146103ec57600080fd5b80639bc94d0114610384578063e551cdaa146103a457600080fd5b806382cb6b72146102b75780638da5cb5b146102e457806390547c14146103365780639456fbcc1461036457600080fd5b80634782f779116100fc57806367148cd2116100e157806367148cd214610256578063690d8320146102765780636e2d44ae1461029657600080fd5b80634782f779146102065780634d7fba6e1461022657600080fd5b80630d8f46971461016e57806313af4035146101a45780634025feb2146101c657806344004cc1146101e657600080fd5b366101695760405134815233907f4103257eaac983ca79a70d28f90dfc4fa16b619bb0c17ee7cab0d4034c2796249060200160405180910390a2005b600080fd5b34801561017a57600080fd5b5061018e610189366004611f8b565b61041c565b60405161019b9190612037565b60405180910390f35b3480156101b057600080fd5b506101c46101bf366004612067565b61044c565b005b3480156101d257600080fd5b506101c46101e136600461208b565b610542565b3480156101f257600080fd5b506101c461020136600461208b565b6106d4565b34801561021257600080fd5b506101c46102213660046120cc565b610865565b34801561023257600080fd5b506102466102413660046121bb565b6109b5565b60405161019b9493929190612286565b34801561026257600080fd5b506101c4610271366004611f8b565b610be7565b34801561028257600080fd5b506101c4610291366004612067565b610e0a565b6102a96102a43660046123dc565b610e98565b60405161019b929190612435565b3480156102c357600080fd5b506102d76102d2366004612450565b610f92565b60405161019b9190612469565b3480156102f057600080fd5b506000546103119073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161019b565b34801561034257600080fd5b50610356610351366004611f8b565b61103e565b60405190815260200161019b565b34801561037057600080fd5b506101c461037f36600461247c565b61106d565b34801561039057600080fd5b506101c461039f3660046124b5565b611188565b3480156103b057600080fd5b506101c46103bf366004612510565b6116c3565b6102a96103d2366004612575565b611b85565b3480156103e357600080fd5b50600254610356565b3480156103f857600080fd5b5061040c610407366004611f8b565b611c7b565b604051901515815260200161019b565b6000600183836040516104309291906125c5565b9081526040519081900360200190205460ff1690505b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60005473ffffffffffffffffffffffffffffffffffffffff1633146105c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018390528416906323b872dd90606401600060405180830381600087803b15801561063957600080fd5b505af115801561064d573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f30b478a5e196e55886228aa87ba74a7dfeba655e0a4d7ba275eabfc22aabb7a8846040516106c791815260200190565b60405180910390a4505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610755576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063a9059cbb906044016020604051808303816000875af11580156107ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ee91906125e3565b508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f6b00f1c7883f053ba83e907fd1965b22fffe3c4111383e725f04638a566cdbfa846040516106c791815260200190565b60005473ffffffffffffffffffffffffffffffffffffffff1633146108e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114610940576040519150601f19603f3d011682016040523d82523d6000602084013e610945565b606091505b505090508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f1f12aa8b6d492dd9b98e2b00b0b20830c2a7ded65afac13b60d169a034ae90bc846040516109a891815260200190565b60405180910390a3505050565b805160208183018101805160018083529383019483019490942093905282546040805160a081018252938501805460ff90811615158652600287015494860194909452600386015473ffffffffffffffffffffffffffffffffffffffff16918501919091526004850180549390921694939290916060840191610a3790612600565b80601f0160208091040260200160405190810160405280929190818152602001828054610a6390612600565b8015610ab05780601f10610a8557610100808354040283529160200191610ab0565b820191906000526020600020905b815481529060010190602001808311610a9357829003601f168201915b5050505050815260200160048201805480602002602001604051908101604052809291908181526020016000905b82821015610bcf576000848152602090819020604080516060810190915260038502909101805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610b3490612600565b80601f0160208091040260200160405190810160405280929190818152602001828054610b6090612600565b8015610bad5780601f10610b8257610100808354040283529160200191610bad565b820191906000526020600020905b815481529060010190602001808311610b9057829003601f168201915b5050505050815260200160028201548152505081526020019060010190610ade565b50505091525050600682015460079092015490919084565b600060018383604051610bfb9291906125c5565b90815260200160405180910390209050610c158383611c7b565b50426006820155600781018054906000610c2e83612682565b9091555050600581015460005b81811015610dae576000836001016004018281548110610c5d57610c5d6126ba565b6000918252602082206003909102018054600282015460405192945073ffffffffffffffffffffffffffffffffffffffff90911691610ca09060018601906126e9565b60006040518083038185875af1925050503d8060008114610cdd576040519150601f19603f3d011682016040523d82523d6000602084013e610ce2565b606091505b5050905080610d99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f447269707069653a20647269702077617320756e7375636365737366756c2c2060448201527f706c6561736520636865636b20796f757220636f6e66696775726174696f6e2060648201527f666f72206d697374616b65730000000000000000000000000000000000000000608482015260a4016104c9565b50508080610da690612682565b915050610c3b565b508383604051610dbf9291906125c5565b60405180910390207fea21435419aad9c54a9d90e2522b6f60bd566401f36fcef661f5f5a28cc0d2c685853342604051610dfc94939291906127c6565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e8b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b610e958147610865565b50565b6000805460609073ffffffffffffffffffffffffffffffffffffffff163314610f1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b8473ffffffffffffffffffffffffffffffffffffffff168385604051610f439190612803565b60006040518083038185875af1925050503d8060008114610f80576040519150601f19603f3d011682016040523d82523d6000602084013e610f85565b606091505b5090969095509350505050565b60028181548110610fa257600080fd5b906000526020600020016000915090508054610fbd90612600565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe990612600565b80156110365780601f1061100b57610100808354040283529160200191611036565b820191906000526020600020905b81548152906001019060200180831161101957829003601f168201915b505050505081565b6000600183836040516110529291906125c5565b90815260405190819003602001902060020154905092915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146110ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152611184908390839073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015611160573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610201919061281f565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611209576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b600081600381111561121d5761121d611fcd565b036112d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f447269707069653a2064726970207374617475732063616e206e65766572206260448201527f6520736574206261636b20746f204e4f4e45206166746572206372656174696f60648201527f6e00000000000000000000000000000000000000000000000000000000000000608482015260a4016104c9565b6000600184846040516112e49291906125c5565b9081526040519081900360200190205460ff169050600081600381111561130d5761130d611fcd565b036113c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f447269707069653a206472697020776974682074686174206e616d6520646f6560448201527f73206e6f7420657869737420616e642063616e6e6f742062652075706461746560648201527f6400000000000000000000000000000000000000000000000000000000000000608482015260a4016104c9565b60038160038111156113d4576113d4611fcd565b03611488576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526044602482018190527f447269707069653a206472697020776974682074686174206e616d6520686173908201527f206265656e20617263686976656420616e642063616e6e6f742062652075706460648201527f6174656400000000000000000000000000000000000000000000000000000000608482015260a4016104c9565b81600381111561149a5761149a611fcd565b8160038111156114ac576114ac611fcd565b0361155f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f447269707069653a2063616e6e6f74207365742064726970207374617475732060448201527f746f207468652073616d6520737461747573206173206974732063757272656e60648201527f7420737461747573000000000000000000000000000000000000000000000000608482015260a4016104c9565b600382600381111561157357611573611fcd565b0361161957600181600381111561158c5761158c611fcd565b14611619576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f447269707069653a2064726970206d757374206669727374206265207061757360448201527f6564206265666f7265206265696e67206172636869766564000000000000000060648201526084016104c9565b816001858560405161162c9291906125c5565b90815260405190819003602001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600183600381111561167357611673611fcd565b021790555083836040516116889291906125c5565b60405180910390207f407cb3ad05e60ec498fb39417c7a4f6b82d5ba80f82fe512a37b02c93181a2a1858585604051610dfc93929190612838565b60005473ffffffffffffffffffffffffffffffffffffffff163314611744576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b6000600184846040516117589291906125c5565b9081526040519081900360200190205460ff16600381111561177c5761177c611fcd565b14611809576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f447269707069653a206472697020776974682074686174206e616d6520616c7260448201527f656164792065786973747300000000000000000000000000000000000000000060648201526084016104c9565b611816602082018261285b565b156118b2576020810135156118ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f447269707069653a20696620616c6c6f77696e67207265656e7472616e74206460448201527f7269702c206d7573742073657420696e74657276616c20746f207a65726f000060648201526084016104c9565b61196d565b600081602001351161196d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526044602482018190527f447269707069653a20696e74657276616c206d75737420626520677265617465908201527f72207468616e207a65726f2069662064726970206973206e6f74207265656e7460648201527f72616e7400000000000000000000000000000000000000000000000000000000608482015260a4016104c9565b6000600184846040516119819291906125c5565b9081526040516020918190038201902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117815591506119c89083018361285b565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905560208201356002820155611a136060830160408401612067565b6003820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055611a676060830183612878565b6004830191611a7791908361292c565b5060005b611a886080840184612a47565b9050811015611afb5760058201611aa26080850185612a47565b83818110611ab257611ab26126ba565b9050602002810190611ac49190612aaf565b815460018101835560009283526020909220909160030201611ae68282612ae3565b50508080611af390612682565b915050611a7b565b50600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01611b3984868361292c565b508383604051611b4a9291906125c5565b60405180910390207fe38d8d98e6cc66f6f520d483c6c5a89289681f897799c4c29d767cf57e76d9a6858585604051610dfc93929190612dd6565b6000805460609073ffffffffffffffffffffffffffffffffffffffff163314611c0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016104c9565b8373ffffffffffffffffffffffffffffffffffffffff1683604051611c2f9190612803565b600060405180830381855af49150503d8060008114611c6a576040519150601f19603f3d011682016040523d82523d6000602084013e611c6f565b606091505b50909590945092505050565b60008060018484604051611c909291906125c5565b90815260405190819003602001902090506002815460ff166003811115611cb957611cb9611fcd565b14611d4857604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f447269707069653a2073656c6563746564206472697020646f6573206e6f742060448201527f6578697374206f72206973206e6f742063757272656e746c792061637469766560648201526084016104c9565b600281015460068201544291611d5d91612edd565b1115611deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f447269707069653a206472697020696e74657276616c20686173206e6f74206560448201527f6c61707365642073696e6365206c61737420647269700000000000000000000060648201526084016104c9565b60038101546040517fc64b3bb500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c64b3bb590611e459060048086019101612ef5565b602060405180830381865afa158015611e62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8691906125e3565b611f38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f447269707069653a2064726970636865636b206661696c656420736f2064726960448201527f70206973206e6f742079657420726561647920746f206265207472696767657260648201527f6564000000000000000000000000000000000000000000000000000000000000608482015260a4016104c9565b5060019392505050565b60008083601f840112611f5457600080fd5b50813567ffffffffffffffff811115611f6c57600080fd5b602083019150836020828501011115611f8457600080fd5b9250929050565b60008060208385031215611f9e57600080fd5b823567ffffffffffffffff811115611fb557600080fd5b611fc185828601611f42565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110612033577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b602081016104468284611ffc565b73ffffffffffffffffffffffffffffffffffffffff81168114610e9557600080fd5b60006020828403121561207957600080fd5b813561208481612045565b9392505050565b6000806000606084860312156120a057600080fd5b83356120ab81612045565b925060208401356120bb81612045565b929592945050506040919091013590565b600080604083850312156120df57600080fd5b82356120ea81612045565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff80841115612142576121426120f8565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715612188576121886120f8565b816040528093508581528686860111156121a157600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156121cd57600080fd5b813567ffffffffffffffff8111156121e457600080fd5b8201601f810184136121f557600080fd5b61220484823560208401612127565b949350505050565b60005b8381101561222757818101518382015260200161220f565b83811115612236576000848401525b50505050565b6000815180845261225481602086016020860161220c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6122908186611ffc565b600060206080818401528551151560808401528086015160a084015260408087015173ffffffffffffffffffffffffffffffffffffffff80821660c0870152606091508189015160a060e08801526122ec61012088018261223c565b60808b01518882037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80016101008a0152805180835291925086019086830190600581901b8401880160005b828110156123a0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086830301845284518781511683528a810151898c8501526123838a85018261223c565b918b0151938b0193909352948a0194938a01939150600101612337565b50968a019b909b52505050509093019390935250949350505050565b600082601f8301126123cd57600080fd5b61208483833560208501612127565b6000806000606084860312156123f157600080fd5b83356123fc81612045565b9250602084013567ffffffffffffffff81111561241857600080fd5b612424868287016123bc565b925050604084013590509250925092565b8215158152604060208201526000612204604083018461223c565b60006020828403121561246257600080fd5b5035919050565b602081526000612084602083018461223c565b6000806040838503121561248f57600080fd5b823561249a81612045565b915060208301356124aa81612045565b809150509250929050565b6000806000604084860312156124ca57600080fd5b833567ffffffffffffffff8111156124e157600080fd5b6124ed86828701611f42565b90945092505060208401356004811061250557600080fd5b809150509250925092565b60008060006040848603121561252557600080fd5b833567ffffffffffffffff8082111561253d57600080fd5b61254987838801611f42565b9095509350602086013591508082111561256257600080fd5b50840160a0818703121561250557600080fd5b6000806040838503121561258857600080fd5b823561259381612045565b9150602083013567ffffffffffffffff8111156125af57600080fd5b6125bb858286016123bc565b9150509250929050565b8183823760009101908152919050565b8015158114610e9557600080fd5b6000602082840312156125f557600080fd5b8151612084816125d5565b600181811c9082168061261457607f821691505b60208210810361264d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126b3576126b3612653565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083546126f781612600565b6001828116801561270f576001811461274257612771565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0084168752821515830287019450612771565b8760005260208060002060005b858110156127685781548a82015290840190820161274f565b50505082870194505b50929695505050505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6060815260006127da60608301868861277d565b73ffffffffffffffffffffffffffffffffffffffff949094166020830152506040015292915050565b6000825161281581846020870161220c565b9190910192915050565b60006020828403121561283157600080fd5b5051919050565b60408152600061284c60408301858761277d565b90506122046020830184611ffc565b60006020828403121561286d57600080fd5b8135612084816125d5565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126128ad57600080fd5b83018035915067ffffffffffffffff8211156128c857600080fd5b602001915036819003821315611f8457600080fd5b601f82111561292757600081815260208120601f850160051c810160208610156129045750805b601f850160051c820191505b8181101561292357828155600101612910565b5050505b505050565b67ffffffffffffffff831115612944576129446120f8565b612958836129528354612600565b836128dd565b6000601f8411600181146129aa57600085156129745750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355612a40565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156129f957868501358255602094850194600190920191016129d9565b5086821015612a34577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612a7c57600080fd5b83018035915067ffffffffffffffff821115612a9757600080fd5b6020019150600581901b3603821315611f8457600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261281557600080fd5b8135612aee81612045565b73ffffffffffffffffffffffffffffffffffffffff81167fffffffffffffffffffffffff00000000000000000000000000000000000000008354161782555060018082016020808501357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1863603018112612b6857600080fd5b8501803567ffffffffffffffff811115612b8157600080fd5b8036038383011315612b9257600080fd5b612ba681612ba08654612600565b866128dd565b6000601f821160018114612bfa5760008315612bc457508382018501355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178655612c8f565b6000868152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0841690835b82811015612c4857868501880135825593870193908901908701612c29565b5084821015612c85577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c198785880101351681555b50508683881b0186555b50505050505050604082013560028201555050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612cd957600080fd5b830160208101925035905067ffffffffffffffff811115612cf957600080fd5b803603821315611f8457600080fd5b81835260006020808501808196508560051b81019150846000805b88811015612dc8578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1893603018112612d61578283fd5b880160608135612d7081612045565b73ffffffffffffffffffffffffffffffffffffffff168752612d9482890183612ca4565b828a8a0152612da6838a01828461277d565b6040948501359990940198909852505099860199945091850191600101612d23565b509298975050505050505050565b604081526000612dea60408301858761277d565b82810360208401528335612dfd816125d5565b15158152602084810135908201526040840135612e1981612045565b73ffffffffffffffffffffffffffffffffffffffff166040820152612e416060850185612ca4565b60a06060840152612e5660a08401828461277d565b91505060808501357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1863603018112612e8e57600080fd5b850160208101903567ffffffffffffffff811115612eab57600080fd5b8060051b3603821315612ebd57600080fd5b8383036080850152612ed0838284612d08565b9998505050505050505050565b60008219821115612ef057612ef0612653565b500190565b6000602080835260008454612f0981612600565b80848701526040600180841660008114612f2a5760018114612f6257612f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f90565b896000528660002060005b85811015612f885781548b8201860152908301908801612f6d565b8a0184019650505b50939897505050505050505056fea164736f6c634300080f000a

Verified Source Code Partial Match

Compiler: v0.8.15+commit.e14f2714 EVM: london Optimization: Yes (999999 runs)
Transactor.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol";

/// @title Transactor
/// @notice Transactor is a minimal contract that can send transactions.
contract Transactor is Owned {
    /// @param _owner Initial contract owner.
    constructor(address _owner) Owned(_owner) { }

    /// @notice Sends a CALL to a target address.
    /// @param _target Address to call.
    /// @param _data   Data to send with the call.
    /// @param _value  ETH value to send with the call.
    /// @return success_ Boolean success value.
    /// @return data_ Bytes data returned by the call.
    function CALL(
        address _target,
        bytes memory _data,
        uint256 _value
    )
        external
        payable
        onlyOwner
        returns (bool success_, bytes memory data_)
    {
        (success_, data_) = _target.call{ value: _value }(_data);
    }

    /// @notice Sends a DELEGATECALL to a target address.
    /// @param _target Address to call.
    /// @param _data   Data to send with the call.
    /// @return success_ Boolean success value.
    /// @return data_ Bytes data returned by the call.
    function DELEGATECALL(
        address _target,
        bytes memory _data
    )
        external
        payable
        onlyOwner
        returns (bool success_, bytes memory data_)
    {
        // slither-disable-next-line controlled-delegatecall
        (success_, data_) = _target.delegatecall(_data);
    }
}
Owned.sol 44 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnerUpdated(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnerUpdated(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function setOwner(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}
AssetReceiver.sol 88 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
import { Transactor } from "./Transactor.sol";

/// @title AssetReceiver
/// @notice AssetReceiver is a minimal contract for receiving funds assets in the form of either
///         ETH, ERC20 tokens, or ERC721 tokens. Only the contract owner may withdraw the assets.
contract AssetReceiver is Transactor {
    /// @notice Emitted when ETH is received by this address.
    /// @param from   Address that sent ETH to this contract.
    /// @param amount Amount of ETH received.
    event ReceivedETH(address indexed from, uint256 amount);

    /// @notice Emitted when ETH is withdrawn from this address.
    /// @param withdrawer Address that triggered the withdrawal.
    /// @param recipient  Address that received the withdrawal.
    /// @param amount     ETH amount withdrawn.
    event WithdrewETH(address indexed withdrawer, address indexed recipient, uint256 amount);

    /// @notice Emitted when ERC20 tokens are withdrawn from this address.
    /// @param withdrawer Address that triggered the withdrawal.
    /// @param recipient  Address that received the withdrawal.
    /// @param asset      Address of the token being withdrawn.
    /// @param amount     ERC20 amount withdrawn.
    event WithdrewERC20(address indexed withdrawer, address indexed recipient, address indexed asset, uint256 amount);

    /// @notice Emitted when ERC20 tokens are withdrawn from this address.
    /// @param withdrawer Address that triggered the withdrawal.
    /// @param recipient  Address that received the withdrawal.
    /// @param asset      Address of the token being withdrawn.
    /// @param id         Token ID being withdrawn.
    event WithdrewERC721(address indexed withdrawer, address indexed recipient, address indexed asset, uint256 id);

    /// @param _owner Initial contract owner.
    constructor(address _owner) Transactor(_owner) { }

    /// @notice Make sure we can receive ETH.
    receive() external payable {
        emit ReceivedETH(msg.sender, msg.value);
    }

    /// @notice Withdraws full ETH balance to the recipient.
    /// @param _to Address to receive the ETH balance.
    function withdrawETH(address payable _to) external onlyOwner {
        withdrawETH(_to, address(this).balance);
    }

    /// @notice Withdraws partial ETH balance to the recipient.
    /// @param _to     Address to receive the ETH balance.
    /// @param _amount Amount of ETH to withdraw.
    function withdrawETH(address payable _to, uint256 _amount) public onlyOwner {
        // slither-disable-next-line reentrancy-unlimited-gas
        (bool success,) = _to.call{ value: _amount }("");
        success; // Suppress warning; We ignore the low-level call result.
        emit WithdrewETH(msg.sender, _to, _amount);
    }

    /// @notice Withdraws full ERC20 balance to the recipient.
    /// @param _asset ERC20 token to withdraw.
    /// @param _to    Address to receive the ERC20 balance.
    function withdrawERC20(ERC20 _asset, address _to) external onlyOwner {
        withdrawERC20(_asset, _to, _asset.balanceOf(address(this)));
    }

    /// @notice Withdraws partial ERC20 balance to the recipient.
    /// @param _asset  ERC20 token to withdraw.
    /// @param _to     Address to receive the ERC20 balance.
    /// @param _amount Amount of ERC20 to withdraw.
    function withdrawERC20(ERC20 _asset, address _to, uint256 _amount) public onlyOwner {
        // slither-disable-next-line unchecked-transfer
        _asset.transfer(_to, _amount);
        // slither-disable-next-line reentrancy-events
        emit WithdrewERC20(msg.sender, _to, address(_asset), _amount);
    }

    /// @notice Withdraws ERC721 token to the recipient.
    /// @param _asset ERC721 token to withdraw.
    /// @param _to    Address to receive the ERC721 token.
    /// @param _id    Token ID of the ERC721 token to withdraw.
    function withdrawERC721(ERC721 _asset, address _to, uint256 _id) external onlyOwner {
        _asset.transferFrom(address(this), _to, _id);
        // slither-disable-next-line reentrancy-events
        emit WithdrewERC721(msg.sender, _to, address(_asset), _id);
    }
}
ERC20.sol 206 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}
ERC721.sol 231 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        if (to.code.length != 0)
            require(
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        if (to.code.length != 0)
            require(
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        if (to.code.length != 0)
            require(
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        if (to.code.length != 0)
            require(
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}
Drippie.sol 275 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { AssetReceiver } from "../AssetReceiver.sol";
import { IDripCheck } from "./IDripCheck.sol";

/// @title Drippie
/// @notice Drippie is a system for managing automated contract interactions. A specific interaction
///         is called a "drip" and can be executed according to some condition (called a dripcheck)
///         and an execution interval. Drips cannot be executed faster than the execution interval.
///         Drips can trigger arbitrary contract calls where the calling contract is this contract
///         address. Drips can also send ETH value, which makes them ideal for keeping addresses
///         sufficiently funded with ETH. Drippie is designed to be connected with smart contract
///         automation services so that drips can be executed automatically. However, Drippie is
///         specifically designed to be separated from these services so that trust assumptions are
///         better compartmentalized.
contract Drippie is AssetReceiver {
    /// @notice Enum representing different status options for a given drip.
    /// @custom:value NONE     Drip does not exist.
    /// @custom:value PAUSED   Drip is paused and cannot be executed until reactivated.
    /// @custom:value ACTIVE   Drip is active and can be executed.
    /// @custom:value ARCHIVED Drip is archived and can no longer be executed or reactivated.
    enum DripStatus {
        NONE,
        PAUSED,
        ACTIVE,
        ARCHIVED
    }

    /// @notice Represents a drip action.
    struct DripAction {
        address payable target;
        bytes data;
        uint256 value;
    }

    /// @notice Represents the configuration for a given drip.
    struct DripConfig {
        bool reentrant;
        uint256 interval;
        IDripCheck dripcheck;
        bytes checkparams;
        DripAction[] actions;
    }

    /// @notice Represents the state of an active drip.
    struct DripState {
        DripStatus status;
        DripConfig config;
        uint256 last;
        uint256 count;
    }

    /// @notice Emitted when a new drip is created.
    /// @param nameref Indexed name parameter (hashed).
    /// @param name    Unindexed name parameter (unhashed).
    /// @param config  Config for the created drip.
    // Emit name twice because indexed version is hashed.
    event DripCreated(string indexed nameref, string name, DripConfig config);

    /// @notice Emitted when a drip status is updated.
    /// @param nameref Indexed name parameter (hashed).
    /// @param name    Unindexed name parameter (unhashed).
    /// @param status  New drip status.
    // Emit name twice because indexed version is hashed.
    event DripStatusUpdated(string indexed nameref, string name, DripStatus status);

    /// @notice Emitted when a drip is executed.
    /// @param nameref   Indexed name parameter (hashed).
    /// @param name      Unindexed name parameter (unhashed).
    /// @param executor  Address that executed the drip.
    /// @param timestamp Time when the drip was executed.
    // Emit name twice because indexed version is hashed.
    event DripExecuted(string indexed nameref, string name, address executor, uint256 timestamp);

    /// @notice Maps from drip names to drip states.
    mapping(string => DripState) public drips;

    /// @notice Array of created drips. Used so offchain services can easily iterate over all drips
    ///         without resorting to event queries. Convenience feature and shouldn't really be
    ///         used onchain because the array will grow indefinitely.
    string[] public created;

    //// @param _owner Initial contract owner.
    constructor(address _owner) AssetReceiver(_owner) { }

    /// @notice Creates a new drip with the given name and configuration. Once created, drips cannot
    ///         be modified in any way (this is a security measure). If you want to update a drip,
    ///         simply pause (and potentially archive) the existing drip and create a new one.
    /// @param _name   Name of the drip.
    /// @param _config Configuration for the drip.
    function create(string calldata _name, DripConfig calldata _config) external onlyOwner {
        // Make sure this drip doesn't already exist. We *must* guarantee that no other function
        // will ever set the status of a drip back to NONE after it's been created. This is why
        // archival is a separate status.
        require(drips[_name].status == DripStatus.NONE, "Drippie: drip with that name already exists");

        // Validate the drip interval, only allowing an interval of zero if the drip has explicitly
        // been marked as reentrant. Prevents client-side bugs making a drip infinitely executable
        // within the same block (of course, restricted by gas limits).
        if (_config.reentrant) {
            require(_config.interval == 0, "Drippie: if allowing reentrant drip, must set interval to zero");
        } else {
            require(_config.interval > 0, "Drippie: interval must be greater than zero if drip is not reentrant");
        }

        // We initialize this way because Solidity won't let us copy arrays into storage yet.
        DripState storage state = drips[_name];
        state.status = DripStatus.PAUSED;
        state.config.reentrant = _config.reentrant;
        state.config.interval = _config.interval;
        state.config.dripcheck = _config.dripcheck;
        state.config.checkparams = _config.checkparams;

        // Solidity doesn't let us copy arrays into storage, so we push each array one by one.
        for (uint256 i = 0; i < _config.actions.length; i++) {
            state.config.actions.push(_config.actions[i]);
        }

        // Add the name of the drip to the array of created drips.
        created.push(_name);

        // Tell the world!
        emit DripCreated(_name, _name, _config);
    }

    /// @notice Sets the status for a given drip. The behavior of this function depends on the
    ///         status that the user is trying to set. A drip can always move between ACTIVE and
    ///         PAUSED, but it can never move back to NONE and once ARCHIVED, it can never move back
    ///         to ACTIVE or PAUSED.
    /// @param _name   Name of the drip to update.
    /// @param _status New drip status.
    function status(string calldata _name, DripStatus _status) external onlyOwner {
        // Make sure we can never set drip status back to NONE. A simple security measure to
        // prevent accidental overwrites if this code is ever updated down the line.
        require(_status != DripStatus.NONE, "Drippie: drip status can never be set back to NONE after creation");

        // Load the drip status once to avoid unnecessary SLOADs.
        DripStatus curr = drips[_name].status;

        // Make sure the drip in question actually exists. Not strictly necessary but there doesn't
        // seem to be any clear reason why you would want to do this, and it may save some gas in
        // the case of a front-end bug.
        require(curr != DripStatus.NONE, "Drippie: drip with that name does not exist and cannot be updated");

        // Once a drip has been archived, it cannot be un-archived. This is, after all, the entire
        // point of archiving a drip.
        require(curr != DripStatus.ARCHIVED, "Drippie: drip with that name has been archived and cannot be updated");

        // Although not strictly necessary, we make sure that the status here is actually changing.
        // This may save the client some gas if there's a front-end bug and the user accidentally
        // tries to "change" the status to the same value as before.
        require(curr != _status, "Drippie: cannot set drip status to the same status as its current status");

        // If the user is trying to archive this drip, make sure the drip has been paused. We do
        // not allow users to archive active drips so that the effects of this action are more
        // abundantly clear.
        if (_status == DripStatus.ARCHIVED) {
            require(curr == DripStatus.PAUSED, "Drippie: drip must first be paused before being archived");
        }

        // If we made it here then we can safely update the status.
        drips[_name].status = _status;
        emit DripStatusUpdated(_name, _name, _status);
    }

    /// @notice Checks if a given drip is executable.
    /// @param _name Drip to check.
    /// @return True if the drip is executable, reverts otherwise.
    function executable(string calldata _name) public view returns (bool) {
        DripState storage state = drips[_name];

        // Only allow active drips to be executed, an obvious security measure.
        require(state.status == DripStatus.ACTIVE, "Drippie: selected drip does not exist or is not currently active");

        // Don't drip if the drip interval has not yet elapsed since the last time we dripped. This
        // is a safety measure that prevents a malicious recipient from, e.g., spending all of
        // their funds and repeatedly requesting new drips. Limits the potential impact of a
        // compromised recipient to just a single drip interval, after which the drip can be paused
        // by the owner address.
        require(
            state.last + state.config.interval <= block.timestamp,
            "Drippie: drip interval has not elapsed since last drip"
        );

        // Make sure we're allowed to execute this drip.
        require(
            state.config.dripcheck.check(state.config.checkparams),
            "Drippie: dripcheck failed so drip is not yet ready to be triggered"
        );

        // Alright, we're good to execute.
        return true;
    }

    /// @notice Triggers a drip. This function is deliberately left as a public function because the
    ///         assumption being made here is that setting the drip to ACTIVE is an affirmative
    ///         signal that the drip should be executable according to the drip parameters, drip
    ///         check, and drip interval. Note that drip parameters are read entirely from the state
    ///         and are not supplied as user input, so there should not be any way for a
    ///         non-authorized user to influence the behavior of the drip. Note that the drip check
    ///         is executed only **once** at the beginning of the call to the drip function and will
    ///         not be executed again between the drip actions within this call.
    /// @param _name Name of the drip to trigger.
    function drip(string calldata _name) external {
        DripState storage state = drips[_name];

        // Make sure the drip can be executed. Since executable reverts if the drip is not ready to
        // be executed, we don't need to do an assertion that the returned value is true.
        executable(_name);

        // Update the last execution time for this drip before the call. Note that it's entirely
        // possible for a drip to be executed multiple times per block or even multiple times
        // within the same transaction (via re-entrancy) if the drip interval is set to zero. Users
        // should set a drip interval of 1 if they'd like the drip to be executed only once per
        // block (since this will then prevent re-entrancy).
        state.last = block.timestamp;

        // Update the number of times this drip has been executed. Although this increases the cost
        // of using Drippie, it slightly simplifies the client-side by not having to worry about
        // counting drips via events. Useful for monitoring the rate of drip execution.
        state.count++;

        // Execute each action in the drip. We allow drips to have multiple actions because there
        // are scenarios in which a contract must do multiple things atomically. For example, the
        // contract may need to withdraw ETH from one account and then deposit that ETH into
        // another account within the same transaction.
        uint256 len = state.config.actions.length;
        for (uint256 i = 0; i < len; i++) {
            // Must be marked as "storage" because copying structs into memory is not yet supported
            // by Solidity. Won't significantly reduce gas costs but at least makes it easier to
            // read what the rest of this section is doing.
            DripAction storage action = state.config.actions[i];

            // Actually execute the action. We could use ExcessivelySafeCall here but not strictly
            // necessary (worst case, a drip gets bricked IFF the target is malicious, doubt this
            // will ever happen in practice). Could save a marginal amount of gas to ignore the
            // returndata.
            // slither-disable-next-line calls-loop
            (bool success,) = action.target.call{ value: action.value }(action.data);

            // Generally should not happen, but could if there's a misconfiguration (e.g., passing
            // the wrong data to the target contract), the recipient is not payable, or
            // insufficient gas was supplied to this transaction. We revert so the drip can be
            // fixed and triggered again later. Means we cannot emit an event to alert of the
            // failure, but can reasonably be detected by off-chain services even without an event.
            // Note that this forces the drip executor to supply sufficient gas to the call
            // (assuming there is some sufficient gas limit that exists, otherwise the drip will
            // not execute).
            require(success, "Drippie: drip was unsuccessful, please check your configuration for mistakes");
        }

        emit DripExecuted(_name, _name, msg.sender, block.timestamp);
    }

    /// @notice Returns the status of a given drip.
    /// @param _name Drip to check.
    /// @return DripStatus of the given drip.
    function getDripStatus(string calldata _name) public view returns (DripStatus) {
        return drips[_name].status;
    }

    /// @notice Returns the interval of a given drip.
    /// @param _name Drip to check.
    /// @return Interval of the given drip.
    function getDripInterval(string calldata _name) public view returns (uint256) {
        return drips[_name].config.interval;
    }

    /// @notice Returns the number of created drips.
    /// @return Number of created drips.
    function getDripCount() public view returns (uint256) {
        return created.length;
    }
}
IDripCheck.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IDripCheck {
    // DripCheck contracts that want to take parameters as inputs MUST expose a struct called
    // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it
    // possible to easily encode parameters on the client side. Solidity does not support generics
    // so it's not possible to do this with explicit typing.

    /// @notice Returns the name of the drip check.
    /// @return name_ The name of the drip check.
    function name() external view returns (string memory name_);

    /// @notice Checks whether a drip should be executable.
    /// @param _params Encoded parameters for the drip check.
    /// @return execute_ Whether the drip should be executed.
    function check(bytes memory _params) external view returns (bool execute_);
}

Read Contract

created 0x82cb6b72 → string
drips 0x4d7fba6e → uint8, tuple, uint256, uint256
executable 0xfc3e3eba → bool
getDripCount 0xf1d42b47 → uint256
getDripInterval 0x90547c14 → uint256
getDripStatus 0x0d8f4697 → uint8
owner 0x8da5cb5b → address

Write Contract 11 functions

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

CALL 0x6e2d44ae
address _target
bytes _data
uint256 _value
returns: bool, bytes
DELEGATECALL 0xedee6239
address _target
bytes _data
returns: bool, bytes
create 0x5fb7b9c4
string _name
tuple _config
drip 0x67148cd2
string _name
setOwner 0x13af4035
address newOwner
status 0x9bc94d01
string _name
uint8 _status
withdrawERC20 0x44004cc1
address _asset
address _to
uint256 _amount
withdrawERC20 0x9456fbcc
address _asset
address _to
withdrawERC721 0x4025feb2
address _asset
address _to
uint256 _id
withdrawETH 0x4782f779
address _to
uint256 _amount
withdrawETH 0x690d8320
address _to

Token Balances (1)

View Transfers →
WETH 0

Recent Transactions

No transactions found for this address