Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x0c4043F06D06458a236e2E72A18148349E987baF
Balance 0 ETH
Nonce 1
Code Size 13634 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

13634 bytes
0x6080604052600436101561001257600080fd5b60003560e01c806308e7f63a1461213e578063181f5a77146120af5780632303348a14611f725780632b596f6d14611ee45780633ccd14ff14611572578063695e1340146113965780636f351771146112ba578063724c13dd146111af5780637497066b1461109457806379ba509714610fbe5780637ec0846d14610f335780638da5cb5b14610ee15780639f4cb53414610ec0578063b87a019414610e6a578063d4b89c74146106af578063db80009214610614578063e3dce080146104d9578063e690f33214610362578063f2fde38b14610284578063f794bdeb146101495763f99ecb6b1461010357600080fd5b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457602060ff600b54166040519015158152f35b600080fd5b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760078054610185816124b8565b610192604051918261233f565b81815261019e826124b8565b916020937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208401940136853760005b82811061023257505050906040519283926020840190602085525180915260408401929160005b82811061020557505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff16855286955093810193928101926001016101f6565b6001908260005273ffffffffffffffffffffffffffffffffffffffff817fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801541661027d82876125ea565b52016101cf565b346101445760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610144576102bb612426565b6102c3612c83565b73ffffffffffffffffffffffffffffffffffffffff8091169033821461033857817fffffffffffffffffffffffff00000000000000000000000000000000000000006000541617600055600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b60046040517fdad89dca000000000000000000000000000000000000000000000000000000008152fd5b346101445760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760ff600b54166104af576103a760043533612f10565b600181019081549060ff8260c01c1660028110156104805760011461045657780100000000000000000000000000000000000000000000000091817fffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffff92549060405191602083527f6a0ed88e9cf3cb493ab4028fcb1dc7d18f0130fcdfba096edde0aadbfbf5e99f63ffffffff8560a01c16938061044d3395600260208401910161268c565b0390a416179055005b60046040517f6f861db1000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60046040517f78a4e7d9000000000000000000000000000000000000000000000000000000008152fd5b34610144576104e7366123ae565b916104f0612c83565b60ff600b54166104af5782156105ce5760005b82811061059357505b60405191806040840160408552526060830191906000905b80821061055b5785151560208601527f509460cccbb176edde6cac28895a4415a24961b8f3a0bd2617b9bb7b4e166c9b85850386a1005b90919283359073ffffffffffffffffffffffffffffffffffffffff821680920361014457600191815260208091019401920190610524565b806105c773ffffffffffffffffffffffffffffffffffffffff6105c16105bc6001958888612b23565b612c62565b166130eb565b5001610503565b60005b8281106105de575061050c565b8061060d73ffffffffffffffffffffffffffffffffffffffff6106076105bc6001958888612b23565b1661331c565b50016105d1565b346101445761063461062536612449565b9161062e6124d0565b50612b44565b6000526004602052604060002073ffffffffffffffffffffffffffffffffffffffff600182015416156106855761066d61068191612740565b6040519182916020835260208301906121fc565b0390f35b60046040517f871e01b2000000000000000000000000000000000000000000000000000000008152fd5b346101445760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760443567ffffffffffffffff8111610144576106fe903690600401612380565b9060643567ffffffffffffffff81116101445761071f903690600401612380565b9160843567ffffffffffffffff811161014457610740903690600401612380565b60ff600b94929454166104af57610758818688612d78565b61076460043533612f10565b9163ffffffff600184015460a01c169561077e3388612cce565b8354946107b060405161079f816107988160038b0161268c565b038261233f565b6107aa368c856128f4565b90612f7f565b6107d26040516107c7816107988160048c0161268c565b6107aa3686886128f4565b6107f46040516107e9816107988160058d0161268c565b6107aa36898d6128f4565b918080610e63575b80610e5c575b610e3257610811602435612e68565b88600052600660205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055602435885515610cdd575b15610b8c575b156108df575b926108ca7f41161473ce2ed633d9f902aab9702d16a5531da27ec84e1939abeffe54ad735395936108da936108bc6108ae978d604051998a996024358b5260a060208c0152600260a08c01910161268c565b9189830360408b01526129b7565b9186830360608801526129b7565b90838203608085015233976129b7565b0390a4005b6108ec6005860154612639565b610b25575b67ffffffffffffffff8411610af65761091a846109116005880154612639565b60058801612970565b6000601f85116001146109f6579284926108bc6108ca938a9b9c61099e876108ae9b9a6108da9a7f41161473ce2ed633d9f902aab9702d16a5531da27ec84e1939abeffe54ad73539e9f6000926109eb575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60058a01555b8c87806109c1575b50509c9b9a995093505092949550925061085c565b6109cb9133612b44565b60005260056020526109e3600435604060002061313d565b508c876109ac565b013590508f8061096c565b9860058601600052602060002060005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe087168110610ade5750926108bc6108ca936108da969388968c7f41161473ce2ed633d9f902aab9702d16a5531da27ec84e1939abeffe54ad73539c9d9e9f897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06108ae9e9d1610610aa6575b505050600187811b0160058a01556109a4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88b60031b161c199101351690558e8d81610a93565b898c0135825560209b8c019b60019092019101610a06565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516020810190610b6b81610b3f60058a0133866129f6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261233f565b5190206000526005602052610b8660043560406000206133e3565b506108f1565b67ffffffffffffffff8311610af657610bb583610bac6004890154612639565b60048901612970565b600083601f8111600114610c165780610c0192600091610c0b575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b6004870155610856565b90508601358d610bd0565b506004870160005260206000209060005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086168110610cc55750847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610610c8d575b5050600183811b016004870155610856565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c19908601351690558a80610c7b565b9091602060018192858a013581550193019101610c27565b67ffffffffffffffff8b11610af657610d068b610cfd60038a0154612639565b60038a01612970565b60008b601f8111600114610d665780610d5192600091610d5b57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b6003880155610850565b90508501358e610bd0565b506003880160005260206000209060005b8d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081168210610e19578091507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610610de0575b905060018092501b016003880155610850565b60f87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9160031b161c19908501351690558b808c610dcd565b5085820135835560019092019160209182019101610d77565b60046040517f6b4a810d000000000000000000000000000000000000000000000000000000008152fd5b5082610802565b50816107fc565b346101445760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457610681610eb4610ea7612426565b6044359060243590612b9f565b604051918291826122a0565b34610144576020610ed9610ed336612449565b91612b44565b604051908152f35b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457610f6a612c83565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600b5416600b557f11a03e25ee25bf1459f9e1cb293ea03707d84917f54a65e32c9a7be2f2edd68a6020604051338152a1005b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760005473ffffffffffffffffffffffffffffffffffffffff808216330361106a57600154917fffffffffffffffffffffffff0000000000000000000000000000000000000000903382851617600155166000553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60046040517f02b543c6000000000000000000000000000000000000000000000000000000008152fd5b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457600980546110d0816124b8565b6110dd604051918261233f565b8181526110e9826124b8565b916020937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208401940136853760005b82811061116d57505050906040519283926020840190602085525180915260408401929160005b82811061115057505050500390f35b835163ffffffff1685528695509381019392810192600101611141565b6001908260005263ffffffff817f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0154166111a882876125ea565b520161111a565b34610144576111bd366123ae565b916111c6612c83565b60ff600b54166104af5782156112845760005b82811061125957505b60405191806040840160408552526060830191906000905b8082106112315785151560208601527fcab63bf31d1e656baa23cebef64e12033ea0ffbd44b1278c3747beec2d2f618c85850386a1005b90919283359063ffffffff8216809203610144576001918152602080910194019201906111fa565b8061127d63ffffffff6112776112726001958888612b23565b612b33565b16613032565b50016111d9565b60005b82811061129457506111e2565b806112b363ffffffff6112ad6112726001958888612b23565b166131c9565b5001611287565b346101445760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760ff600b54166104af576112ff60043533612f10565b6001810190815463ffffffff8160a01c1660ff8260c01c1660028110156104805715610456577fffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffff926113513383612cce565b80547f17b2d730bb5e064df3fbc6165c8aceb3b0d62c524c196c0bc1012209280bc9a6604051602081528061138e3395600260208401910161268c565b0390a4169055005b34610144576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610144576004359060ff600b54166104af576113de8233612f10565b916113f6336000526008602052604060002054151590565b156115425782600493546000526006835260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905533600052600283526114488260406000206133e3565b50600181019063ffffffff80835460a01c166000526003855261146f8460406000206133e3565b506005820161147e8154612639565b61150e575b508154925460a01c16917f76ee2dfcae10cb8522e62e713e62660e09ecfaab08db15d9404de19141322571604051868152806114c6339560028a8401910161268c565b0390a46000525261150c6005604060002060008155600060018201556114ee60028201612ada565b6114fa60038201612ada565b61150660048201612ada565b01612ada565b005b60405161152381610b3f8982019433866129f6565b5190206000526005855261153b8460406000206133e3565b5086611483565b60246040517f85982a00000000000000000000000000000000000000000000000000000000008152336004820152fd5b346101445760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760043567ffffffffffffffff8111610144576115c1903690600401612380565b6044359163ffffffff8316830361014457600260643510156101445760843567ffffffffffffffff8111610144576115fd903690600401612380565b91909260a43567ffffffffffffffff811161014457611620903690600401612380565b60c43567ffffffffffffffff811161014457611640903690600401612380565b96909560ff600b54166104af57611657338a612cce565b8415611eba5760408511611e8257611670888483612d78565b61167b858733612b44565b80600052600460205273ffffffffffffffffffffffffffffffffffffffff60016040600020015416611e58576116b2602435612e68565b604051906116bf82612322565b602435825233602083015263ffffffff8b1660408301526116e56064356060840161262d565b6116f036888a6128f4565b60808301526117003684866128f4565b60a08301526117103686886128f4565b60c0830152611720368b8b6128f4565b60e0830152806000526004602052604060002091805183556001830173ffffffffffffffffffffffffffffffffffffffff60208301511681549077ffffffff0000000000000000000000000000000000000000604085015160a01b1690606085015160028110156104805778ff0000000000000000000000000000000000000000000000007fffffffffffffff000000000000000000000000000000000000000000000000009160c01b1693161717179055608081015180519067ffffffffffffffff8211610af657611803826117fa6002880154612639565b60028801612970565b602090601f8311600114611d8c57611850929160009183611cb55750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60028401555b60a081015180519067ffffffffffffffff8211610af6576118878261187e6003880154612639565b60038801612970565b602090601f8311600114611cc0576118d4929160009183611cb55750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60038401555b60c081015180519067ffffffffffffffff8211610af65761190b826119026004880154612639565b60048801612970565b602090601f8311600114611be857918061195c9260e09594600092611ac35750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b60048501555b015180519267ffffffffffffffff8411610af657838d926119948e9661198b6005860154612639565b60058601612970565b602090601f8311600114611ace579463ffffffff6108bc95819a957fc4399022965bad9b2b468bbd8c758a7e80cdde36ff3088ddbb7f93bdfb5623cb9f9e9d9994611a20876108da9f9b98600593611a849f9a600092611ac35750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b9101555b336000526002602052611a3b83604060002061313d565b50166000526003602052611a5381604060002061313d565b508d82611a9a575b5050506108ae6040519a8b9a611a738c606435612191565b60a060208d015260a08c01916129b7565b97838903608085015216963396602435966129b7565b611aba92611aa89133612b44565b6000526005602052604060002061313d565b508c8f8d611a5b565b01519050388061096c565b906005840160005260206000209160005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085168110611bbe57506108bc9563ffffffff9a957fc4399022965bad9b2b468bbd8c758a7e80cdde36ff3088ddbb7f93bdfb5623cb9f9e9d99946001876108da9f9b96928f9693611a849f9a94837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06005971610611b87575b505050811b01910155611a24565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c19169055388080611b79565b939550918194969750600160209291839285015181550194019201918f9492918f97969492611adf565b906004860160005260206000209160005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085168110611c9d5750918391600193837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060e098971610611c66575b505050811b016004850155611962565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558f8080611c56565b91926020600181928685015181550194019201611bf9565b015190508f8061096c565b9190600386016000526020600020906000935b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168510611d715760019450837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610611d3a575b505050811b0160038401556118da565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558e8080611d2a565b81810151835560209485019460019093019290910190611cd3565b9190600286016000526020600020906000935b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168510611e3d5760019450837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0811610611e06575b505050811b016002840155611856565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558e8080611df6565b81810151835560209485019460019093019290910190611d9f565b60046040517fa0677dd0000000000000000000000000000000000000000000000000000000008152fd5b604485604051907f36a7c503000000000000000000000000000000000000000000000000000000008252600482015260406024820152fd5b60046040517f485b8ed4000000000000000000000000000000000000000000000000000000008152fd5b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457611f1b612c83565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600b541617600b557f2789711f6fd67d131ad68378617b5d1d21a2c92b34d7c3745d70b3957c08096c6020604051338152a1005b34610144576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760043567ffffffffffffffff811161014457611fc2903690600401612380565b60ff600b54166104af57611fd69133612b44565b9081600052600560205260406000209182549182156106855760005b838110611ffb57005b806120086001928761301a565b90549060031b1c60005260048352604060002063ffffffff8382015460a01c16600052600a8452604060002054151580612092575b612049575b5001611ff2565b7f95d94f817db4971aa99ba35d0fe019bd8cc39866fbe02b6d47b5f0f3727fb673604051868152604086820152806120893394600260408401910161268c565b0390a286612042565b506120aa336000526008602052604060002054151590565b61203d565b346101445760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014457604051604081019080821067ffffffffffffffff831117610af65761068191604052601681527f576f726b666c6f77526567697374727920312e302e3000000000000000000000602082015260405191829160208352602083019061219e565b346101445760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101445760043563ffffffff8116810361014457610eb46106819160443590602435906127ff565b9060028210156104805752565b919082519283825260005b8481106121e85750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b6020818301810151848301820152016121a9565b61229d9160e061228c61227a6122686101008651865273ffffffffffffffffffffffffffffffffffffffff602088015116602087015263ffffffff604088015116604087015261225460608801516060880190612191565b60808701519080608088015286019061219e565b60a086015185820360a087015261219e565b60c085015184820360c086015261219e565b9201519060e081840391015261219e565b90565b6020808201906020835283518092526040830192602060408460051b8301019501936000915b8483106122d65750505050505090565b9091929394958480612312837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528a516121fc565b98019301930191949392906122c6565b610100810190811067ffffffffffffffff821117610af657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610af657604052565b9181601f840112156101445782359167ffffffffffffffff8311610144576020838186019501011161014457565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101445760043567ffffffffffffffff9283821161014457806023830112156101445781600401359384116101445760248460051b8301011161014457602401919060243580151581036101445790565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361014457565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101445760043573ffffffffffffffffffffffffffffffffffffffff8116810361014457916024359067ffffffffffffffff8211610144576124b491600401612380565b9091565b67ffffffffffffffff8111610af65760051b60200190565b604051906124dd82612322565b606060e0836000815260006020820152600060408201526000838201528260808201528260a08201528260c08201520152565b6040516020810181811067ffffffffffffffff821117610af6576040526000815290565b9061253e826124b8565b61254b604051918261233f565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061257982946124b8565b019060005b82811061258a57505050565b6020906125956124d0565b8282850101520161257e565b919082018092116125ae57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b919082039182116125ae57565b80518210156125fe5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60028210156104805752565b90600182811c92168015612682575b602083101461265357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691612648565b80546000939261269b82612639565b9182825260209360019160018116908160001461270357506001146126c2575b5050505050565b90939495506000929192528360002092846000945b8386106126ef575050505001019038808080806126bb565b8054858701830152940193859082016126d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b0101915038808080806126bb565b90600560e06040936127fb85519161275783612322565b6127f48397825485526127a160ff600185015473ffffffffffffffffffffffffffffffffffffffff8116602089015263ffffffff8160a01c168489015260c01c166060870161262d565b80516127b481610798816002880161268c565b608086015280516127cc81610798816003880161268c565b60a086015280516127e481610798816004880161268c565b60c086015251809681930161268c565b038461233f565b0152565b63ffffffff1691600083815260036020906003602052604093604084205490818710156128e4576128539181606489931180156128dc575b6128d4575b8161284782856125a1565b11156128c457506125dd565b9461285d86612534565b96845b87811061287257505050505050505090565b60019082875284865261289188882061288b83876125a1565b9061301a565b905490861b1c8752600486526128a8888820612740565b6128b2828c6125ea565b526128bd818b6125ea565b5001612860565b6128cf9150826125a1565b6125dd565b50606461283c565b508015612837565b505050505050505061229d612510565b92919267ffffffffffffffff8211610af6576040519161293c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461233f565b829481845281830111610144578281602093846000960137010152565b818110612964575050565b60008155600101612959565b9190601f811161297f57505050565b6129ab926000526020600020906020601f840160051c830193106129ad575b601f0160051c0190612959565b565b909150819061299e565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b91907fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009060601b168252601490600092815492612a3284612639565b92600194600181169081600014612a995750600114612a54575b505050505090565b9091929395945060005260209460206000206000905b858210612a865750505050601492935001013880808080612a4c565b8054858301850152908701908201612a6a565b92505050601494507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091935016838301528015150201013880808080612a4c565b612ae48154612639565b9081612aee575050565b81601f60009311600114612b00575055565b908083918252612b1f601f60208420940160051c840160018501612959565b5555565b91908110156125fe5760051b0190565b3563ffffffff811681036101445790565b91906034612b9991836040519485927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602085019860601b16885284840137810160008382015203601481018452018261233f565b51902090565b73ffffffffffffffffffffffffffffffffffffffff169160008381526002926020906002602052604093604084205490818310156128e457612bf69181606485931180156128dc576128d4578161284782856125a1565b94612c0086612534565b96845b878110612c1557505050505050505090565b600190828752838652612c2e88882061288b83886125a1565b90549060031b1c875260048652612c46888820612740565b612c50828c6125ea565b52612c5b818b6125ea565b5001612c03565b3573ffffffffffffffffffffffffffffffffffffffff811681036101445790565b73ffffffffffffffffffffffffffffffffffffffff600154163303612ca457565b60046040517f2b5c74de000000000000000000000000000000000000000000000000000000008152fd5b63ffffffff1680600052600a60205260406000205415612d47575073ffffffffffffffffffffffffffffffffffffffff1680600052600860205260406000205415612d165750565b602490604051907f85982a000000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907f8fe6d7e10000000000000000000000000000000000000000000000000000000082526004820152fd5b908115612e3e5760c891828111612e085750818111612dd35750808211612d9d575050565b60449250604051917ecd56a800000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b604491604051917ecd56a800000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60449083604051917ecd56a800000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517f9cd963cf000000000000000000000000000000000000000000000000000000008152fd5b8015612ee65780600052600660205260ff60406000205416612ebc576000526006602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b60046040517f4cb050e4000000000000000000000000000000000000000000000000000000008152fd5b60046040517f7dc2f4e1000000000000000000000000000000000000000000000000000000008152fd5b90600052600460205260406000209073ffffffffffffffffffffffffffffffffffffffff806001840154169182156106855716809103612f4e575090565b602490604051907f31ee6dc70000000000000000000000000000000000000000000000000000000082526004820152fd5b9081518151908181149384612f96575b5050505090565b6020929394508201209201201438808080612f8f565b6009548110156125fe5760096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0190600090565b6007548110156125fe5760076000527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880190600090565b80548210156125fe5760005260206000200190600090565b6000818152600a60205260408120546130e657600954680100000000000000008110156130b95790826130a561307084600160409601600955612fac565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b9055600954928152600a6020522055600190565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b905090565b6000818152600860205260408120546130e657600754680100000000000000008110156130b957908261312961307084600160409601600755612fe3565b905560075492815260086020522055600190565b919060018301600090828252806020526040822054156000146131c357845494680100000000000000008610156131965783613186613070886001604098999a0185558461301a565b9055549382526020522055600190565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b50925050565b6000818152600a60205260408120549091908015613317577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908181018181116132ea57600954908382019182116132bd57818103613289575b505050600954801561325c5781019061323b82612fac565b909182549160031b1b191690556009558152600a6020526040812055600190565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526031600452fd5b6132a761329861307093612fac565b90549060031b1c928392612fac565b90558452600a6020526040842055388080613223565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b505090565b6000818152600860205260408120549091908015613317577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908181018181116132ea57600754908382019182116132bd578181036133af575b505050600754801561325c5781019061338e82612fe3565b909182549160031b1b19169055600755815260086020526040812055600190565b6133cd6133be61307093612fe3565b90549060031b1c928392612fe3565b9055845260086020526040842055388080613376565b90600182019060009281845282602052604084205490811515600014612f8f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91828101818111613508578254908482019182116134db578181036134a6575b505050805480156134795782019161345c838361301a565b909182549160031b1b191690555582526020526040812055600190565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526031600452fd5b6134c66134b6613070938661301a565b90549060031b1c9283928661301a565b90558652846020526040862055388080613444565b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fdfea164736f6c6343000818000a

Verified Source Code Partial Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: paris Optimization: Yes (1000000 runs)
WorkflowRegistry.sol 732 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol";

import {Ownable2StepMsgSender} from "../shared/access/Ownable2StepMsgSender.sol";

import {Strings} from "../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/Strings.sol";
import {EnumerableSet} from "../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol";

contract WorkflowRegistry is Ownable2StepMsgSender, ITypeAndVersion {
  using EnumerableSet for EnumerableSet.Bytes32Set;
  using EnumerableSet for EnumerableSet.AddressSet;
  using EnumerableSet for EnumerableSet.UintSet;

  string public constant override typeAndVersion = "WorkflowRegistry 1.0.0";
  uint8 private constant MAX_WORKFLOW_NAME_LENGTH = 64;
  uint8 private constant MAX_URL_LENGTH = 200;
  uint8 private constant MAX_PAGINATION_LIMIT = 100;

  enum WorkflowStatus {
    ACTIVE,
    PAUSED
  }

  struct WorkflowMetadata {
    bytes32 workflowID; //     Unique identifier from hash of owner address, WASM binary content, config content and secrets URL.
    address owner; // ─────────╮ Workflow owner.
    uint32 donID; //           │ Unique identifier for the Workflow DON.
    WorkflowStatus status; // ─╯ Current status of the workflow (active, paused).
    string workflowName; //    Human readable string capped at 64 characters length.
    string binaryURL; //       URL to the WASM binary.
    string configURL; //       URL to the config.
    string secretsURL; //      URL to the encrypted secrets. Workflow DON applies a default refresh period (e.g. daily).
  }

  /// @dev Maps an owner address to a set of their workflow (name + owner) hashess.
  mapping(address owner => EnumerableSet.Bytes32Set workflowKeys) private s_ownerWorkflowKeys;
  /// @dev Maps a DON ID to a set of workflow IDs.
  mapping(uint32 donID => EnumerableSet.Bytes32Set workflowKeys) private s_donWorkflowKeys;
  /// @dev Maps a workflow (name + owner) hash to its corresponding workflow metadata.
  mapping(bytes32 workflowKey => WorkflowMetadata workflowMetadata) private s_workflows;
  /// @dev Mapping to track workflows by secretsURL hash (owner + secretsURL).
  /// This is used to find all workflows that have the same secretsURL when a force secrets update event is requested.
  mapping(bytes32 secretsURLHash => EnumerableSet.Bytes32Set workflowKeys) private s_secretsHashToWorkflows;
  /// @dev Keep track of all workflowIDs to ensure uniqueness.
  mapping(bytes32 workflowID => bool inUse) private s_workflowIDs;

  /// @dev List of all authorized EOAs/contracts allowed to access this contract's state functions. All view functions are open access.
  EnumerableSet.AddressSet private s_authorizedAddresses;
  /// @dev List of all authorized DON IDs.
  EnumerableSet.UintSet private s_allowedDONs;

  bool private s_registryLocked = false;

  event AllowedDONsUpdatedV1(uint32[] donIDs, bool allowed);
  event AuthorizedAddressesUpdatedV1(address[] addresses, bool allowed);
  event WorkflowRegisteredV1(
    bytes32 indexed workflowID,
    address indexed workflowOwner,
    uint32 indexed donID,
    WorkflowStatus status,
    string workflowName,
    string binaryURL,
    string configURL,
    string secretsURL
  );
  event WorkflowUpdatedV1(
    bytes32 indexed oldWorkflowID,
    address indexed workflowOwner,
    uint32 indexed donID,
    bytes32 newWorkflowID,
    string workflowName,
    string binaryURL,
    string configURL,
    string secretsURL
  );
  event WorkflowPausedV1(
    bytes32 indexed workflowID, address indexed workflowOwner, uint32 indexed donID, string workflowName
  );
  event WorkflowActivatedV1(
    bytes32 indexed workflowID, address indexed workflowOwner, uint32 indexed donID, string workflowName
  );
  event WorkflowDeletedV1(
    bytes32 indexed workflowID, address indexed workflowOwner, uint32 indexed donID, string workflowName
  );
  event WorkflowForceUpdateSecretsRequestedV1(address indexed owner, bytes32 secretsURLHash, string workflowName);
  event RegistryLockedV1(address lockedBy);
  event RegistryUnlockedV1(address unlockedBy);

  error AddressNotAuthorized(address caller);
  error BinaryURLRequired();
  error CallerIsNotWorkflowOwner(address caller);
  error DONNotAllowed(uint32 donID);
  error InvalidWorkflowID();
  error RegistryLocked();
  error URLTooLong(uint256 providedLength, uint8 maxAllowedLength);
  error WorkflowAlreadyInDesiredStatus();
  error WorkflowAlreadyRegistered();
  error WorkflowContentNotUpdated();
  error WorkflowDoesNotExist();
  error WorkflowIDAlreadyExists();
  error WorkflowNameRequired();
  error WorkflowNameTooLong(uint256 providedLength, uint8 maxAllowedLength);

  modifier registryNotLocked() {
    if (s_registryLocked) revert RegistryLocked();
    _;
  }

  // ================================================================
  // |                            Admin                             |
  // ================================================================

  /// @notice Updates the list of allowed DON IDs.
  /// @dev If a DON ID with associated workflows is removed from the allowed DONs list, the only allowed actions on
  /// workflows for that DON are to pause or delete them. It will no longer be possible to update, activate, or register
  /// new workflows for a removed DON.
  /// @param donIDs The list of unique identifiers for Workflow DONs.
  /// @param allowed True if they should be added to the allowlist, false to remove them.
  function updateAllowedDONs(uint32[] calldata donIDs, bool allowed) external onlyOwner registryNotLocked {
    uint256 length = donIDs.length;
    if (allowed) {
      for (uint256 i = 0; i < length; ++i) {
        s_allowedDONs.add(donIDs[i]);
      }
    } else {
      for (uint256 i = 0; i < length; ++i) {
        s_allowedDONs.remove(donIDs[i]);
      }
    }

    emit AllowedDONsUpdatedV1(donIDs, allowed);
  }

  /// @notice Updates a list of authorized addresses that can register workflows.
  /// @dev We don't check if an existing authorized address will be set to false, please take extra caution.
  /// @param addresses The list of addresses.
  /// @param allowed True if they should be added to whitelist, false to remove them.
  function updateAuthorizedAddresses(address[] calldata addresses, bool allowed) external onlyOwner registryNotLocked {
    uint256 length = addresses.length;
    if (allowed) {
      for (uint256 i = 0; i < length; ++i) {
        s_authorizedAddresses.add(addresses[i]);
      }
    } else {
      for (uint256 i = 0; i < length; ++i) {
        s_authorizedAddresses.remove(addresses[i]);
      }
    }

    emit AuthorizedAddressesUpdatedV1(addresses, allowed);
  }

  /// @notice Locks the registry, preventing any further modifications.
  /// @dev This function can only be called by the owner of the contract. Once locked, the registry cannot be modified
  /// until it is unlocked by calling `unlockRegistry`. Emits a `RegistryLockedV1` event.
  function lockRegistry() external onlyOwner {
    s_registryLocked = true;
    emit RegistryLockedV1(msg.sender);
  }

  /// @notice Unlocks the registry, allowing modifications to be made.
  /// @dev This function can only be called by the owner of the contract. Once unlocked, the registry can be modified
  /// again. Emits a `RegistryUnlockedV1` event.
  function unlockRegistry() external onlyOwner {
    s_registryLocked = false;
    emit RegistryUnlockedV1(msg.sender);
  }

  // ================================================================
  // |                       Workflow Management                    |
  // ================================================================

  /// @notice Registers a new workflow.
  /// @dev Registers a new workflow after validating the caller, DON ID, workflow name, and workflow metadata.
  /// This function performs the following steps:
  /// - Validates the caller is authorized and the DON ID is allowed.
  /// - Validates the workflow metadata (workflowID, binaryURL, configURL, secretsURL) lengths.
  /// - Checks if the workflow with the given name already exists for the owner.
  /// - Stores the workflow metadata in the appropriate mappings for the owner and DON.
  /// - Adds the secretsURL to the hash mapping if present.
  ///
  /// Requirements:
  /// - Caller must be an authorized address.
  /// - The provided DON ID must be allowed.
  /// - The workflow name must not exceed `MAX_WORKFLOW_NAME_LENGTH`.
  /// - Workflow metadata must be valid and adhere to set requirements.
  /// - Workflow with the given name must not already exist for the owner.
  ///
  /// Emits:
  /// - `WorkflowRegisteredV1` event upon successful registration.
  ///
  /// @param workflowName The human-readable name for the workflow. Must not exceed 64 characters.
  /// @param workflowID The unique identifier for the workflow based on the WASM binary content, config content and
  /// secrets URL.
  /// @param donID The unique identifier of the Workflow DON that this workflow is associated with.
  /// @param status Initial status for this workflow after registration (e.g., Active or Paused).
  /// @param binaryURL The URL pointing to the WASM binary for the workflow.
  /// @param configURL The URL pointing to the configuration file for the workflow.
  /// @param secretsURL The URL pointing to the secrets file for the workflow. Can be empty if there are no secrets.
  function registerWorkflow(
    string calldata workflowName,
    bytes32 workflowID,
    uint32 donID,
    WorkflowStatus status,
    string calldata binaryURL,
    string calldata configURL,
    string calldata secretsURL
  ) external registryNotLocked {
    _validatePermissions(donID, msg.sender);
    _validateWorkflowName(bytes(workflowName).length);
    _validateWorkflowURLs(bytes(binaryURL).length, bytes(configURL).length, bytes(secretsURL).length);

    bytes32 workflowKey = computeHashKey(msg.sender, workflowName);
    if (s_workflows[workflowKey].owner != address(0)) {
      revert WorkflowAlreadyRegistered();
    }

    _requireUniqueWorkflowID(workflowID);

    // Create new workflow entry
    s_workflows[workflowKey] = WorkflowMetadata({
      workflowID: workflowID,
      owner: msg.sender,
      donID: donID,
      status: status,
      workflowName: workflowName,
      binaryURL: binaryURL,
      configURL: configURL,
      secretsURL: secretsURL
    });

    s_ownerWorkflowKeys[msg.sender].add(workflowKey);
    s_donWorkflowKeys[donID].add(workflowKey);

    // Hash the secretsURL and add the workflow to the secrets hash mapping
    if (bytes(secretsURL).length > 0) {
      bytes32 secretsHash = computeHashKey(msg.sender, secretsURL);
      s_secretsHashToWorkflows[secretsHash].add(workflowKey);
    }

    emit WorkflowRegisteredV1(workflowID, msg.sender, donID, status, workflowName, binaryURL, configURL, secretsURL);
  }

  /// @notice Updates the workflow metadata for a given workflow.
  /// @dev Updates the workflow metadata based on the provided parameters.
  /// - If a field needs to be updated, the new value should be provided.
  /// - If the value should remain unchanged, provide the same value as before.
  /// - To remove an optional field (such as `configURL` or `secretsURL`), pass an empty string ("").
  /// - To get the workflowKey, use `computeHashKey` with the workflow owner's address and the workflow name, or
  ///   perform an offchain equivalent of keccak256(abi.encodePacked(owner, workflowName)).
  ///
  /// This function performs the following steps:
  /// - Validates the provided workflow metadata.
  /// - Retrieves the workflow by the caller's address and `workflowName`.
  /// - Updates only the fields that have changed.
  /// - Ensures that the workflow ID (`newWorkflowID`) must change and at least one of the URLs must also change.
  /// - Updates the `secretsURL` hash mappings if the `secretsURL` changes.
  ///
  /// Requirements:
  /// - `binaryURL` must always be provided, as it is required.
  /// - If only one field is being updated, the other fields must be provided with their current values to keep them unchanged, otherwise
  ///   they will be treated as empty strings.
  /// - The DON ID must be in the allowed list to perform updates.
  /// - The caller must be an authorized address. This means that even if the caller is the owner of the workflow, if they were later
  ///   removed from the authorized addresses list, they will not be able to perform updates.
  ///
  /// Emits:
  /// - `WorkflowUpdatedV1` event indicating the workflow has been successfully updated.
  ///
  /// @param workflowKey The unique identifier for the workflow.
  /// @param newWorkflowID The rehashed unique identifier for the workflow.
  /// @param binaryURL The URL pointing to the WASM binary. Must always be provided.
  /// @param configURL The URL pointing to the configuration file. Provide an empty string ("") to remove it.
  /// @param secretsURL The URL pointing to the secrets file. Provide an empty string ("") to remove it.
  function updateWorkflow(
    bytes32 workflowKey,
    bytes32 newWorkflowID,
    string calldata binaryURL,
    string calldata configURL,
    string calldata secretsURL
  ) external registryNotLocked {
    _validateWorkflowURLs(bytes(binaryURL).length, bytes(configURL).length, bytes(secretsURL).length);

    WorkflowMetadata storage workflow = _getWorkflowFromStorage(msg.sender, workflowKey);

    uint32 donID = workflow.donID;
    _validatePermissions(donID, msg.sender);

    // Store the old workflowID for event emission.
    bytes32 currentWorkflowID = workflow.workflowID;

    // Determine which URLs have changed
    bool sameBinaryURL = Strings.equal(workflow.binaryURL, binaryURL);
    bool sameConfigURL = Strings.equal(workflow.configURL, configURL);
    bool sameSecretsURL = Strings.equal(workflow.secretsURL, secretsURL);
    if (sameBinaryURL && sameConfigURL && sameSecretsURL) {
      revert WorkflowContentNotUpdated();
    }

    // Ensure the new workflowID is unique
    _requireUniqueWorkflowID(newWorkflowID);

    // Free the old workflowID
    s_workflowIDs[currentWorkflowID] = false;

    // Update all fields that have changed and the relevant sets
    workflow.workflowID = newWorkflowID;
    if (!sameBinaryURL) {
      workflow.binaryURL = binaryURL;
    }
    if (!sameConfigURL) {
      workflow.configURL = configURL;
    }
    if (!sameSecretsURL) {
      // Remove the old secrets hash if secretsURL is not empty
      if (bytes(workflow.secretsURL).length > 0) {
        // Using keccak256 instead of computeHashKey as currentSecretsURL is memory
        bytes32 oldSecretsHash = keccak256(abi.encodePacked(msg.sender, workflow.secretsURL));
        s_secretsHashToWorkflows[oldSecretsHash].remove(workflowKey);
      }

      workflow.secretsURL = secretsURL;

      // Add the new secrets hash if secretsURL is not empty
      if (bytes(secretsURL).length > 0) {
        bytes32 newSecretsHash = computeHashKey(msg.sender, secretsURL);
        s_secretsHashToWorkflows[newSecretsHash].add(workflowKey);
      }
    }

    // Emit an event after updating the workflow
    emit WorkflowUpdatedV1(
      currentWorkflowID, msg.sender, donID, newWorkflowID, workflow.workflowName, binaryURL, configURL, secretsURL
    );
  }

  /// @notice Pauses an existing workflow.
  /// @dev Workflows with any DON ID can be paused. If a caller was later removed from the authorized addresses list,
  /// they will still be able to pause the workflow.
  ///
  /// To get the workflowKey, use `computeHashKey` with the workflow owner's address and the workflow name, or perform
  /// an offchain equivalent of `keccak256(abi.encodePacked(owner, workflowName))`.
  /// @param workflowKey The unique identifier for the workflow.
  function pauseWorkflow(
    bytes32 workflowKey
  ) external registryNotLocked {
    _updateWorkflowStatus(workflowKey, WorkflowStatus.PAUSED);
  }

  /// @notice Activates an existing workflow.
  /// @dev The DON ID for the workflow must be in the allowed list to perform this action. The caller must also be an
  /// authorized address. This means that even if the caller is the owner of the workflow, if they were later removed
  /// from the authorized addresses list, they will not be able to activate the workflow.
  ///
  /// To get the workflowKey, use `computeHashKey` with the workflow owner's address and the workflow name, or perform
  /// an offchain equivalent of `keccak256(abi.encodePacked(owner, workflowName))`.
  /// @param workflowKey The unique identifier for the workflow.
  function activateWorkflow(
    bytes32 workflowKey
  ) external registryNotLocked {
    _updateWorkflowStatus(workflowKey, WorkflowStatus.ACTIVE);
  }

  /// @notice Deletes an existing workflow, removing it from the contract storage.
  /// @dev This function permanently removes a workflow associated with the caller's address.
  /// - Workflows with any DON ID can be deleted.
  /// - The caller must also be an authorized address. This means that even if the caller is the owner of the workflow,
  ///   if they were later removed from the authorized addresses list, they will not be able to delete the workflow.
  /// - To get the workflowKey, use `computeHashKey` with the workflow owner's address and the workflow name, or
  ///   perform an offchain equivalent of `keccak256(abi.encodePacked(owner, workflowName))`.
  ///
  /// The function performs the following operations:
  /// - Retrieves the workflow metadata using the workflow name and owner address.
  /// - Ensures that only the owner of the workflow can perform this operation.
  /// - Deletes the workflow from the `s_workflows` mapping.
  /// - Removes the workflow from associated sets (`s_ownerWorkflowKeys`, `s_donWorkflowKeys`, and
  ///   `s_secretsHashToWorkflows`).
  ///
  /// Requirements:
  /// - The caller must be the owner of the workflow and an authorized address.
  ///
  /// Emits:
  /// - `WorkflowDeletedV1` event indicating that the workflow has been deleted successfully.
  ///
  /// @param workflowKey The unique identifier for the workflow.
  function deleteWorkflow(
    bytes32 workflowKey
  ) external registryNotLocked {
    // Retrieve workflow metadata from storage
    WorkflowMetadata storage workflow = _getWorkflowFromStorage(msg.sender, workflowKey);

    // Only checking access for the caller instead of using _validatePermissions so that even if the DON was removed from the
    // allowed list, the workflow can still be deleted.
    if (!s_authorizedAddresses.contains(msg.sender)) {
      revert AddressNotAuthorized(msg.sender);
    }

    // Release the workflowID for reuse
    s_workflowIDs[workflow.workflowID] = false;

    // Remove the workflow from the owner and DON mappings
    s_ownerWorkflowKeys[msg.sender].remove(workflowKey);
    s_donWorkflowKeys[workflow.donID].remove(workflowKey);

    // Remove the workflow from the secrets hash set if secretsURL is not empty
    if (bytes(workflow.secretsURL).length > 0) {
      // Using keccak256 instead of computeHashKey as secretsURL is storage ref
      bytes32 secretsHash = keccak256(abi.encodePacked(msg.sender, workflow.secretsURL));
      s_secretsHashToWorkflows[secretsHash].remove(workflowKey);
    }

    // Emit an event indicating the workflow has been deleted. We need to do this before deleting the workflow from storage.
    emit WorkflowDeletedV1(workflow.workflowID, msg.sender, workflow.donID, workflow.workflowName);

    // Delete the workflow metadata from storage
    delete s_workflows[workflowKey];
  }

  /// @notice Requests a force update for workflows that share the same secrets URL.
  /// @dev This function allows an owner to request a force update for all workflows that share a given `secretsURL`.
  /// The `secretsURL` can be shared between multiple workflows, but they must all belong to the same owner. This
  /// function ensures that the caller owns all workflows associated with the given `secretsURL`.
  /// If you need to compare the `secretsHash` outside the contract, use `computeHashKey` with the owner's address and
  /// the `secretsURL` string passed into this function.
  ///
  /// The function performs the following steps:
  /// - Hashes the provided `secretsURL` and `msg.sender` to generate a unique mapping key.
  /// - Retrieves all workflows associated with the given secrets hash.
  /// - Collects the names of all matching workflows and emits an event indicating a force update request.
  ///
  /// Requirements:
  /// - The caller must be the owner of all workflows that share the given `secretsURL`.
  ///
  /// Emits:
  /// - `WorkflowForceUpdateSecretsRequestedV1` event indicating that a force update for workflows using this
  /// `secretsURL` has been requested.
  /// @param secretsURL The URL pointing to the updated secrets file. This can be shared among multiple workflows.
  function requestForceUpdateSecrets(
    string calldata secretsURL
  ) external registryNotLocked {
    // Use secretsURL and sender hash key to get the mapping key
    bytes32 secretsHash = computeHashKey(msg.sender, secretsURL);

    // Retrieve all workflow keys associated with the given secrets hash
    EnumerableSet.Bytes32Set storage workflowKeys = s_secretsHashToWorkflows[secretsHash];
    uint256 matchCount = workflowKeys.length();

    // No workflows found with the provided secretsURL
    if (matchCount == 0) {
      revert WorkflowDoesNotExist();
    }

    // Iterate through matched workflows and emit events for accessible ones
    for (uint256 i = 0; i < matchCount; ++i) {
      bytes32 workflowKey = workflowKeys.at(i);
      WorkflowMetadata storage workflow = s_workflows[workflowKey];

      if (s_allowedDONs.contains(workflow.donID) && s_authorizedAddresses.contains(msg.sender)) {
        emit WorkflowForceUpdateSecretsRequestedV1(msg.sender, secretsHash, workflow.workflowName);
      }
    }
  }

  /// @dev Internal function to update the workflow status.
  ///
  /// This function is used to change the status of an existing workflow, either to "Paused" or "Active".
  ///
  /// The function performs the following operations:
  /// - Retrieves the workflow metadata from storage based on the workflow name.
  /// - Only the owner of the workflow can update the status.
  /// - Checks if the workflow is already in the desired status, and reverts if no change is necessary to avoid
  ///   unnecessary storage writes.
  /// - Updates the status of the workflow and emits the appropriate event (`WorkflowPausedV1` or
  ///   `WorkflowActivatedV1`).
  ///
  /// Emits:
  /// - `WorkflowPausedV1` or `WorkflowActivatedV1` event indicating that the relevant workflow status has been updated.
  /// @param workflowKey The unique identifier for the workflow.
  /// @param newStatus The new status to set for the workflow (either `Paused` or `Active`).
  function _updateWorkflowStatus(bytes32 workflowKey, WorkflowStatus newStatus) internal {
    // Retrieve workflow metadata once
    WorkflowMetadata storage workflow = _getWorkflowFromStorage(msg.sender, workflowKey);
    uint32 donID = workflow.donID;

    // Avoid unnecessary storage writes if already in the desired status
    if (workflow.status == newStatus) {
      revert WorkflowAlreadyInDesiredStatus();
    }

    // Emit the appropriate event based on newStatus
    if (newStatus == WorkflowStatus.ACTIVE) {
      _validatePermissions(donID, msg.sender);
      emit WorkflowActivatedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
    } else if (newStatus == WorkflowStatus.PAUSED) {
      emit WorkflowPausedV1(workflow.workflowID, msg.sender, donID, workflow.workflowName);
    }

    // Update the workflow status
    workflow.status = newStatus;
  }

  /// @dev Internal function to retrieve a workflow from storage.
  /// @param sender The address of the caller. Must be the owner of the workflow.
  /// @param workflowKey The unique identifier for the workflow.
  /// @return workflow The workflow metadata.
  function _getWorkflowFromStorage(
    address sender,
    bytes32 workflowKey
  ) internal view returns (WorkflowMetadata storage workflow) {
    workflow = s_workflows[workflowKey];

    if (workflow.owner == address(0)) revert WorkflowDoesNotExist();
    if (workflow.owner != sender) revert CallerIsNotWorkflowOwner(sender);

    return workflow;
  }

  /// @notice Ensures the given workflowID is unique and marks it as used.
  /// @param workflowID The workflowID to validate and consume.
  function _requireUniqueWorkflowID(
    bytes32 workflowID
  ) internal {
    if (workflowID == bytes32(0)) revert InvalidWorkflowID();

    if (s_workflowIDs[workflowID]) {
      revert WorkflowIDAlreadyExists();
    }

    s_workflowIDs[workflowID] = true;
  }

  // ================================================================
  // |                       Workflow Queries                       |
  // ================================================================

  /// @notice Returns workflow metadata.
  /// @param workflowOwner Address that owns this workflow.
  /// @param workflowName The human-readable name for the workflow.
  /// @return WorkflowMetadata The metadata of the workflow.
  function getWorkflowMetadata(
    address workflowOwner,
    string calldata workflowName
  ) external view returns (WorkflowMetadata memory) {
    bytes32 workflowKey = computeHashKey(workflowOwner, workflowName);
    WorkflowMetadata storage workflow = s_workflows[workflowKey];

    if (workflow.owner == address(0)) revert WorkflowDoesNotExist();

    return workflow;
  }

  /// @notice Retrieves a list of workflow metadata for a specific owner.
  /// @dev This function allows paginated retrieval of workflows owned by a particular address. If the `limit` is set
  /// to 0 or exceeds the `MAX_PAGINATION_LIMIT`, the `MAX_PAGINATION_LIMIT` will be used instead in both cases.
  /// @param workflowOwner The address of the workflow owner for whom the workflow metadata is being retrieved.
  /// @param start The index at which to start retrieving workflows (zero-based index). If the start index is greater
  /// than or equal to the total number of workflows, an empty array is returned.
  /// @param limit The maximum number of workflow metadata entries to retrieve. If the limit exceeds the available
  /// number of workflows from the start index, only the available entries are returned.
  /// @return workflowMetadataList An array of `WorkflowMetadata` structs containing metadata of workflows owned by
  /// the specified owner.
  function getWorkflowMetadataListByOwner(
    address workflowOwner,
    uint256 start,
    uint256 limit
  ) external view returns (WorkflowMetadata[] memory workflowMetadataList) {
    uint256 totalWorkflows = s_ownerWorkflowKeys[workflowOwner].length();
    if (start >= totalWorkflows) {
      return new WorkflowMetadata[](0);
    }

    if (limit > MAX_PAGINATION_LIMIT || limit == 0) {
      limit = MAX_PAGINATION_LIMIT;
    }

    uint256 end = (start + limit > totalWorkflows) ? totalWorkflows : start + limit;

    uint256 resultLength = end - start;
    workflowMetadataList = new WorkflowMetadata[](resultLength);

    for (uint256 i = 0; i < resultLength; ++i) {
      bytes32 workflowKey = s_ownerWorkflowKeys[workflowOwner].at(start + i);
      workflowMetadataList[i] = s_workflows[workflowKey];
    }

    return workflowMetadataList;
  }

  /// @notice Retrieves a list of workflow metadata for a specific DON ID.
  /// @dev This function allows paginated retrieval of workflows associated with a particular DON. If the `limit` is
  /// set to 0 or exceeds the `MAX_PAGINATION_LIMIT`, the `MAX_PAGINATION_LIMIT` will be used instead in both cases.
  /// @param donID The unique identifier of the DON whose associated workflows are being retrieved.
  /// @param start The index at which to start retrieving workflows (zero-based index). If the start index is greater
  /// than or equal to the total number of workflows, an empty array is returned.
  /// @param limit The maximum number of workflow metadata entries to retrieve. If the limit exceeds the available
  /// number of workflows from the start index, only the available entries are returned.
  /// @return workflowMetadataList An array of `WorkflowMetadata` structs containing metadata of workflows associated
  /// with the specified DON ID.
  function getWorkflowMetadataListByDON(
    uint32 donID,
    uint256 start,
    uint256 limit
  ) external view returns (WorkflowMetadata[] memory workflowMetadataList) {
    uint256 totalWorkflows = s_donWorkflowKeys[donID].length();
    if (start >= totalWorkflows) {
      return new WorkflowMetadata[](0);
    }

    if (limit > MAX_PAGINATION_LIMIT || limit == 0) {
      limit = MAX_PAGINATION_LIMIT;
    }

    uint256 end = (start + limit > totalWorkflows) ? totalWorkflows : start + limit;

    uint256 resultLength = end - start;
    workflowMetadataList = new WorkflowMetadata[](resultLength);

    for (uint256 i = 0; i < resultLength; ++i) {
      bytes32 workflowKey = s_donWorkflowKeys[donID].at(start + i);
      workflowMetadataList[i] = s_workflows[workflowKey];
    }

    return workflowMetadataList;
  }

  /// @notice Fetch all allowed DON IDs
  /// @return allowedDONs List of all allowed DON IDs
  function getAllAllowedDONs() external view returns (uint32[] memory allowedDONs) {
    uint256 len = s_allowedDONs.length();
    allowedDONs = new uint32[](len);
    for (uint256 i = 0; i < len; ++i) {
      allowedDONs[i] = uint32(s_allowedDONs.at(i));
    }

    return allowedDONs;
  }

  /// @notice Fetch all authorized addresses
  /// @return authorizedAddresses List of all authorized addresses
  function getAllAuthorizedAddresses() external view returns (address[] memory authorizedAddresses) {
    uint256 len = s_authorizedAddresses.length();
    authorizedAddresses = new address[](len);
    for (uint256 i = 0; i < len; ++i) {
      authorizedAddresses[i] = s_authorizedAddresses.at(i);
    }

    return authorizedAddresses;
  }

  /// @notice Returns whether the registry is currently locked
  /// @return True if the registry is locked, false otherwise
  function isRegistryLocked() external view returns (bool) {
    return s_registryLocked;
  }

  // ================================================================
  // |                          Validation                          |
  // ================================================================

  /// @dev Internal function to validate the urls for a workflow.
  function _validateWorkflowURLs(
    uint256 binaryURLLength,
    uint256 configURLLength,
    uint256 secretsURLLength
  ) internal pure {
    if (binaryURLLength == 0) {
      revert BinaryURLRequired();
    }

    if (binaryURLLength > MAX_URL_LENGTH) {
      revert URLTooLong(binaryURLLength, MAX_URL_LENGTH);
    }

    if (configURLLength > MAX_URL_LENGTH) {
      revert URLTooLong(configURLLength, MAX_URL_LENGTH);
    }

    if (secretsURLLength > MAX_URL_LENGTH) {
      revert URLTooLong(secretsURLLength, MAX_URL_LENGTH);
    }
  }

  /// @dev Internal function to validate the length of a workflow name.
  /// @param workflowNameLength The workflow name to validate.
  /// @custom:throws WorkflowNameTooLong if the workflow name exceeds MAX_WORKFLOW_NAME_LENGTH (64 characters).
  function _validateWorkflowName(
    uint256 workflowNameLength
  ) internal pure {
    if (workflowNameLength == 0) {
      revert WorkflowNameRequired();
    }

    if (workflowNameLength > MAX_WORKFLOW_NAME_LENGTH) {
      revert WorkflowNameTooLong(workflowNameLength, MAX_WORKFLOW_NAME_LENGTH);
    }
  }

  /// @notice Validates access permissions for a given DON and caller.
  /// @dev Reverts with DONNotAllowed if the DON is not allowed or AddressNotAuthorized if the caller is not authorized.
  /// @param donID The ID of the DON to check.
  /// @param caller The address attempting to access the DON
  function _validatePermissions(uint32 donID, address caller) internal view {
    if (!s_allowedDONs.contains(donID)) {
      // First, ensure the DON is in the allowed list. This is separate from the permission check below because a DON
      // can be removed from the allowed list without removing the permissioned addresses associated with the DON.
      revert DONNotAllowed(donID);
    }

    // Then, ensure the specific address is also authorized.
    if (!s_authorizedAddresses.contains(caller)) revert AddressNotAuthorized(caller);
  }

  /// @notice Generates a unique `workflowKey` by combining the owner's address with a specific field.
  /// This is essential for managing workflows within the registry. The following functions use this as an input:
  /// - updateRegistry
  /// - pauseWorkflow
  /// - activateWorkflow
  /// - deleteWorkflow
  /// If you do not have the `workflowKey` for these functions, you can compute it using this function
  /// with the owner's address and the workflow name.
  /// @dev This function ensures uniqueness for operations like workflow management or secrets
  /// handling by hashing the owner's address together with a distinguishing field such as
  /// the workflow name or secrets URL.
  /// @param owner The address of the owner. Typically used to uniquely associate the field with the owner.
  /// @param field A string field, such as the workflow name or secrets URL, that is used to generate the unique hash.
  /// @return A unique `bytes32` hash computed from the combination of the owner's address and the given field.
  function computeHashKey(address owner, string calldata field) public pure returns (bytes32) {
    return keccak256(abi.encodePacked(owner, field));
  }
}
ITypeAndVersion.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITypeAndVersion {
  function typeAndVersion() external pure returns (string memory);
}
Ownable2StepMsgSender.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {Ownable2Step} from "./Ownable2Step.sol";

/// @notice Sets the msg.sender to be the owner of the contract and does not set a pending owner.
contract Ownable2StepMsgSender is Ownable2Step {
  constructor() Ownable2Step(msg.sender, address(0)) {}
}
Strings.sol 94 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}
EnumerableSet.sol 378 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
Ownable2Step.sol 84 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IOwnable} from "../interfaces/IOwnable.sol";

/// @notice A minimal contract that implements 2-step ownership transfer and nothing more. It's made to be minimal
/// to reduce the impact of the bytecode size on any contract that inherits from it.
contract Ownable2Step is IOwnable {
  /// @notice The pending owner is the address to which ownership may be transferred.
  address private s_pendingOwner;
  /// @notice The owner is the current owner of the contract.
  /// @dev The owner is the second storage variable so any implementing contract could pack other state with it
  /// instead of the much less used s_pendingOwner.
  address private s_owner;

  error OwnerCannotBeZero();
  error MustBeProposedOwner();
  error CannotTransferToSelf();
  error OnlyCallableByOwner();

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    if (newOwner == address(0)) {
      revert OwnerCannotBeZero();
    }

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /// @notice Get the current owner
  function owner() public view override returns (address) {
    return s_owner;
  }

  /// @notice Allows an owner to begin transferring ownership to a new address. The new owner needs to call
  /// `acceptOwnership` to accept the transfer before any permissions are changed.
  /// @param to The address to which ownership will be transferred.
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /// @notice validate, transfer ownership, and emit relevant events
  /// @param to The address to which ownership will be transferred.
  function _transferOwnership(address to) private {
    if (to == msg.sender) {
      revert CannotTransferToSelf();
    }

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /// @notice Allows an ownership transfer to be completed by the recipient.
  function acceptOwnership() external override {
    if (msg.sender != s_pendingOwner) {
      revert MustBeProposedOwner();
    }

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /// @notice validate access
  function _validateOwnership() internal view {
    if (msg.sender != s_owner) {
      revert OnlyCallableByOwner();
    }
  }

  /// @notice Reverts if called by anyone other than the contract owner.
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}
Math.sol 415 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}
IOwnable.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IOwnable {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

Read Contract

computeHashKey 0x9f4cb534 → bytes32
getAllAllowedDONs 0x7497066b → uint32[]
getAllAuthorizedAddresses 0xf794bdeb → address[]
getWorkflowMetadata 0xdb800092 → tuple
getWorkflowMetadataListByDON 0x08e7f63a → tuple[]
getWorkflowMetadataListByOwner 0xb87a0194 → tuple[]
isRegistryLocked 0xf99ecb6b → bool
owner 0x8da5cb5b → address
typeAndVersion 0x181f5a77 → string

Write Contract 12 functions

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

acceptOwnership 0x79ba5097
No parameters
activateWorkflow 0x6f351771
bytes32 workflowKey
deleteWorkflow 0x695e1340
bytes32 workflowKey
lockRegistry 0x2b596f6d
No parameters
pauseWorkflow 0xe690f332
bytes32 workflowKey
registerWorkflow 0x3ccd14ff
string workflowName
bytes32 workflowID
uint32 donID
uint8 status
string binaryURL
string configURL
string secretsURL
requestForceUpdateSecrets 0x2303348a
string secretsURL
transferOwnership 0xf2fde38b
address to
unlockRegistry 0x7ec0846d
No parameters
updateAllowedDONs 0x724c13dd
uint32[] donIDs
bool allowed
updateAuthorizedAddresses 0xe3dce080
address[] addresses
bool allowed
updateWorkflow 0xd4b89c74
bytes32 workflowKey
bytes32 newWorkflowID
string binaryURL
string configURL
string secretsURL

Recent Transactions

No transactions found for this address