Address Contract Verified
Address
0x02219F8B9BB7B9853AA110D687EE82e9835A13fB
Balance
0 ETH
Nonce
1
Code Size
13538 bytes
Creator
0xba5Ed099...a5Ed at tx 0x46a11a3d...00c57d
Indexed Transactions
0
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