Address Contract Partially Verified
Address
0x7078c4537C04c2b2E52ddBa06074dBdACF23cA15
Balance
30.0000 ETH
Nonce
1
Code Size
12203 bytes
Creator
0x2179a608...27B6 at tx 0x856c9bf3...84aeb9
Indexed Transactions
0
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 →Recent Transactions
No transactions found for this address