Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x02219F8B9BB7B9853AA110D687EE82e9835A13fB
Balance 0 ETH
Nonce 1
Code Size 13538 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

13538 bytes
0x608060405234801561001057600080fd5b50600436106103d05760003560e01c80636a42b8f8116101ff578063c08c66ed1161011a578063d54ad002116100ad578063eaf968f11161007c578063eaf968f114610b00578063f1e50ccb14610b13578063f821e19e14610b3a578063fe436e2614610b4d57600080fd5b8063d54ad00214610a54578063df478e5f14610a5c578063df9042eb14610a6f578063e58378bb14610ad957600080fd5b8063c6b54b3c116100e9578063c6b54b3c146109e0578063c6d69aaa14610a07578063d520e52514610a2e578063d547741f14610a4157600080fd5b8063c08c66ed1461095f578063c0d7a3e114610986578063c2643ce5146109ad578063c3f8b3a4146109cd57600080fd5b806391d1485411610192578063a73adb2211610161578063a73adb22146108d9578063a8b8c97c1461090f578063ac9650d814610918578063b171b6561461093857600080fd5b806391d14854146108605780639edaec3314610897578063a217fddf146108aa578063a3be41b5146108b257600080fd5b80637a0e5270116101ce5780637a0e5270146108075780638312ebd11461082e5780638a3c24d5146108375780638e24581f1461085757600080fd5b80636a42b8f8146107b15780636a7dd020146107ba57806371f1b90c146107e15780637448440d146107f457600080fd5b806337662136116102ef5780634de4125e1161028257806364ed75fe1161025157806364ed75fe1461073057806365a7a33414610743578063667537ae1461076a5780636732cfba1461078a57600080fd5b80634de4125e146106d05780634ea374d9146106e35780635037ec621461070a578063529985a41461071d57600080fd5b80634125ff90116102be5780634125ff901461066157806346817e4e1461066b5780634774565b146106965780634806f080146106a957600080fd5b806337662136146105e85780633ab9198a146106075780633f290c8e1461061a5780633ffdc9bb1461062d57600080fd5b80631e5eb1d0116103675780632f2ff15d116103365780632f2ff15d1461057257806331ba7b011461058557806332165013146105ae57806336568abe146105d557600080fd5b80631e5eb1d0146104cc578063222f8b3814610515578063248a9ca3146105285780632c82595f1461054b57600080fd5b8063154c5d45116103a3578063154c5d451461044f578063156522a8146104765780631977ddee146104995780631cb64705146104c457600080fd5b806301ffc9a7146103d557806307929c52146103fd5780630b09c729146104325780630e5f2e9014610447575b600080fd5b6103e86103e3366004612dcc565b610b74565b60405190151581526020015b60405180910390f35b6104247fa23dcad5e1437109ba80d5d43ce61b76c4ba71b990bb2fb30e4a25825b5722ac81565b6040519081526020016103f4565b610445610440366004612dfe565b610bdd565b005b610445610e9c565b6104247f72e2e10353a44bac613fb0efb863d5efcf21233daceb078cd60b9b439c34bc6b81565b6103e8610484366004612e2a565b60076020526000908152604090205460ff1681565b6104ac6104a7366004612dcc565b610f9e565b6040516001600160a01b0390911681526020016103f4565b61042461102a565b600a54600b54600c54600d546104eb936001600160a01b031692919084565b604080516001600160a01b03909516855260208501939093529183015260608201526080016103f4565b610445610523366004612e8e565b611039565b610424610536366004612ee2565b60009081526020819052604090206001015490565b6104ac7f00000000000000000000000099a055251170411c4505519aaec57020b6129bb881565b610445610580366004612dfe565b611231565b6104ac610593366004612dcc565b6012602052600090815260409020546001600160a01b031681565b6104247fbc39e22ae17949978850c1ebfcfc5ce3aec62d7305fe0a77729f8f273e1b0ed981565b6104456105e3366004612dfe565b6114fb565b600e54600f546010546011546104eb936001600160a01b031692919084565b610445610615366004612efb565b61154c565b610445610628366004612efb565b61166f565b6104ac61063b366004612f25565b60066020908152600092835260408084209091529082529020546001600160a01b031681565b6104246206978081565b610424610679366004612f48565b600560209081526000928352604080842090915290825290205481565b6104456106a4366004612e8e565b6117de565b6104247f4bf52dc2c954981278ff6c74fd7abc8016e411aaaea43c0c70c3d11bba3c7de981565b6104456106de366004612f64565b61191e565b6104247fe2c1d5a87694d73df1ee00dc4a0f486d83d77c51d700fea699a4db3d4f3c3c5b81565b610445610718366004612ee2565b611aa7565b61044561072b366004612dfe565b611c13565b61044561073e366004612ee2565b611e43565b6104247f826af3bf5125b9a75b53766bdf167be999465a06ba11477e11639a49143bd73481565b610424610778366004612ee2565b60096020526000908152604090205481565b6104247f3f5fba3a1436af8b45aefdf26f89f5807959d0b316fbafd40b66ca527d0e49b381565b61042460015481565b6104247f9d48cb35069276177127c4c724db2665903a5bf9bfefebf62e7ce30dded7330481565b6104246107ef366004612dfe565b611e93565b6104ac610802366004612f97565b611eef565b6104247fb15e3077e7f51205b0d5d0054126c25ef95262bd0c35a00a0dc8a4c9a6553b4c81565b6104246103e881565b610424610845366004612ee2565b60086020526000908152604090205481565b61042460025481565b6103e861086e366004612dfe565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6104456108a5366004612dcc565b611f5f565b610424600081565b6104247f1928ca9df04ffa6228863ec8e94d0b8aca7ef13127ad89b9eef5b48f9035601981565b6104246108e7366004612e2a565b3360009081526005602090815260408083206001600160a01b03949094168352929052205490565b61042460035481565b61092b610926366004612fe2565b611ff8565b6040516103f491906130a9565b6104247f22275e60a6684a0d69c566c1f1659cb8bb5f893b39e0e971d6289561c8663fd081565b6104247f5c3c5b2faf1e3af313e082480f79d9f0248207ea9aa02755e0cae60568f86bd781565b6104247f31ca2e68417c4c75d5434e529d2ad5f6fc83e9bbdb0f840ed95c2525b4b53cb581565b6104246109bb366004612ee2565b60046020526000908152604090205481565b6104456109db366004612e8e565b6120e0565b6104247f0f6ee822d2ee125e4ce6edbae6c10a76fa9fd4617e0399ab687226fa3344210081565b6104247fadee834f4f5d01f5a91928686d239e53c5fe9aacf08de75b080e57b11d82e89c81565b610445610a3c366004612e8e565b61223b565b610445610a4f366004612dfe565b612470565b610445612580565b610445610a6a366004612efb565b6126fd565b610424610a7d366004612efb565b6040516001600160e01b0319831660208201526001600160601b0319606083901b166024820152600090819060380160408051808303601f19018152918152815160209283012060009081526009909252902054949350505050565b6104247fb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e81565b610445610b0e366004612e2a565b6128e9565b6104247fb090ffe716a3976b8bd0307087b831c863608a7b0889217af1a3bd6c18b8f3de81565b610424610b48366004612e8e565b61291b565b6104247f124d70cd22f9afb5a50d2dae097ffad37d6e6cb6487d74061ef061ceaa0fdd7081565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610bd757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b60008281526020818152604080832060010154835280832033808552925290912054839060ff16610c50576040517fee780d820000000000000000000000000000000000000000000000000000000081526001600160a01b03909216600483015260248201526044015b60405180910390fd5b50506001600160a01b038116610cdf576040805163674604c960e11b81526004810191909152601460448201527f416363657373436f6e74726f6c44656c61796564000000000000000000000000606482015260806024820152600b60848201527f70726f706f7365526f6c6500000000000000000000000000000000000000000060a482015260c401610c47565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610d3a576040517f30aa62f800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008282604051602001610d6592919091825260601b6001600160601b031916602082015260340190565b60408051601f1981840301815291815281516020928301206000818152600490935291205490915015610dc4576040517f7428c57300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dcc6129a6565b60046000838152602001908152602001600020819055507f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a60688585604051602001610e399291909182526001600160a01b0316602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401610e6592919061310e565b600060405180830381600087803b158015610e7f57600080fd5b505af1158015610e93573d6000803e3d6000fd5b50505050505050565b7f826af3bf5125b9a75b53766bdf167be999465a06ba11477e11639a49143bd734610ec6816129fe565b600e54600f54601054604080516001600160a01b03948516602082015280820193909352606080840192909252805180840390920182526080830190819052636f51101d60e11b90527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb89092169163dea2203a91610f4a9161012f9160840161310e565b600060405180830381600087803b158015610f6457600080fd5b505af1158015610f78573d6000803e3d6000fd5b5050600e80546001600160a01b031916905550506000600f819055601081905560115550565b6001600160e01b031981166000908152601260205260408120546001600160a01b03168281611023576040805163614699fd60e01b81526004810191909152600660448201527f616374696f6e000000000000000000000000000000000000000000000000000060648201526001600160e01b03199091166024820152608401610c47565b5092915050565b60006110346129a6565b905090565b7fe2c1d5a87694d73df1ee00dc4a0f486d83d77c51d700fea699a4db3d4f3c3c5b611063816129fe565b6001600160a01b0382161580159061107a57508215155b6110975760405163674604c960e11b8152600401610c479061312f565b60408051606084901b6001600160601b031916602080830191909152825160148184030181526034909201909252805191012060006110d68686612a0b565b60008181526006602090815260408083206001600160e01b0319871684529091529020549091506001600160a01b03161561112457604051632250dcf360e01b815260040160405180910390fd5b60008686848760405160200161113d9493929190613195565b60405160208183030381529060405280519060200120905061115d6129a6565b60086000838152602001908152602001600020819055507f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a606684886040516020016111ca9291909182526001600160a01b0316602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016111f692919061310e565b600060405180830381600087803b15801561121057600080fd5b505af1158015611224573d6000803e3d6000fd5b5050505050505050505050565b3360009081527fd329ff8a035c3ce5df2b0dae604d660c0d8783bf7e64be00c1d10db96c0b87b4602052604090205460ff161561137f576112728282612a3f565b7f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a60cc84846040516020016112c89291909182526001600160a01b0316602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016112f492919061310e565b600060405180830381600087803b15801561130e57600080fd5b505af1158015611322573d6000803e3d6000fd5b5050505060046000838360405160200161135392919091825260601b6001600160601b031916602082015260340190565b604051602081830303815290604052805190602001208152602001908152602001600020600090555050565b7fb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e82036113d8576040517fb11477c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828260405160200161140392919091825260601b6001600160601b031916602082015260340190565b604051602081830303815290604052805190602001209050600460008281526020019081526020016000205460000361144f5760405163d15fc6c360e01b815260040160405180910390fd5b60008181526004602052604090205442908082101561148a57604051632fc0420360e21b815260048101929092526024820152604401610c47565b50506000818152600460205260408120556114a58383612a3f565b7f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a60cc8585604051602001610e399291909182526001600160a01b0316602082015260400190565b6001600160a01b038116331461153d576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115478282612a64565b505050565b7f22275e60a6684a0d69c566c1f1659cb8bb5f893b39e0e971d6289561c8663fd0611576816129fe565b604080516001600160e01b031985166020808301829052606086901b6001600160601b031916602484015283516018818503018152603884018552805190820120600081815260099092529381205560588201526001600160a01b0380851660788301527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb8169063dea2203a9061012d906098015b6040516020818303038152906040526040518363ffffffff1660e01b815260040161163792919061310e565b600060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b5050505050505050565b7fb15e3077e7f51205b0d5d0054126c25ef95262bd0c35a00a0dc8a4c9a6553b4c611699816129fe565b6001600160e01b031983166000908152601260205260409020546001600160a01b0316156116da57604051632250dcf360e01b815260040160405180910390fd5b6001600160a01b038216158015906116fb57506001600160e01b0319831615155b6117185760405163674604c960e11b8152600401610c47906131ca565b6040516001600160e01b0319841660208201526001600160601b0319606084901b1660248201526000906038016040516020818303038152906040528051906020012090506117656129a6565b60096000838152602001908152602001600020819055507f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a6065868660405160200161160b9291906001600160e01b03199290921682526001600160a01b0316602082015260400190565b7fb090ffe716a3976b8bd0307087b831c863608a7b0889217af1a3bd6c18b8f3de611808816129fe565b60408051606084901b6001600160601b031916602080830191909152825160148184030181526034909201909252805191012060006118478686612a0b565b90506000868684876040516020016118629493929190613195565b60405160208183030381529060405280519060200120905060086000828152602001908152602001600020546000036118ae5760405163d15fc6c360e01b815260040160405180910390fd5b600060086000838152602001908152602001600020819055507f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a61012e84886040516020016111ca9291909182526001600160a01b0316602082015260400190565b7f3f5fba3a1436af8b45aefdf26f89f5807959d0b316fbafd40b66ca527d0e49b3611948816129fe565b6001600160a01b03841661196f5760405163674604c960e11b8152600401610c4790613230565b8160006103e8808311156119a7576040516316c0ea7160e01b8152600481019390935260248301919091526044820152606401610c47565b50505081831115838390916119f1576040517f5463361700000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401610c47565b50506040518060800160405280856001600160a01b03168152602001848152602001838152602001611a216129a6565b90528051600e80546001600160a01b0319166001600160a01b03928316179055602080830151600f5560408084015160105560609384015160115580518884169281019290925281018690529182018490527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb8169063dea2203a9060679060800161160b565b7fb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e611ad1816129fe565b60015482141580611ae25750600254155b8015611af15750620697808211155b611b27576040517f5d96c4f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600354421015611b3c57600060038190556002555b7f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a61019060015485604051602001611b8c929190918252602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611bb892919061310e565b600060405180830381600087803b158015611bd257600080fd5b505af1158015611be6573d6000803e3d6000fd5b505050506001548210611bf95750600155565b600154611c0690426132ac565b60035560028290555b5050565b3360009081527f6c7a4e0787a740cd0c586784fe0992426832a7d0ca8d098716f3216c432f66c2602052604090205460ff1680611c7e57503360009081527fd329ff8a035c3ce5df2b0dae604d660c0d8783bf7e64be00c1d10db96c0b87b4602052604090205460ff165b3390611cc2576040517f1a56ce900000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610c47565b50600460008383604051602001611cf092919091825260601b6001600160601b031916602082015260340190565b60405160208183030381529060405280519060200120815260200190815260200160002054600003611d355760405163d15fc6c360e01b815260040160405180910390fd5b600460008383604051602001611d6292919091825260601b6001600160601b031916602082015260340190565b604051602081830303815290604052805190602001208152602001908152602001600020600090557f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a6101308484604051602001611de19291909182526001600160a01b0316602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611e0d92919061310e565b600060405180830381600087803b158015611e2757600080fd5b505af1158015611e3b573d6000803e3d6000fd5b505050505050565b600b548110801590611e575750600c548111155b600b54600c548392611e8d576040516316c0ea7160e01b8152600481019390935260248301919091526044820152606401610c47565b50505050565b6000600460008484604051602001611ec292919091825260601b6001600160601b031916602082015260340190565b60405160208183030381529060405280519060200120815260200190815260200160002054905092915050565b600080611efc8585612a0b565b60008181526006602090815260408083206001600160e01b0319881684529091529020549091506001600160a01b031685858583611f505760405163614699fd60e01b8152600401610c47939291906132e8565b509193505050505b9392505050565b7f5c3c5b2faf1e3af313e082480f79d9f0248207ea9aa02755e0cae60568f86bd7611f89816129fe565b6001600160e01b0319821660008181526012602090815260409182902080546001600160a01b03191690558151908101929092526001600160a01b037f00000000000000000000000099a055251170411c4505519aaec57020b6129bb8169163dea2203a916101919101611de1565b6040805160008152602081019091526060908267ffffffffffffffff81111561202357612023613316565b60405190808252806020026020018201604052801561205657816020015b60608152602001906001900390816120415790505b50915060005b838110156120d8576120b33086868481811061207a5761207a61332c565b905060200281019061208c9190613342565b8560405160200161209f93929190613389565b604051602081830303815290604052612aef565b8382815181106120c5576120c561332c565b602090810291909101015260010161205c565b505092915050565b7f9d48cb35069276177127c4c724db2665903a5bf9bfefebf62e7ce30dded7330461210a816129fe565b60408051606084901b6001600160601b031916602080830191909152825160148184030181526034909201909252805191012060006121498686612a0b565b60008181526006602090815260408083206001600160e01b03198716845282529182902080546001600160a01b031916905590519192507f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b03169163dea2203a91610192916121d59186918a91019182526001600160a01b0316602082015260400190565b6040516020818303038152906040526040518363ffffffff1660e01b815260040161220192919061310e565b600060405180830381600087803b15801561221b57600080fd5b505af115801561222f573d6000803e3d6000fd5b50505050505050505050565b7f72e2e10353a44bac613fb0efb863d5efcf21233daceb078cd60b9b439c34bc6b612265816129fe565b60408051606084901b6001600160601b031916602080830191909152825160148184030181526034909201909252805191012060006122a48686612a0b565b60008181526006602090815260408083206001600160e01b0319871684529091529020549091506001600160a01b0316156122f257604051632250dcf360e01b815260040160405180910390fd5b841580159061230957506001600160a01b03841615155b6123265760405163674604c960e11b8152600401610c47906133b0565b60008686848760405160200161233f9493929190613195565b604051602081830303815290604052805190602001209050600860008281526020019081526020016000205460000361238b5760405163d15fc6c360e01b815260040160405180910390fd5b6000818152600860205260409020544290808210156123c657604051632fc0420360e21b815260048101929092526024820152604401610c47565b50506000818152600860209081526040808320839055848352600682528083206001600160e01b031987168452825280832080546001600160a01b0319166001600160a01b038a81169182179092558085526007845293829020805460ff191660011790558151928301869052908201929092527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb89091169063dea2203a9060ca906060016111ca565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166124ca576040517f153f153c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6124d48282612b65565b60046000838360405160200161250192919091825260601b6001600160601b031916602082015260340190565b604051602081830303815290604052805190602001208152602001908152602001600020600090557f00000000000000000000000099a055251170411c4505519aaec57020b6129bb86001600160a01b031663dea2203a6101948484604051602001611de19291909182526001600160a01b0316602082015260400190565b7f31ca2e68417c4c75d5434e529d2ad5f6fc83e9bbdb0f840ed95c2525b4b53cb56125aa816129fe565b6011546000036125cd5760405163d15fc6c360e01b815260040160405180910390fd5b6011544290808210156125fc57604051632fc0420360e21b815260048101929092526024820152604401610c47565b5050600e54600f54601054604080516001600160a01b03948516602082015280820193909352606080840192909252805180840390920182526080830190819052636f51101d60e11b90527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb89092169163dea2203a916126819160cb9160840161310e565b600060405180830381600087803b15801561269b57600080fd5b505af11580156126af573d6000803e3d6000fd5b505060006011819055600e8054600a80546001600160a01b03199081166001600160a01b03841617909155600f8054600b5560108054600c55600d8690559190921690925582905555505050565b7fadee834f4f5d01f5a91928686d239e53c5fe9aacf08de75b080e57b11d82e89c612727816129fe565b6001600160e01b031983166000908152601260205260409020546001600160a01b03161561276857604051632250dcf360e01b815260040160405180910390fd5b6001600160a01b0382161580159061278957506001600160e01b0319831615155b6127a65760405163674604c960e11b8152600401610c4790613416565b6040516001600160e01b0319841660208201526001600160601b0319606084901b166024820152600090603801604051602081830303815290604052805190602001209050600960008281526020019081526020016000205460000361281f5760405163d15fc6c360e01b815260040160405180910390fd5b60008181526009602052604090205442908082101561285a57604051632fc0420360e21b815260048101929092526024820152604401610c47565b505060008181526009602090815260408083208390556001600160e01b03198716808452601283529281902080546001600160a01b0319166001600160a01b03888116918217909255825193840194909452908201929092527f00000000000000000000000099a055251170411c4505519aaec57020b6129bb89091169063dea2203a9060c99060600161160b565b6128f281612b8a565b3360009081526005602090815260408083206001600160a01b0394909416835292905220429055565b60008061295c836040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b90506000858583866040516020016129779493929190613195565b60408051808303601f190181529181528151602092830120600090815260089092529020549695505050505050565b60006003544210156129dd576000600254426129c291906132ac565b905060035481116129d5576003546129d7565b805b91505090565b600254156129f15760028054600155600090555b60015461103490426132ac565b612a088133612bea565b50565b60008282604051602001612a2092919061347c565b60408051601f1981840301815291905280516020909101209392505050565b600082815260208190526040902060010154612a5a816129fe565b611e8d8383612c56565b6000828152602081815260408083206001600160a01b038516845290915281205460ff1615612ae7576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610bd7565b506000610bd7565b6060600080846001600160a01b031684604051612b0c9190613490565b600060405180830381855af49150503d8060008114612b47576040519150601f19603f3d011682016040523d82523d6000602084013e612b4c565b606091505b5091509150612b5c858383612cf8565b95945050505050565b600082815260208190526040902060010154612b80816129fe565b611e8d8383612a64565b6001600160a01b038116600090815260076020526040902054819060ff16611c0f576040517f5c026af90000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610c47565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611c0f576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101839052604401610c47565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16612ae7576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055612cb03390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610bd7565b606082612d0d57612d0882612d6d565b611f58565b8151158015612d2457506001600160a01b0384163b155b15612d66576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610c47565b5080611f58565b805115612d7d5780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80356001600160e01b031981168114612dc757600080fd5b919050565b600060208284031215612dde57600080fd5b611f5882612daf565b80356001600160a01b0381168114612dc757600080fd5b60008060408385031215612e1157600080fd5b82359150612e2160208401612de7565b90509250929050565b600060208284031215612e3c57600080fd5b611f5882612de7565b60008083601f840112612e5757600080fd5b50813567ffffffffffffffff811115612e6f57600080fd5b602083019150836020828501011115612e8757600080fd5b9250929050565b600080600060408486031215612ea357600080fd5b833567ffffffffffffffff811115612eba57600080fd5b612ec686828701612e45565b9094509250612ed9905060208501612de7565b90509250925092565b600060208284031215612ef457600080fd5b5035919050565b60008060408385031215612f0e57600080fd5b612f1783612daf565b9150612e2160208401612de7565b60008060408385031215612f3857600080fd5b82359150612e2160208401612daf565b60008060408385031215612f5b57600080fd5b612f1783612de7565b600080600060608486031215612f7957600080fd5b612f8284612de7565b95602085013595506040909401359392505050565b600080600060408486031215612fac57600080fd5b833567ffffffffffffffff811115612fc357600080fd5b612fcf86828701612e45565b9094509250612ed9905060208501612daf565b60008060208385031215612ff557600080fd5b823567ffffffffffffffff81111561300c57600080fd5b8301601f8101851361301d57600080fd5b803567ffffffffffffffff81111561303457600080fd5b8560208260051b840101111561304957600080fd5b6020919091019590945092505050565b60005b8381101561307457818101518382015260200161305c565b50506000910152565b60008151808452613095816020860160208601613059565b601f01601f19169290920160200192915050565b6000602082016020835280845180835260408501915060408160051b86010192506020860160005b8281101561310257603f198786030184526130ed85835161307d565b945060209384019391909101906001016130d1565b50929695505050505050565b828152604060208201526000613127604083018461307d565b949350505050565b60408152600061315960408301600a81526910591b5a5b95985d5b1d60b21b602082015260400190565b828103602093840152600b81527f70726f706f7365506f6f6c000000000000000000000000000000000000000000928101929092525060400190565b838582376001600160e01b0319929092169190920190815260609190911b6001600160601b0319166004820152601801919050565b6040815260006131f460408301600a81526910591b5a5b95985d5b1d60b21b602082015260400190565b828103602093840152600d81527f70726f706f7365416374696f6e00000000000000000000000000000000000000928101929092525060400190565b60408152600061325a60408301600a81526910591b5a5b95985d5b1d60b21b602082015260400190565b828103602093840152601081527f70726f706f7365466565436f6e66696700000000000000000000000000000000928101929092525060400190565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bd757610bd7613296565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006132fc6040830185876132bf565b90506001600160e01b031983166020830152949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261335957600080fd5b83018035915067ffffffffffffffff82111561337457600080fd5b602001915036819003821315612e8757600080fd5b8284823760008382016000815283516133a6818360208801613059565b0195945050505050565b6040815260006133da60408301600a81526910591b5a5b95985d5b1d60b21b602082015260400190565b828103602093840152600781527f616464506f6f6c00000000000000000000000000000000000000000000000000928101929092525060400190565b60408152600061344060408301600a81526910591b5a5b95985d5b1d60b21b602082015260400190565b828103602093840152600981527f616464416374696f6e0000000000000000000000000000000000000000000000928101929092525060400190565b6020815260006131276020830184866132bf565b600082516134a2818460208701613059565b919091019291505056fea2646970667358221220e900e1f97a4bc284f8b3be2deed684a6d56bb4dac2269308aa768f6f518bcca464736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: paris Optimization: Yes (1000 runs)
AccessControl.sol 209 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
Address.sol 150 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
Errors.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}
ERC165.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Multicall.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)

pragma solidity ^0.8.20;

import {Address} from "./Address.sol";
import {Context} from "./Context.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
 * careful about sending transactions invoking {multicall}. For example, a relay address that filters function
 * selectors won't filter calls nested within a {multicall} operation.
 *
 * NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
 * If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
 * to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
 * {_msgSender} are not propagated to subcalls.
 */
abstract contract Multicall is Context {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        bytes memory context = msg.sender == _msgSender()
            ? new bytes(0)
            : msg.data[msg.data.length - _contextSuffixLength():];

        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
        }
        return results;
    }
}
ActionBase.sol 157 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IAdminVault} from "../interfaces/IAdminVault.sol";
import {ILogger} from "../interfaces/ILogger.sol";
import {Errors} from "../Errors.sol";

/// @title ActionBase - Base contract for all actions in the protocol
/// @notice Implements common functionality and interfaces for all actions
/// @dev This contract should be inherited by all specific action contracts
/// @notice Found a vulnerability? Please contact [email protected] - we appreciate responsible disclosure and reward ethical hackers
abstract contract ActionBase {
    using SafeERC20 for IERC20;

    /// @notice Interface for the admin vault
    IAdminVault public immutable ADMIN_VAULT;

    /// @notice Interface for the logger
    ILogger public immutable LOGGER;

    /// @notice Basis points for fee calculations (100% = 10000)
    uint256 public constant FEE_BASIS_POINTS = 10000;

    /// @notice Duration of a fee period (1 year)
    uint256 public constant FEE_PERIOD = 365 days;

    /// @notice Enum representing different types of actions
    enum ActionType {
        DEPOSIT_ACTION,
        WITHDRAW_ACTION,
        SWAP_ACTION,
        COVER_ACTION,
        FEE_ACTION,
        TRANSFER_ACTION,
        CUSTOM_ACTION
    }

    /// @notice Enum representing different types of logs
    // List of log types, this list should be updated with each new log type added to the system.
    //   Existing values should not be changed/removed, as they may be already in use by a deployed action.
    //   UNUSED keeps the enum starting at index 1 for off-chain processing.
    enum LogType {
        UNUSED,
        BALANCE_UPDATE,
        BUY_COVER,
        CURVE_3POOL_SWAP,
        SEND_TOKEN,
        PULL_TOKEN,
        PARASWAP_SWAP,
        UPGRADE_ACTION
    }

    /// @notice Initializes the ActionBase contract
    /// @param _adminVault Address of the admin vault
    /// @param _logger Address of the logger contract
    constructor(address _adminVault, address _logger) {
        ADMIN_VAULT = IAdminVault(_adminVault);
        LOGGER = ILogger(_logger);
    }

    /// @notice Executes the implemented action
    /// @dev This function should be overridden by inheriting contracts
    /// @param _callData Encoded input data for the action
    /// @param _strategyId The ID of the strategy executing this action (for logging use only)
    function executeAction(bytes memory _callData, uint16 _strategyId) public payable virtual;

    /// @notice Returns the type of action being implemented
    /// @return uint8 The action type as defined in the ActionType enum
    function actionType() public pure virtual returns (uint8);

    /// @notice Processes the fee taking, figures out if it's a supply and we need to initialize the fee timestamp
    /// @param _pool Address of the pool
    /// @param _feePercentage Fee percentage in basis points
    /// @param _feeToken Address of the fee token
    /// @return feeInTokens The amount of fee taken
    /// @dev it's rare but in some cases the _pool does differ from the _feeToken
    function _processFee(
        address _pool,
        uint256 _feePercentage,
        address _feeToken
    ) internal returns (uint256 feeInTokens) {
        uint256 lastFeeTimestamp = ADMIN_VAULT.getLastFeeTimestamp(_pool);

        // Initialize timestamp if not set, this will be the users first interaction with the pool
        if (lastFeeTimestamp == 0) {
            ADMIN_VAULT.setFeeTimestamp(_pool);
            return 0;
        }

        uint256 currentTimestamp = block.timestamp;
        if (lastFeeTimestamp == currentTimestamp) {
            return 0; // Don't take fees twice in the same block
        }

        IERC20 vault = IERC20(_feeToken);
        uint256 balance = vault.balanceOf(address(this));
        uint256 fee = _calculateFee(balance, _feePercentage, lastFeeTimestamp, currentTimestamp);
        if (fee > 0) {
            vault.safeTransfer(ADMIN_VAULT.feeConfig().recipient, fee);
        }
        ADMIN_VAULT.setFeeTimestamp(_pool);
        return fee;
    }

    /// @notice Calculates the fee due from the vault
    /// @param _totalDeposit Total amount deposited in the vault
    /// @param _feePercentage Fee percentage in basis points
    /// @param _lastFeeTimestamp Timestamp of the last fee collection
    /// @param _currentTimestamp Current timestamp
    /// @return uint256 The calculated fee amount
    function _calculateFee(
        uint256 _totalDeposit,
        uint256 _feePercentage,
        uint256 _lastFeeTimestamp,
        uint256 _currentTimestamp
    ) internal pure returns (uint256) {
        uint256 secondsPassed = _currentTimestamp - _lastFeeTimestamp;
        uint256 annualFee = (_totalDeposit * _feePercentage) / FEE_BASIS_POINTS;
        uint256 feeForPeriod = (annualFee * secondsPassed) / FEE_PERIOD;
        return feeForPeriod;
    }

    /// @notice Generates a pool ID from an address
    /// @param _addr Address to generate the pool ID from
    /// @return bytes4 The generated pool ID
    function _poolIdFromAddress(address _addr) internal pure returns (bytes4) {
        return bytes4(keccak256(abi.encodePacked(_addr)));
    }

    /// @notice Encodes balance update information
    /// @param _strategyId ID of the strategy
    /// @param _poolId ID of the pool
    /// @param _balanceBefore Balance before the action
    /// @param _balanceAfter Balance after the action
    /// @param _feeInTokens Amount of fee taken in tokens
    /// @return bytes Encoded balance update information
    function _encodeBalanceUpdate(
        uint16 _strategyId,
        bytes4 _poolId,
        uint256 _balanceBefore,
        uint256 _balanceAfter,
        uint256 _feeInTokens
    ) internal pure returns (bytes memory) {
        return abi.encode(_strategyId, _poolId, _balanceBefore, _balanceAfter, _feeInTokens);
    }

    function _checkFeesTaken(address _token) internal view {
        uint256 feeTimestamp = ADMIN_VAULT.getLastFeeTimestamp(_token);
        require(feeTimestamp == 0 || feeTimestamp >= block.timestamp, Errors.Action_FeesNotPaid(protocolName(), actionType(), _token));
    }

    /// @notice Returns the name of the protocol
    /// @return string The name of the protocol
    function protocolName() public pure virtual returns (string memory);
}
AccessControlDelayed.sol 163 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Errors} from "../Errors.sol";
import {ILogger} from "../interfaces/ILogger.sol";
import {Roles} from "./Roles.sol";

/// @title Add delays to granting roles in access control
/// @dev We should intercept calls to grantRole and implement a proposal system
///      to allow for a delay to pass before the role is granted.
/// @notice Found a vulnerability? Please contact [email protected] - we appreciate responsible disclosure and reward ethical hackers
abstract contract AccessControlDelayed is AccessControl, Roles {
    /// @notice The maximum delay for a role proposal, to avoid costly mistakes
    uint256 public constant MAX_DELAY = 5 days;

    ILogger public immutable LOGGER;

    /// @notice The delay period for any proposals in the system
    uint256 public delay;
    /// @notice The new delay to be set after delayReductionLockTime
    uint256 public proposedDelay;
    /// @notice The time when the new delay can be set/used
    uint256 public delayReductionLockTime;

    /// @notice mapping of proposed roles to the timestamp they can be granted
    mapping(bytes32 => uint256) public proposedRoles;

    constructor(uint256 _delay, address _logger) {
        delay = _delay;
        LOGGER = ILogger(_logger);
    }

    /// @notice Proposes a role with a delay
    /// @dev Only accounts with the admin role for the role being proposed can make proposals
    function proposeRole(bytes32 role, address account) external {
        require(
            hasRole(getRoleAdmin(role), msg.sender),
            Errors.AccessControlDelayed_MustHaveAdminRole(msg.sender, role)
        );
        require(account != address(0), Errors.InvalidInput("AccessControlDelayed", "proposeRole"));
        require(!hasRole(role, account), Errors.AdminVault_AlreadyGranted());
        bytes32 proposalId = keccak256(abi.encodePacked(role, account));
        require(proposedRoles[proposalId] == 0, Errors.AdminVault_AlreadyProposed());

        proposedRoles[proposalId] = _getDelayTimestamp();
        LOGGER.logAdminVaultEvent(104, abi.encode(role, account));
    }

    /// @notice Grants a role after the delay has passed
    /// @dev OWNER_ROLE can grant any role immediately, others must follow proposal system
    function grantRole(bytes32 role, address account) public override(AccessControl) {
        if (hasRole(OWNER_ROLE, msg.sender)) {
            // OWNER_ROLE can grant any role immediately
            super.grantRole(role, account);
            LOGGER.logAdminVaultEvent(204, abi.encode(role, account));

            // If a proposal was made for this role, we should delete it
            delete proposedRoles[keccak256(abi.encodePacked(role, account))];
            return;
        }

        // For all other admins (ROLE_MANAGER_ROLE), must follow proposal system
        require(role != OWNER_ROLE, Errors.AccessControlDelayed_CannotGrantOwnerRole());
        bytes32 proposalId = keccak256(abi.encodePacked(role, account));
        require(proposedRoles[proposalId] != 0, Errors.AdminVault_NotProposed());
        require(
            block.timestamp >= proposedRoles[proposalId],
            Errors.AdminVault_DelayNotPassed(block.timestamp, proposedRoles[proposalId])
        );

        delete proposedRoles[proposalId];
        super.grantRole(role, account);
        LOGGER.logAdminVaultEvent(204, abi.encode(role, account));
    }

    /// @notice Cancels a role proposal
    /// @dev Only ROLE_MANAGER_ROLE or OWNER_ROLE can cancel proposals
    function cancelRoleProposal(bytes32 role, address account) external {
        require(
            hasRole(ROLE_MANAGER_ROLE, msg.sender) || hasRole(OWNER_ROLE, msg.sender),
            Errors.AccessControlDelayed_MustHaveRoleManagerOrOwner(msg.sender)
        );
        require(proposedRoles[keccak256(abi.encodePacked(role, account))] != 0, Errors.AdminVault_NotProposed());
        delete proposedRoles[keccak256(abi.encodePacked(role, account))];
        LOGGER.logAdminVaultEvent(304, abi.encode(role, account));
    }

    /// @notice Revokes a role from the given account
    /// @dev We must check that the account has the role, and also remove any proposals
    function revokeRole(bytes32 role, address account) public override(AccessControl) {
        require(hasRole(role, account), Errors.AdminVault_NotGranted());
        super.revokeRole(role, account);
        // no need to check if the proposal exists, we're just setting it to 0
        delete proposedRoles[keccak256(abi.encodePacked(role, account))];
        LOGGER.logAdminVaultEvent(404, abi.encode(role, account));
    }

    // A helper to find the time when a role proposal will be available to grant
    function getRoleProposalTime(bytes32 role, address account) public view returns (uint256) {
        return proposedRoles[keccak256(abi.encodePacked(role, account))];
    }

    /* Admin function to change the delay
        If the new delay is longer we just use it.
        If the new delay is shorter we must set a timestamp for when the old delay
        would have expired and we can use the new delay after that time.
        e.g. If the delay is 2 hours, and we reduce it to 1 hour. All new proposals
            must wait until at least now + 2 hours (old delay) but in 1 hour's time
            they may start using the new delay (because both the old and the new
            delays will have passed by the time they may be granted).
        Note: We don't simply add the shorter delay to the delayReductionLockTime
            because for legitimate use we may want to shorten the delay, say from
            2 days to 1 day, in this case we don't want to wait a total of 3 days.
        This means that the delay used by default should include enough time to:
        -- Notice the change
        -- Deal with the security hole (remove attackers permissions)
        -- Adjust the delay back to a suitable value
        -- Cancel any proposals made during this period
    */
    function changeDelay(uint256 _newDelay) public onlyRole(OWNER_ROLE) {
        // (_newDelay must be different from delay OR there must be a proposal to cancel)
        //  AND _newDelay must not more than 5 days (to avoid costly mistakes)
        require(
            (_newDelay != delay || proposedDelay == 0) && _newDelay <= MAX_DELAY,
            Errors.AccessControlDelayed_InvalidDelay()
        );

        if (block.timestamp < delayReductionLockTime) {
            // The delay must already have been reduced because delayReductionLockTime is in the future
            // We can't have set the delay to proposedDelay yet, so we can just delete it
            delete delayReductionLockTime;
            delete proposedDelay;
        }
        LOGGER.logAdminVaultEvent(400, abi.encode(delay, _newDelay));
        if (_newDelay >= delay) {
            // New delay is longer, just set it
            delay = _newDelay;
        } else {
            // New delay is shorter, enforce old delay until it is met
            delayReductionLockTime = block.timestamp + delay;
            proposedDelay = _newDelay;
        }
    }

    /// @notice Returns the timestamp to wait until,
    /// @dev Factors in the the delayReductionLockTime
    /// @dev If after the lock time we can set delay to the new value
    function _getDelayTimestamp() internal returns (uint256) {
        if (block.timestamp < delayReductionLockTime) {
            // We haven't reached the lock time yet,
            // We must wait until the greater of the lock time, or now + proposedDelay
            uint256 proposedDelayTime = block.timestamp + proposedDelay;
            return proposedDelayTime > delayReductionLockTime ? proposedDelayTime : delayReductionLockTime;
        }
        // We have reached the lock time, we may set the delay to the proposed delay
        if (proposedDelay != 0) {
            delay = proposedDelay;
            delete proposedDelay;
        }
        return block.timestamp + delay;
    }
}
AdminVault.sol 377 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
import {Errors} from "../Errors.sol";
import {AccessControlDelayed} from "./AccessControlDelayed.sol";

/// @title AdminVault
/// @notice A stateful contract that manages global variables and permissions for the protocol.
/// @notice Part of the Brava protocol.
/// @notice Found a vulnerability? Please contact [email protected] - we appreciate responsible disclosure and reward ethical hackers
/// @author BravaLabs.xyz
contract AdminVault is AccessControlDelayed, Multicall {
    /// @notice The maximum fee basis points.
    /// @dev 1000 = 10%
    uint256 public constant MAX_FEE_BASIS = 1000;

    /// @notice Timestamp tracking for fee collection: user => token => timestamp
    ///  Used to store the timestamp of the last fee collection for a given user and token combination.
    ///  Is zero if never deposited, or when balance reduced to zero.
    mapping(address => mapping(address => uint256)) public lastFeeTimestamp;

    /// @notice Protocol and pool management: protocol => poolId => poolAddress
    ///  Action contracts are only given the poolId, so this mapping limits them to pool addresses we've approved.
    mapping(uint256 => mapping(bytes4 => address)) public protocolPools;

    /// @notice Quick check for pool addresses, limits attack surface when writing timestamps to storage
    /// @dev Don't remove pools from this mapping, for non-unique pools this is the underlying asset address.
    /// @dev So removing them could break other protocols. (e.g. Aave V2 and V3 have the same 'pool' address for USDC)
    mapping(address => bool) public pool;

    /// @notice Proposal tracking: proposalId => timestamp
    mapping(bytes32 => uint256) public poolProposals;
    mapping(bytes32 => uint256) public actionProposals;

    /// @notice Fee configuration structure
    struct FeeConfig {
        address recipient;
        uint256 minBasis;
        uint256 maxBasis;
        uint256 proposalTime; // Used only for proposals, 0 for active config
    }
    /// @notice Current active fee configuration
    FeeConfig public feeConfig;
    /// @notice Pending fee configuration proposal
    FeeConfig public pendingFeeConfig;

    /// @notice Action management: actionId => actionAddress
    /// The sequence executor is only given the actionId, so this mapping limits it to action addresses we've approved.
    mapping(bytes4 => address) public actionAddresses;

    /// @notice Initializes the AdminVault with an initial owner, delay period and logger.
    /// @param _initialOwner The address to be granted all initial
    /// @param _delay The required delay period for proposals (in seconds).
    /// @param _logger The address of the Logger contract.
    constructor(address _initialOwner, uint256 _delay, address _logger) AccessControlDelayed(_delay, _logger) {
        require(_initialOwner != address(0) && _logger != address(0), Errors.InvalidInput("AdminVault", "constructor"));

        // Set initial fee configuration
        feeConfig = FeeConfig({recipient: _initialOwner, minBasis: 0, maxBasis: MAX_FEE_BASIS, proposalTime: 0});

        // Setup initial roles
        _grantRole(OWNER_ROLE, _initialOwner);
        _grantRole(ROLE_MANAGER_ROLE, _initialOwner);

        // Grant all operational roles to initial owner
        _grantRole(FEE_PROPOSER_ROLE, _initialOwner);
        _grantRole(FEE_CANCELER_ROLE, _initialOwner);
        _grantRole(FEE_EXECUTOR_ROLE, _initialOwner);
        _grantRole(POOL_PROPOSER_ROLE, _initialOwner);
        _grantRole(POOL_CANCELER_ROLE, _initialOwner);
        _grantRole(POOL_EXECUTOR_ROLE, _initialOwner);
        _grantRole(POOL_DISPOSER_ROLE, _initialOwner);
        _grantRole(ACTION_PROPOSER_ROLE, _initialOwner);
        _grantRole(ACTION_CANCELER_ROLE, _initialOwner);
        _grantRole(ACTION_EXECUTOR_ROLE, _initialOwner);
        _grantRole(ACTION_DISPOSER_ROLE, _initialOwner);
        _grantRole(FEE_TAKER_ROLE, _initialOwner);
        _grantRole(TRANSACTION_PROPOSER_ROLE, _initialOwner);
        _grantRole(TRANSACTION_CANCELER_ROLE, _initialOwner);
        _grantRole(TRANSACTION_EXECUTOR_ROLE, _initialOwner);
        _grantRole(TRANSACTION_DISPOSER_ROLE, _initialOwner);

        // Set role hierarchy
        _setRoleAdmin(OWNER_ROLE, OWNER_ROLE);
        _setRoleAdmin(ROLE_MANAGER_ROLE, OWNER_ROLE);
        _setRoleAdmin(FEE_TAKER_ROLE, ROLE_MANAGER_ROLE);

        // All operational roles managed by ROLE_MANAGER_ROLE
        _setRoleAdmin(FEE_PROPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(FEE_CANCELER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(FEE_EXECUTOR_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(POOL_PROPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(POOL_CANCELER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(POOL_EXECUTOR_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(POOL_DISPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(ACTION_PROPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(ACTION_CANCELER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(ACTION_EXECUTOR_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(ACTION_DISPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(TRANSACTION_PROPOSER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(TRANSACTION_CANCELER_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(TRANSACTION_EXECUTOR_ROLE, ROLE_MANAGER_ROLE);
        _setRoleAdmin(TRANSACTION_DISPOSER_ROLE, ROLE_MANAGER_ROLE);
    }

    /// Fee management
    ///  - Propose
    ///  - Cancel
    ///  - Set

    /// @notice Proposes a new fee configuration including recipient and fee range
    /// @param _recipient The address of the proposed fee recipient
    /// @param _min The minimum fee in basis points
    /// @param _max The maximum fee in basis points
    function proposeFeeConfig(address _recipient, uint256 _min, uint256 _max) external onlyRole(FEE_PROPOSER_ROLE) {
        require(_recipient != address(0), Errors.InvalidInput("AdminVault", "proposeFeeConfig"));
        require(_max <= MAX_FEE_BASIS, Errors.AdminVault_FeePercentageOutOfRange(_max, 0, MAX_FEE_BASIS));
        require(_min <= _max, Errors.AdminVault_InvalidFeeRange(_min, _max));

        pendingFeeConfig = FeeConfig({
            recipient: _recipient,
            minBasis: _min,
            maxBasis: _max,
            proposalTime: _getDelayTimestamp()
        });

        LOGGER.logAdminVaultEvent(103, abi.encode(_recipient, _min, _max));
    }

    /// @notice Cancels the pending fee configuration proposal
    function cancelFeeConfigProposal() external onlyRole(FEE_CANCELER_ROLE) {
        LOGGER.logAdminVaultEvent(
            303,
            abi.encode(pendingFeeConfig.recipient, pendingFeeConfig.minBasis, pendingFeeConfig.maxBasis)
        );
        delete pendingFeeConfig;
    }

    /// @notice Sets the pending fee configuration after the proposal delay has passed
    function setFeeConfig() external onlyRole(FEE_EXECUTOR_ROLE) {
        require(pendingFeeConfig.proposalTime != 0, Errors.AdminVault_NotProposed());
        require(
            block.timestamp >= pendingFeeConfig.proposalTime,
            Errors.AdminVault_DelayNotPassed(block.timestamp, pendingFeeConfig.proposalTime)
        );

        LOGGER.logAdminVaultEvent(
            203,
            abi.encode(pendingFeeConfig.recipient, pendingFeeConfig.minBasis, pendingFeeConfig.maxBasis)
        );

        // Update active config (note: proposalTime remains 0 for active config)
        pendingFeeConfig.proposalTime = 0;
        feeConfig = pendingFeeConfig;
        delete pendingFeeConfig;
    }

    /// Pool management
    ///  - Propose
    ///  - Cancel
    ///  - Add
    ///  - Remove

    /// @notice Proposes a new pool for a protocol.
    /// @param _protocolName The name of the protocol.
    /// @param _poolAddress The address of the pool.
    function proposePool(string calldata _protocolName, address _poolAddress) external onlyRole(POOL_PROPOSER_ROLE) {
        require(
            _poolAddress != address(0) && bytes(_protocolName).length != 0,
            Errors.InvalidInput("AdminVault", "proposePool")
        );
        bytes4 poolId = _poolIdFromAddress(_poolAddress);
        uint256 protocolId = _protocolIdFromName(_protocolName);
        require(protocolPools[protocolId][poolId] == address(0), Errors.AdminVault_AlreadyAdded());

        bytes32 proposalId = keccak256(abi.encodePacked(_protocolName, poolId, _poolAddress));
        poolProposals[proposalId] = _getDelayTimestamp();

        LOGGER.logAdminVaultEvent(102, abi.encode(protocolId, _poolAddress));
    }

    /// @notice Cancels a pool proposal.
    /// @param _protocolName The name of the protocol.
    /// @param _poolAddress The address of the proposed pool.
    function cancelPoolProposal(
        string calldata _protocolName,
        address _poolAddress
    ) external onlyRole(POOL_CANCELER_ROLE) {
        bytes4 poolId = _poolIdFromAddress(_poolAddress);
        uint256 protocolId = _protocolIdFromName(_protocolName);
        bytes32 proposalId = keccak256(abi.encodePacked(_protocolName, poolId, _poolAddress));
        require(poolProposals[proposalId] != 0, Errors.AdminVault_NotProposed());

        poolProposals[proposalId] = 0;

        LOGGER.logAdminVaultEvent(302, abi.encode(protocolId, _poolAddress));
    }

    /// @notice Adds a new pool after the proposal delay has passed.
    /// @param _protocolName The name of the protocol.
    /// @param _poolAddress The address of the pool to add.
    function addPool(string calldata _protocolName, address _poolAddress) external onlyRole(POOL_EXECUTOR_ROLE) {
        bytes4 poolId = _poolIdFromAddress(_poolAddress);
        uint256 protocolId = _protocolIdFromName(_protocolName);
        require(protocolPools[protocolId][poolId] == address(0), Errors.AdminVault_AlreadyAdded());
        require(
            bytes(_protocolName).length != 0 && _poolAddress != address(0),
            Errors.InvalidInput("AdminVault", "addPool")
        );

        bytes32 proposalId = keccak256(abi.encodePacked(_protocolName, poolId, _poolAddress));
        require(poolProposals[proposalId] != 0, Errors.AdminVault_NotProposed());
        require(
            block.timestamp >= poolProposals[proposalId],
            Errors.AdminVault_DelayNotPassed(block.timestamp, poolProposals[proposalId])
        );

        delete poolProposals[proposalId];
        protocolPools[protocolId][poolId] = _poolAddress;
        pool[_poolAddress] = true;
        LOGGER.logAdminVaultEvent(202, abi.encode(protocolId, _poolAddress));
    }

    function removePool(string calldata _protocolName, address _poolAddress) external onlyRole(POOL_DISPOSER_ROLE) {
        bytes4 poolId = _poolIdFromAddress(_poolAddress);
        uint256 protocolId = _protocolIdFromName(_protocolName);
        delete protocolPools[protocolId][poolId];
        LOGGER.logAdminVaultEvent(402, abi.encode(protocolId, _poolAddress));
    }

    /// Action management
    ///  - Propose
    ///  - Cancel
    ///  - Add
    ///  - Remove

    /// @notice Proposes a new action.
    /// @param _actionId The identifier of the action.
    /// @param _actionAddress The address of the action contract.
    function proposeAction(bytes4 _actionId, address _actionAddress) external onlyRole(ACTION_PROPOSER_ROLE) {
        require(actionAddresses[_actionId] == address(0), Errors.AdminVault_AlreadyAdded());
        require(
            _actionAddress != address(0) && _actionId != bytes4(0),
            Errors.InvalidInput("AdminVault", "proposeAction")
        );

        bytes32 proposalId = keccak256(abi.encodePacked(_actionId, _actionAddress));
        actionProposals[proposalId] = _getDelayTimestamp();
        LOGGER.logAdminVaultEvent(101, abi.encode(_actionId, _actionAddress));
    }

    /// @notice Cancels an action proposal.
    /// @param _actionId The identifier of the action.
    /// @param _actionAddress The address of the proposed action contract.
    function cancelActionProposal(bytes4 _actionId, address _actionAddress) external onlyRole(ACTION_CANCELER_ROLE) {
        bytes32 proposalId = keccak256(abi.encodePacked(_actionId, _actionAddress));
        actionProposals[proposalId] = 0;
        LOGGER.logAdminVaultEvent(301, abi.encode(_actionId, _actionAddress));
    }

    /// @notice Adds a new action after the proposal delay has passed.
    /// @param _actionId The identifier of the action.
    /// @param _actionAddress The address of the action contract to add.
    function addAction(bytes4 _actionId, address _actionAddress) external onlyRole(ACTION_EXECUTOR_ROLE) {
        require(actionAddresses[_actionId] == address(0), Errors.AdminVault_AlreadyAdded());
        require(_actionAddress != address(0) && _actionId != bytes4(0), Errors.InvalidInput("AdminVault", "addAction"));

        bytes32 proposalId = keccak256(abi.encodePacked(_actionId, _actionAddress));
        require(actionProposals[proposalId] != 0, Errors.AdminVault_NotProposed());
        require(
            block.timestamp >= actionProposals[proposalId],
            Errors.AdminVault_DelayNotPassed(block.timestamp, actionProposals[proposalId])
        );

        delete actionProposals[proposalId];
        actionAddresses[_actionId] = _actionAddress;
        LOGGER.logAdminVaultEvent(201, abi.encode(_actionId, _actionAddress));
    }

    function removeAction(bytes4 _actionId) external onlyRole(ACTION_DISPOSER_ROLE) {
        delete actionAddresses[_actionId];
        LOGGER.logAdminVaultEvent(401, abi.encode(_actionId));
    }

    /// @notice Sets the users fee timestamp for a pool to the current block timestamp.
    /// @param _pool The address of the pool token.
    function setFeeTimestamp(address _pool) external {
        _isPool(_pool);
        lastFeeTimestamp[msg.sender][_pool] = block.timestamp;
    }

    /// @notice Checks if a given address is a pool.
    /// @dev This should always be used when initializing or updating fee timestamps
    /// @dev Without this check an attacker could call one of those functions with a pool address of their choice
    /// @dev this would give them access to the storage slot of their choice. It's only a timestamp they could put there, but still not good.
    /// @param _pool The address to check.
    function _isPool(address _pool) internal view {
        require(pool[_pool], Errors.AdminVault_NotPool(_pool));
    }

    /// @notice Retrieves the address of a pool for a given protocol and pool ID.
    /// @param _protocolName The name of the protocol.
    /// @param _poolId The identifier of the pool.
    /// @return The address of the pool.
    function getPoolAddress(string calldata _protocolName, bytes4 _poolId) external view returns (address) {
        uint256 protocolId = _protocolIdFromName(_protocolName);
        address poolAddress = protocolPools[protocolId][_poolId];
        require(poolAddress != address(0), Errors.AdminVault_NotFound(_protocolName, _poolId));
        return poolAddress;
    }

    /// @notice Retrieves the address of an action for a given action ID.
    /// @param _actionId The identifier of the action.
    /// @return The address of the action contract.
    function getActionAddress(bytes4 _actionId) external view returns (address) {
        address actionAddress = actionAddresses[_actionId];
        require(actionAddress != address(0), Errors.AdminVault_NotFound("action", _actionId));
        return actionAddress;
    }

    /// @notice Retrieves the last fee timestamp for a given pool.
    /// @param _pool The address of the pool token.
    /// @return The last fee timestamp.
    function getLastFeeTimestamp(address _pool) external view returns (uint256) {
        return lastFeeTimestamp[msg.sender][_pool];
    }

    /// @notice Checks if a given fee basis is within the allowed range.
    /// @notice Used by action contracts to ensure they are taking fees within the allowed range.
    /// @param _feeBasis The fee basis to check.
    function checkFeeBasis(uint256 _feeBasis) external view {
        require(
            _feeBasis >= feeConfig.minBasis && _feeBasis <= feeConfig.maxBasis,
            Errors.AdminVault_FeePercentageOutOfRange(_feeBasis, feeConfig.minBasis, feeConfig.maxBasis)
        );
    }

    /// @notice Retrieves the proposal time for a given pool.
    /// @param _protocolName The name of the protocol.
    /// @param _poolAddress The address of the pool.
    /// @return The proposal timestamp.
    function getPoolProposalTime(string calldata _protocolName, address _poolAddress) external view returns (uint256) {
        bytes4 poolId = _poolIdFromAddress(_poolAddress);
        bytes32 proposalId = keccak256(abi.encodePacked(_protocolName, poolId, _poolAddress));
        return poolProposals[proposalId];
    }

    /// @notice Retrieves the proposal time for a given action.
    /// @param _actionId The identifier of the action.
    /// @param _actionAddress The address of the action contract.
    /// @return The proposal timestamp.
    function getActionProposalTime(bytes4 _actionId, address _actionAddress) external view returns (uint256) {
        bytes32 proposalId = keccak256(abi.encodePacked(_actionId, _actionAddress));
        return actionProposals[proposalId];
    }

    /// @notice Generates a pool ID from an address.
    /// @param _addr The address to generate the pool ID from.
    /// @return bytes4 The pool ID
    function _poolIdFromAddress(address _addr) internal pure returns (bytes4) {
        return bytes4(keccak256(abi.encodePacked(_addr)));
    }

    /// @notice Generates a protocolID from a protocol name
    /// @param _protocolName The name of the protocol
    /// @return uint256 The protocol ID
    function _protocolIdFromName(string calldata _protocolName) internal pure returns (uint256) {
        return uint256(keccak256(abi.encode(_protocolName)));
    }

    /// @notice Returns the delay period for proposals
    /// @return The timestamp to wait until
    function getDelayTimestamp() external returns (uint256) {
        return _getDelayTimestamp();
    }
}
Roles.sol 64 lines
// SPDX-License-Identifier: MIT

pragma solidity =0.8.28;

/// @notice Found a vulnerability? Please contact [email protected] - we appreciate responsible disclosure and reward ethical hackers
abstract contract Roles {
    // Role definitions
    // Access to the private keys associated with each address granted roles will be managed off-chain.
    //   And will vary depending on the privilege of the role and future security reviews.

    // OWNER_ROLE is the highest role in the hierarchy, reserved for emergencies and critical operations.
    //   It has unrestricted ability to grant and revoke any role, including other OWNER_ROLEs.
    //   This role should be extremely secure and rarely used, ideally only in emergency situations.
    //   It can bypass the delay system and has ultimate control over all roles.

    // ROLE_MANAGER_ROLE is the administrative role for day-to-day role management.
    //   It can propose, grant, and revoke any role except OWNER_ROLE.
    //   All operations by this role are subject to a time delay for security.
    //   This role is managed by OWNER_ROLE and should be secured appropriately.

    // Operational roles are divided by their domain:
    // FEE_ prefix is fee management roles (no disposer, as we always have a fee config)
    // POOL_ prefix is pool management roles
    // ACTION_ prefix is action management roles

    // Each domain follows a consistent pattern:
    // *_PROPOSER_ROLE may propose new configurations, this will be behind a multi-sig or similar strong security.
    //   only proposals that have passed an off-chain vetting process should be proposed.
    // *_EXECUTOR_ROLE may execute proposed configurations, this is likely less permissioned than the proposers.
    //   it should be reasonably easy for team members to execute changes once the proposal has passed.
    // *_CANCELER_ROLE may cancel proposals, this role is a defence mechanism and should be treated as relatively in-secure.
    //   it should be possible to cancel a proposal if there was a successful attack and/or the proposal is not
    //   going to be used. This role may be given to bots and/or team members on easy to access software wallets.
    // *_DISPOSER_ROLE may remove pools and actions, this shouldn't be frequently required.
    //   It's likely this role will be given to the same address(es) as the proposers.

    // All operational roles are managed by ROLE_MANAGER_ROLE through the delayed proposal system.
    // Lower privileged roles may be assigned to the same address as higher privileged roles.
    //   This means a PROPOSER may also be an EXECUTOR or CANCELER, so they may cancel or execute their own proposals.

    // Master role definitions
    bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
    bytes32 public constant ROLE_MANAGER_ROLE = keccak256("ROLE_MANAGER_ROLE");

    // Granular role definitions
    bytes32 public constant FEE_PROPOSER_ROLE = keccak256("FEE_PROPOSER_ROLE");
    bytes32 public constant FEE_CANCELER_ROLE = keccak256("FEE_CANCELER_ROLE");
    bytes32 public constant FEE_EXECUTOR_ROLE = keccak256("FEE_EXECUTOR_ROLE");
    bytes32 public constant POOL_PROPOSER_ROLE = keccak256("POOL_PROPOSER_ROLE");
    bytes32 public constant POOL_CANCELER_ROLE = keccak256("POOL_CANCELER_ROLE");
    bytes32 public constant POOL_EXECUTOR_ROLE = keccak256("POOL_EXECUTOR_ROLE");
    bytes32 public constant POOL_DISPOSER_ROLE = keccak256("POOL_DISPOSER_ROLE");
    bytes32 public constant ACTION_PROPOSER_ROLE = keccak256("ACTION_PROPOSER_ROLE");
    bytes32 public constant ACTION_CANCELER_ROLE = keccak256("ACTION_CANCELER_ROLE");
    bytes32 public constant ACTION_EXECUTOR_ROLE = keccak256("ACTION_EXECUTOR_ROLE");
    bytes32 public constant ACTION_DISPOSER_ROLE = keccak256("ACTION_DISPOSER_ROLE");
    bytes32 public constant TRANSACTION_PROPOSER_ROLE = keccak256("TRANSACTION_PROPOSER_ROLE");
    bytes32 public constant TRANSACTION_CANCELER_ROLE = keccak256("TRANSACTION_CANCELER_ROLE");
    bytes32 public constant TRANSACTION_EXECUTOR_ROLE = keccak256("TRANSACTION_EXECUTOR_ROLE");
    bytes32 public constant TRANSACTION_DISPOSER_ROLE = keccak256("TRANSACTION_DISPOSER_ROLE");

    // FEE_TAKER_ROLE is the role that can trigger the fee taking mechanism
    bytes32 public constant FEE_TAKER_ROLE = keccak256("FEE_TAKER_ROLE");
}
Errors.sol 74 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

/// @title Errors
/// @notice This contract contains all custom errors used across the protocol
/// @notice Found a vulnerability? Please contact [email protected] - we appreciate responsible disclosure and reward ethical hackers
contract Errors {
    // Generic errors
    error InvalidInput(string _contract, string _function);

    // AccessControlDelayed errors
    error AccessControlDelayed_InvalidDelay();
    error AccessControlDelayed_MustHaveAdminRole(address account, bytes32 role);
    error AccessControlDelayed_CannotGrantOwnerRole();
    error AccessControlDelayed_MustHaveRoleManagerOrOwner(address account);

    // AdminVault errors
    error AdminVault_FeePercentageOutOfRange(uint256 _providedPercentage, uint256 _minAllowed, uint256 _maxAllowed);
    error AdminVault_InvalidFeeRange(uint256 _minFee, uint256 _maxFee);
    error AdminVault_NotInitialized();
    error AdminVault_DelayNotPassed(uint256 _currentTime, uint256 _requiredTime);
    error AdminVault_NotFound(string _entityType, bytes4 _entityId);
    error AdminVault_NotProposed();
    error AdminVault_AlreadyProposed();
    error AdminVault_NotAdded();
    error AdminVault_AlreadyAdded();
    error AdminVault_NotPool(address _pool);
    error AdminVault_AlreadyGranted();
    error AdminVault_NotGranted();
    error AdminVault_TransactionNotProposed();
    error AdminVault_TransactionAlreadyApproved();
    error AdminVault_TransactionNotApproved(bytes32 txHash);
    error AdminVault_MissingRole(bytes32 role, address account);

    // FeeTakeSafeModule errors
    error FeeTakeSafeModule_SenderNotFeeTaker(address _sender);
    error FeeTakeSafeModule_InvalidActionType(bytes4 _actionId);
    error FeeTakeSafeModule_ExecutionFailed();

    // Generic Action errors
    error Action_ZeroAmount(string _protocolName, uint8 _actionType);
    error Action_InsufficientSharesReceived(
        string _protocolName,
        uint8 _actionType,
        uint256 _sharesReceived,
        uint256 _minSharesReceived
    );
    error Action_MaxSharesBurnedExceeded(
        string _protocolName,
        uint8 _actionType,
        uint256 _sharesBurned,
        uint256 _maxAllowed
    );
    error Action_InvalidPool(string _protocolName, uint8 _actionType);
    error Action_UnderlyingReceivedLessThanExpected(uint256 _underlyingReceived, uint256 _expected);
    error Action_FeesNotPaid(string _protocolName, uint8 _actionType, address _token);

    // CompoundV2Supply errors
    error Action_CompoundError(string _protocolName, uint8 _actionType, uint256 _errorCode);

    // Curve3PoolSwap errors
    error Curve3Pool__InvalidTokenIndices(int128 _fromToken, int128 _toToken);

    // ParaswapSwap errors
    error Paraswap__SwapFailed();
    error Paraswap__InsufficientOutput(uint256 _amountReceived, uint256 _minToAmount);

    // SendToken errors
    error Action_InvalidRecipient(string _protocolName, uint8 _actionType);

    // UpgradeAction errors
    error UpgradeAction_TransactionNotApproved(bytes32 txHash);
    error UpgradeAction_ExecutionFailed();
}
IAdminVault.sol 77 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

interface IAdminVault {
    // Errors
    error SenderNotAdmin();
    error SenderNotOwner();
    error FeeTimestampNotInitialized();
    error FeeTimestampAlreadyInitialized();
    error FeePercentageOutOfRange();
    error InvalidRange();
    error InvalidRecipient();
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
    error AccessControlBadConfirmation();

    // Structs
    struct FeeConfig {
        address recipient;
        uint256 minBasis;
        uint256 maxBasis;
        uint256 proposalTime;
    }

    // View Functions
    // solhint-disable-next-line func-name-mixedcase
    function LOGGER() external view returns (address);
    // solhint-disable-next-line func-name-mixedcase
    function OWNER_ROLE() external view returns (bytes32);
    // solhint-disable-next-line func-name-mixedcase
    function ADMIN_ROLE() external view returns (bytes32);
    function feeConfig() external view returns (FeeConfig memory);
    function pendingFeeConfig() external view returns (FeeConfig memory);
    function lastFeeTimestamp(address, address) external view returns (uint256);
    function protocolPools(uint256 protocolId, bytes4 poolId) external view returns (address);
    function actionAddresses(bytes4 actionId) external view returns (address);
    function getPoolAddress(string calldata _protocolName, bytes4 _poolId) external view returns (address);
    function getActionAddress(bytes4 _actionId) external view returns (address);
    function getLastFeeTimestamp(address _vault) external view returns (uint256);
    function checkFeeBasis(uint256 _feeBasis) external view;
    function getPoolProposalTime(string calldata protocolName, address poolAddress) external view returns (uint256);
    function getActionProposalTime(bytes4 actionId, address actionAddress) external view returns (uint256);

    // Role Management Functions
    function hasRole(bytes32 role, address account) external view returns (bool);
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
    function grantRole(bytes32 role, address account) external;
    function revokeRole(bytes32 role, address account) external;
    function renounceRole(bytes32 role, address callerConfirmation) external;

    // Fee Management Functions
    function proposeFeeConfig(address recipient, uint256 min, uint256 max) external;
    function cancelFeeConfigProposal() external;
    function setFeeConfig() external;
    function setFeeTimestamp(address _vault) external;

    // Pool Management Functions
    function proposePool(string calldata protocolName, address poolAddress) external;
    function cancelPoolProposal(string calldata protocolName, address poolAddress) external;
    function addPool(string calldata protocolName, address poolAddress) external;
    function removePool(string calldata protocolName, address poolAddress) external;

    // Action Management Functions
    function proposeAction(bytes4 actionId, address actionAddress) external;
    function cancelActionProposal(bytes4 actionId, address actionAddress) external;
    function addAction(bytes4 actionId, address actionAddress) external;
    function removeAction(bytes4 actionId) external;

    // Transaction Management Functions
    function proposeTransaction(bytes32 txHash) external;
    function cancelTransactionProposal(bytes32 txHash) external;
    function approveTransaction(bytes32 txHash) external;
    function revokeTransaction(bytes32 txHash) external;
    function isApprovedTransaction(bytes32 txHash) external view returns (bool);

    // Delay Management Functions
    function getDelayTimestamp() external returns (uint256);
}
ILogger.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import {ActionBase} from "../actions/ActionBase.sol";

interface ILogger {
    event ActionEvent(address caller, ActionBase.LogType logId, bytes data);
    event AdminVaultEvent(uint256 logId, bytes data);

    function logActionEvent(ActionBase.LogType _logType, bytes memory _data) external;
    function logAdminVaultEvent(uint256 _logId, bytes memory _data) external;
}

Read Contract

ACTION_CANCELER_ROLE 0xb171b656 → bytes32
ACTION_DISPOSER_ROLE 0xc08c66ed → bytes32
ACTION_EXECUTOR_ROLE 0xc6d69aaa → bytes32
ACTION_PROPOSER_ROLE 0x7a0e5270 → bytes32
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
FEE_CANCELER_ROLE 0x65a7a334 → bytes32
FEE_EXECUTOR_ROLE 0xc0d7a3e1 → bytes32
FEE_PROPOSER_ROLE 0x6732cfba → bytes32
FEE_TAKER_ROLE 0x07929c52 → bytes32
LOGGER 0x2c82595f → address
MAX_DELAY 0x4125ff90 → uint256
MAX_FEE_BASIS 0x8312ebd1 → uint256
OWNER_ROLE 0xe58378bb → bytes32
POOL_CANCELER_ROLE 0xf1e50ccb → bytes32
POOL_DISPOSER_ROLE 0x6a7dd020 → bytes32
POOL_EXECUTOR_ROLE 0x154c5d45 → bytes32
POOL_PROPOSER_ROLE 0x4ea374d9 → bytes32
ROLE_MANAGER_ROLE 0xc6b54b3c → bytes32
TRANSACTION_CANCELER_ROLE 0xfe436e26 → bytes32
TRANSACTION_DISPOSER_ROLE 0xa3be41b5 → bytes32
TRANSACTION_EXECUTOR_ROLE 0x32165013 → bytes32
TRANSACTION_PROPOSER_ROLE 0x4806f080 → bytes32
actionAddresses 0x31ba7b01 → address
actionProposals 0x667537ae → uint256
checkFeeBasis 0x64ed75fe
delay 0x6a42b8f8 → uint256
delayReductionLockTime 0xa8b8c97c → uint256
feeConfig 0x1e5eb1d0 → address, uint256, uint256, uint256
getActionAddress 0x1977ddee → address
getActionProposalTime 0xdf9042eb → uint256
getLastFeeTimestamp 0xa73adb22 → uint256
getPoolAddress 0x7448440d → address
getPoolProposalTime 0xf821e19e → uint256
getRoleAdmin 0x248a9ca3 → bytes32
getRoleProposalTime 0x71f1b90c → uint256
hasRole 0x91d14854 → bool
lastFeeTimestamp 0x46817e4e → uint256
pendingFeeConfig 0x37662136 → address, uint256, uint256, uint256
pool 0x156522a8 → bool
poolProposals 0x8a3c24d5 → uint256
proposedDelay 0x8e24581f → uint256
proposedRoles 0xc2643ce5 → uint256
protocolPools 0x3ffdc9bb → address
supportsInterface 0x01ffc9a7 → bool

Write Contract 20 functions

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

addAction 0xdf478e5f
bytes4 _actionId
address _actionAddress
addPool 0xd520e525
string _protocolName
address _poolAddress
cancelActionProposal 0x3ab9198a
bytes4 _actionId
address _actionAddress
cancelFeeConfigProposal 0x0e5f2e90
No parameters
cancelPoolProposal 0x4774565b
string _protocolName
address _poolAddress
cancelRoleProposal 0x529985a4
bytes32 role
address account
changeDelay 0x5037ec62
uint256 _newDelay
getDelayTimestamp 0x1cb64705
No parameters
returns: uint256
grantRole 0x2f2ff15d
bytes32 role
address account
multicall 0xac9650d8
bytes[] data
returns: bytes[]
proposeAction 0x3f290c8e
bytes4 _actionId
address _actionAddress
proposeFeeConfig 0x4de4125e
address _recipient
uint256 _min
uint256 _max
proposePool 0x222f8b38
string _protocolName
address _poolAddress
proposeRole 0x0b09c729
bytes32 role
address account
removeAction 0x9edaec33
bytes4 _actionId
removePool 0xc3f8b3a4
string _protocolName
address _poolAddress
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
revokeRole 0xd547741f
bytes32 role
address account
setFeeConfig 0xd54ad002
No parameters
setFeeTimestamp 0xeaf968f1
address _pool

Recent Transactions

No transactions found for this address