Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xd5a09A0827c3D91E8530dEEE3b73eDAE6b33ae95
Balance 0 ETH
Nonce 1
Code Size 23722 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23722 bytes
0x60806040526004361061016a5760003560e01c8063715018a6116100d1578063ba49f47d1161008a578063e1ca8f1511610064578063e1ca8f15146104a8578063f2fde38b146104c8578063f4b70990146104e8578063ffb43c301461050d57600080fd5b8063ba49f47d14610462578063c4d66de814610475578063d4ea31f01461049557600080fd5b8063715018a61461036f57806384b0196e146103845780638da5cb5b146103ac57806399fbab88146103c1578063a622ee7c146103ee578063ad3cb1cc1461042457600080fd5b80634f1ef286116101235780634f1ef286146102e057806352d1902d146102f35780635464556714610316578063575815111461032957806357ab320a146103495780635cd988261461035c57600080fd5b80630aa19397146101765780630eb9af381461019857806321de18f1146101d557806322c792d9146101ff578063256b5a021461029057806345aab48a146102b057600080fd5b3661017157005b600080fd5b34801561018257600080fd5b50610196610191366004614c89565b610520565b005b3480156101a457600080fd5b506101b86101b3366004614d83565b6109dd565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101e157600080fd5b506000546101ef9060ff1681565b60405190151581526020016101cc565b61021261020d366004614dcb565b610a36565b6040516101cc9190815181526020808301516001600160a01b039081169183019190915260408084015182169083015260608084015191821690830152610120820190506080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010083015161010083015292915050565b34801561029c57600080fd5b506101966102ab366004614d83565b610d0a565b3480156102bc57600080fd5b506101ef6102cb366004614d83565b60036020526000908152604090205460ff1681565b6101966102ee366004614f72565b610f15565b3480156102ff57600080fd5b50610308610f34565b6040519081526020016101cc565b610196610324366004614fe3565b610f51565b34801561033557600080fd5b50610196610344366004614d83565b611172565b610212610357366004615041565b6111f9565b61021261036a366004615080565b61134c565b34801561037b57600080fd5b5061019661135f565b34801561039057600080fd5b50610399611373565b6040516101cc9796959493929190615115565b3480156103b857600080fd5b506101b8611424565b3480156103cd57600080fd5b506103086103dc3660046151ad565b60016020526000908152604090205481565b3480156103fa57600080fd5b506101b8610409366004614d83565b6002602052600090815260409020546001600160a01b031681565b34801561043057600080fd5b50610455604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516101cc91906151c6565b6101966104703660046151d9565b611452565b34801561048157600080fd5b50610196610490366004614d83565b611843565b6101966104a33660046152b7565b611950565b3480156104b457600080fd5b506101966104c3366004615355565b611b66565b3480156104d457600080fd5b506101966104e3366004614d83565b611c4b565b3480156104f457600080fd5b506000546101b89061010090046001600160a01b031681565b61021261051b36600461538e565b611c89565b610528611dc0565b6066610532611df8565b6001600160a01b031663c614384c82336040518363ffffffff1660e01b815260040161055f9291906153c4565b60006040518083038186803b15801561057757600080fd5b505afa15801561058b573d6000803e3d6000fd5b50505086851490506105b05760405163b4fa3fb360e01b815260040160405180910390fd5b60008290036105d25760405163ac8da8e360e01b815260040160405180910390fd5b856000888282816105e5576105e56153e6565b9050610120020160400160208101906105fe9190614d83565b9050600089896000818110610615576106156153e6565b90506101200201606001602081019061062e9190614d83565b90506000805b848110156108065760008c8c83818110610650576106506153e6565b90506101200201803603810190610667919061548b565b905061067281611e07565b8151600090815260016020526040902054146106a15760405163673f032f60e11b815260040160405180910390fd5b846001600160a01b031681604001516001600160a01b0316146106d757604051631eb3268560e31b815260040160405180910390fd5b836001600160a01b031681606001516001600160a01b03161461070d5760405163020adce760e11b815260040160405180910390fd5b60008b8b84818110610721576107216153e6565b9050602002013590506000610734611df8565b60408085015160c086015160808701519251630e0700c960e41b81526001600160a01b03928316600482015260248101919091526044810192909252919091169063e0700c9090606401602060405180830381865afa15801561079b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107bf91906154a8565b9050808211806107cd575081155b156107eb576040516373a4c33f60e11b815260040160405180910390fd5b6107f582866154d7565b945050600190920191506106349050565b5060008061081e610817898b6155c0565b8587611edd565b915091508281146108425760405163b0f8fc9b60e01b815260040160405180910390fd5b60005b868110156109a75760008e8e83818110610861576108616153e6565b90506101200201803603810190610878919061548b565b905060008d8d8481811061088e5761088e6153e6565b905060200201359050600060018a6108a691906155cd565b84146108bc576108b786838961210c565b6108be565b855b905060006108df8460a001518560e001518461210c9092919063ffffffff16565b9050818460e0018181516108f391906155cd565b90525060a08401805182919061090a9083906155cd565b90525042608085015261091c84611e07565b845160009081526001602052604090205561093782886155cd565b965061094383896155cd565b8451604080519182526020820186905260009082015260608101849052608081018390529098507f4013fac4b4e975fa8ffff23c54f8559a67ae4ed54b834624d4e6888f137d95ce9060a00160405180910390a15050600190920191506108459050565b506109b7600086600080856121d0565b505050505050506109d56001600080516020615c5583398151915255565b505050505050565b6001600160a01b03818116600090815260026020526040812054909116610a1757604051630681d31960e51b815260040160405180910390fd5b506001600160a01b039081166000908152600260205260409020541690565b610a3e614bde565b610a46611dc0565b610a508484612285565b336001600160a01b03831614801590610a825750610a6c6125ab565b6001600160a01b0316336001600160a01b031614155b15610aa057604051630f23084160e31b815260040160405180910390fd5b61012084013515801590610ad757506001600160a01b038216610acb61016086016101408701614d83565b6001600160a01b031614155b15610af557604051630f23084160e31b815260040160405180910390fd5b6000610b076040860160208701614d83565b90506000610b1e6101b36040880160208901614d83565b60405163317afabb60e21b8152608088013560048201529091506001600160a01b0382169063c5ebeaec90602401600060405180830381600087803b158015610b6657600080fd5b505af1158015610b7a573d6000803e3d6000fd5b50505050600080610bb488806101000190610b9591906155e0565b610b9e916155c0565b85610baf60608c0160408d01614d83565b611edd565b915091508760a00135811015610bdd5760405163406220a960e01b815260040160405180910390fd5b610be5611df8565b6001600160a01b0316638a5d8a3460608a013583610c0960408d0160208e01614d83565b610c1960608e0160408f01614d83565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b03908116604484015216606482015260840160006040518083038186803b158015610c6e57600080fd5b505afa158015610c82573d6000803e3d6000fd5b505050508760800135821115610cab576040516335f36d1160e21b815260040160405180910390fd5b8760800135821015610cda57610cda83610cc98460808c01356155cd565b6001600160a01b0387169190612616565b610ce686898385612675565b945050505050610d036001600080516020615c5583398151915255565b9392505050565b6067610d14611df8565b6001600160a01b031663c614384c82336040518363ffffffff1660e01b8152600401610d419291906153c4565b60006040518083038186803b158015610d5957600080fd5b505afa158015610d6d573d6000803e3d6000fd5b5050600054604051639b845cdd60e01b815260ff909116151560048201523092506001600160a01b0385169150639b845cdd90602401602060405180830381865afa158015610dc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de49190615629565b6001600160a01b031614610e0b57604051630681d31960e51b815260040160405180910390fd5b6000826001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6f9190615629565b6001600160a01b038082166000908152600260205260409020549192501615610eab576040516304aabf3360e01b815260040160405180910390fd5b6001600160a01b0381811660008181526002602090815260409182902080546001600160a01b03191694881694851790559051928352909130917f891f008b8c9cadc4c9114ed37ce718f739b28049d58e50e35a02d94c4e9b06ff910160405180910390a3505050565b610f1d612899565b610f268261293e565b610f30828261299b565b5050565b6000610f3e612a58565b50600080516020615c3583398151915290565b610f59611dc0565b610f73610f6d610f6884615666565b612aa1565b82612b38565b6000610f8560a0840160808501614d83565b9050610f9081612c50565b4283351015610fb2576040516362b439dd60e11b815260040160405180910390fd5b60006040518060c00160405280856020013581526020018560400135815260200160008152602001866002811115610fec57610fec6156f1565b81526000602082015260400161100a6101c087016101a08801614d83565b6001600160a01b0316905290506000611034826060870161102f6101808901896155e0565b612cfc565b606086013560008181526001602052604090205491925090600019016110aa577f75b84e1e549840eae7725d388221efd1eff0445233ca8ba75c16fe9aa4420f9d81858460000151856040015186606001518760a0015160405161109d96959493929190615707565b60405180910390a1611152565b7fbf109bd723f23a8453e35c569e9a1249639bca162cab600e8ac822cffeb84e5581858460000151856040015186606001518760a0015188608001518961010001518a60e00151604051611149999897969594939291909889526001600160a01b0397909716602089015260408801959095526060870193909352608086019190915260a085015260c084015260e08301526101008201526101200190565b60405180910390a15b5050505061116d6001600080516020615c5583398151915255565b505050565b61117a611df8565b604051630935e01b60e21b81523360048201526001600160a01b0391909116906324d7806c9060240160006040518083038186803b1580156111bb57600080fd5b505afa1580156111cf573d6000803e3d6000fd5b5050506001600160a01b039091166000908152600360205260409020805460ff1916600117905550565b611201614bde565b611209611dc0565b61121383836131b9565b61122360a0840160808501614d83565b6001600160a01b0316336001600160a01b031614611274576112436125ab565b6001600160a01b0316336001600160a01b03161461127457604051630f23084160e31b815260040160405180910390fd5b60006112883685900385016060860161548b565b905083600001358160a0018181516112a091906154d7565b90525060e081018051853591906112b89083906154d7565b9052506112c481611e07565b60608501356000818152600160205260409020919091557fe3212ff75492fa89c1dcc88edb7da97c3dba570be9ed969716421a1937d2e8759061130d60a0870160808801614d83565b60405161132592919088359081906000908190615707565b60405180910390a190506113466001600080516020615c5583398151915255565b92915050565b611354614bde565b610d03838333610a36565b6113676133a1565b61137160006133d3565b565b60006060808280808381600080516020615c1583398151915280549091501580156113a057506001810154155b6113e95760405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b60448201526064015b60405180910390fd5b6113f1613444565b6113f9613507565b60408051600080825260208201909252600f60f81b9c939b5091995046985030975095509350915050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61145a611dc0565b6066611464611df8565b6001600160a01b031663c614384c82336040518363ffffffff1660e01b81526004016114919291906153c4565b60006040518083038186803b1580156114a957600080fd5b505afa1580156114bd573d6000803e3d6000fd5b505050506060860135602085013581146114ea5760405163af61069360e01b815260040160405180910390fd5b428560600135101561150f576040516362b439dd60e11b815260040160405180910390fd5b4287351015611531576040516362b439dd60e11b815260040160405180910390fd5b600061154360a0890160808a01614d83565b905061159c8161156061155b368a90038a018a615749565b613546565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506135d792505050565b6115b16115ab610f688a615666565b88612b38565b60006040518060c001604052808a6020013581526020018a6040013581526020018860c0013581526020018b60028111156115ee576115ee6156f1565b81526000602082015260400161160c6101c08c016101a08d01614d83565b6001600160a01b03169052905060006116318260608c0161102f6101808e018e6155e0565b6060810151604082015160208301519293509091600061165183856154d7565b9050600061166260208e018e6157d7565b905060018160ff1611156116895760405163af61069360e01b815260040160405180910390fd5b60ff8116156116b4576116a08260808f01356157f2565b6116ae60a08f0135856157f2565b106116d2565b6116c28260808f01356157f2565b6116d060a08f0135856157f2565b115b156116f057604051635d5ce00360e01b815260040160405180910390fd5b6000898152600160205260409020546000190161177757855160a080880151604080518d81526001600160a01b038d16602082015260ff86168183015260608101949094526080840188905291830188905260c0830152517f6fe8780cb281bfa04b1136759ae4474c3ada2c511162f19556f299187c63340a9181900360e00190a1611822565b7fa3afbbf0ccff02ce05f5ab35fbd61c51728a8de873a5a5e51b910650a41555958989838960000151888a8c60a001518d608001518e61010001518f60e001516040516118199a99989796959493929190998a526001600160a01b039890981660208a015260ff9690961660408901526060880194909452608087019290925260a086015260c085015260e08401526101008301526101208201526101400190565b60405180910390a15b505050505050505050506109d56001600080516020615c5583398151915255565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03166000811580156118885750825b90506000826001600160401b031660011480156118a45750303b155b9050811580156118b2575080155b156118d05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156118fa57845460ff60401b1916600160401b1785555b611903866137be565b83156109d557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050505050565b611958611dc0565b6064611962611df8565b6001600160a01b031663c614384c82336040518363ffffffff1660e01b815260040161198f9291906153c4565b60006040518083038186803b1580156119a757600080fd5b505afa1580156119bb573d6000803e3d6000fd5b5050505060006040518060c0016040528088815260200160008152602001600081526020018960028111156119f2576119f26156f1565b8152600160208201526001600160a01b03851660409091015290506000611a1b82888888612cfc565b90506000611a27611df8565b6001600160a01b0316637ccef6c3611a4560608b0160408c01614d83565b611a5560808c0160608d01614d83565b8b60e001356040518463ffffffff1660e01b8152600401611a7893929190615809565b602060405180830381865afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab991906154a8565b9050808260c001518360000151611ad091906154d7565b1115611aef5760405163c4d82e4360e01b815260040160405180910390fd5b7fc84dd454965cb66936af89c78c1833d5dc2554cd53f6ef2ad1f7b0945a94c5938835611b2260408b0160208c01614d83565b8451604080870151606088015160a08901519251611b439695949390615707565b60405180910390a1505050506109d56001600080516020615c5583398151915255565b611b6e6137d1565b611b7661381a565b611b7f81613822565b611b87613833565b611c0382611bbc576040518060400160405280600f81526020016e15d85cd8589a54da1bdc9d141bdbdb608a1b815250611be4565b6040518060400160405280600e81526020016d15d85cd8589a531bdb99d41bdbdb60921b8152505b604051806040016040528060018152602001603160f81b815250613843565b6000805460ff1916831515178155600190600390611c1f613855565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790555050565b611c536133a1565b6001600160a01b038116611c7d57604051631e4fbdf760e01b8152600060048201526024016113e0565b611c86816133d3565b50565b611c91614bde565b611c99611dc0565b611ca3838361389c565b611cb36080840160608501614d83565b6001600160a01b0316336001600160a01b031614611ce457604051630f23084160e31b815260040160405180910390fd5b6000611cf83685900385016040860161548b565b905083600001358160e001818151611d1091906155cd565b905250611d1c81611e07565b604080860135600090815260016020522055611d57611d416080860160608701614d83565b60608301516001600160a01b0316908635612616565b7f54b6819c53801a5d5a7d6d5ab617065bf559e436720e1ff204d1c66fe3120bbf6040850135611d8d6080870160608801614d83565b604080519283526001600160a01b039091166020830152600090820181905286356060830152608082015260a001611325565b600080516020615c55833981519152805460011901611df257604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6000611e02611424565b905090565b60007f48c32350097d00445d05f246b194474a08c279eadfd70fd48d659fc07ffc7297826000015183602001518460400151856060015186608001518760a001518860c001518960e001518a6101000151604051602001611ec09a99989796959493929190998a5260208a01989098526001600160a01b0396871660408a0152948616606089015292909416608087015260a086015260c085019290925260e08401919091526101008301526101208201526101400190565b604051602081830303815290604052805190602001209050919050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015611f26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4a91906154a8565b6040516370a0823160e01b81523060048201529092506001600160a01b038416906370a0823190602401602060405180830381865afa158015611f91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb591906154a8565b855190915060005b81811015612017576000878281518110611fd957611fd96153e6565b6020026020010151905061200d8160400151826020015183600001516001600160a01b0316613a4b9092919063ffffffff16565b5050600101611fbd565b506040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa15801561205c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208091906154a8565b61208a90846155cd565b6040516370a0823160e01b815230600482015290935082906001600160a01b038616906370a0823190602401602060405180830381865afa1580156120d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f791906154a8565b61210191906155cd565b915050935093915050565b6000838302816000198587098281108382030391505080600003612143578382816121395761213961582d565b0492505050610d03565b8084116121635760405163227bc15360e01b815260040160405180910390fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b60006121db856109dd565b905060006121e983856154d7565b90506121ff6001600160a01b0387168383612616565b60405163340f508760e21b8152600481018290526024810188905285151560448201526001600160a01b0383169063d03d421c90606401600060405180830381600087803b15801561225057600080fd5b505af1158015612264573d6000803e3d6000fd5b5050505050505050505050565b6001600080516020615c5583398151915255565b612299610f6d61229484615843565b613ae8565b60006122ae368490038401610120850161548b565b905060006122c26040850160208601614d83565b905060006122d66060860160408701614d83565b835190915015612389576122e983611e07565b8351600090815260016020526040902054146123185760405163673f032f60e11b815260040160405180910390fd5b82604001516001600160a01b0316826001600160a01b03161461234e57604051631eb3268560e31b815260040160405180910390fd5b82606001516001600160a01b0316816001600160a01b0316146123845760405163020adce760e11b815260040160405180910390fd5b6124a4565b8435600090815260016020526040902054156123b85760405163e168e4db60e01b815260040160405180910390fd5b6000546124049060ff166123e557816001600160a01b031660009081526003602052604090205460ff1690565b826001600160a01b031660009081526003602052604090205460ff1690565b61242157604051631eb3268560e31b815260040160405180910390fd5b806001600160a01b0316826001600160a01b0316036124535760405163020adce760e11b815260040160405180910390fd5b8261010001518360e001518460c001518560a0015161247291906154d7565b61247c91906154d7565b61248691906154d7565b156124a45760405163673f032f60e11b815260040160405180910390fd5b6124b26101008601866155e0565b90506000036124d45760405163ac8da8e360e01b815260040160405180910390fd5b84608001356000036124f957604051635884252160e11b815260040160405180910390fd5b428560c00135101561251e576040516362b439dd60e11b815260040160405180910390fd5b6000546125549060ff166125325781612534565b825b61254660e088013560608901356154d7565b61254e613855565b33613bc1565b6000546125a49060e08701359060ff1661257d576125786060880160408901614d83565b61258d565b61258d6040880160208901614d83565b61259f61026089016102408a01614d83565b613c3c565b5050505050565b60006125b5611df8565b6001600160a01b031663ea81ec6f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125f2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e029190615629565b6040516001600160a01b0383811660248301526044820183905261116d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613c8c565b61267d614bde565b610120840135151560008161269357853561269a565b6101208601355b90506000604051806101200160405280838152602001896001600160a01b031681526020018860200160208101906126d29190614d83565b6001600160a01b031681526020016126f060608a0160408b01614d83565b6001600160a01b03168152602001846127095742612710565b6101a08901355b815260200161272860608a01356101c08b01356154d7565b815260200161273c876101e08b01356154d7565b81526020016060890135612755896102008c01356154d7565b61275f91906154d7565b815260200161277760e08a01356102208b01356154d7565b9052905061278481611e07565b60008381526001602052604090205582156127f0577fe80a6b78f88f08b92cac13fc3aa5b23cf4753f8fb8bd85e4b356b670634f77f7828960608a0135886127cc828c6154d7565b8c60e001356040516127e396959493929190615707565b60405180910390a161288e565b7f41ae823bf4c91d7bece87d6eada54c198fd07594ad19d96d72d025896049bfdb828961282360408b0160208c01614d83565b61283360608c0160408d01614d83565b60e080870151604080519687526001600160a01b0395861660208801529385169386019390935292166060848101919091528b0135608084015260a0830189905260c0830152808a0135908201526101000160405180910390a15b979650505050505050565b306001600160a01b037f000000000000000000000000d5a09a0827c3d91e8530deee3b73edae6b33ae9516148061292057507f000000000000000000000000d5a09a0827c3d91e8530deee3b73edae6b33ae956001600160a01b0316612914600080516020615c35833981519152546001600160a01b031690565b6001600160a01b031614155b156113715760405163703e46dd60e11b815260040160405180910390fd5b612946611df8565b604051630935e01b60e21b81523360048201526001600160a01b0391909116906324d7806c9060240160006040518083038186803b15801561298757600080fd5b505afa1580156125a4573d6000803e3d6000fd5b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156129f5575060408051601f3d908101601f191682019092526129f2918101906154a8565b60015b612a1d57604051634c9c8ce360e01b81526001600160a01b03831660048201526024016113e0565b600080516020615c358339815191528114612a4e57604051632a87526960e21b8152600481018290526024016113e0565b61116d8383613cef565b306001600160a01b037f000000000000000000000000d5a09a0827c3d91e8530deee3b73edae6b33ae9516146113715760405163703e46dd60e11b815260040160405180910390fd5b60007fa0bd57f9850d2bad9e64e8002b0ce58a1098b44251ffb0980264663b82250ae5826000015183602001518460400151612ae08660600151611e07565b612aed8760800151613d45565b60a080890151604080516020810199909952880196909652606087019490945260808601929092529184019190915260c08301526001600160a01b031660e082015261010001611ec0565b6000612b4383613eba565b90506000600182612b5760208601866157d7565b604080516000815260208181018084529490945260ff9092168282015291860135606082015290850135608082015260a0016020604051602081039080840390855afa158015612bab573d6000803e3d6000fd5b5050506020604051035190506000612bc1611df8565b6001600160a01b031663d1f856ee6065846040518363ffffffff1660e01b8152600401612bef9291906153c4565b6040805180830381865afa158015612c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2f9190615910565b509050806125a457604051638baa579f60e01b815260040160405180910390fd5b6001600160a01b0381163303612c635750565b6000612c6d611df8565b6001600160a01b031663d1f856ee6064336040518363ffffffff1660e01b8152600401612c9b9291906153c4565b6040805180830381865afa158015612cb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cdb9190615910565b50905080610f3057604051630f23084160e31b815260040160405180910390fd5b612d4b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8335612d64612d5f3687900387018761548b565b611e07565b60008281526001602052604090205414612d915760405163673f032f60e11b815260040160405180910390fd5b6000839003612db35760405163ac8da8e360e01b815260040160405180910390fd5b602086015160c08601359060e08701359060a0880135901580612dd95750828960200151115b15612de657602089018390525b612df4888a60000151613ee7565b8952612e23612e0387896155c0565b612e1360808b0160608c01614d83565b610baf60608c0160408d01614d83565b604087015260208601819052821015612e4f57604051631cbf0b8960e01b815260040160405180910390fd5b612e6185604001518a60200151613fab565b604087015260608601526080890151158015612e84575088602001518560400151105b15612ea25760405163b0f8fc9b60e01b815260040160405180910390fd5b82896020015103612edd5761010080890135608087015260e0860182905285018290526020850151612ed5908390613fab565b508552612f5c565b82856040015183612eee91906157f2565b612ef89190615948565b61010086015260408501518390612f0f90836157f2565b612f199190615948565b60e086015260408501518390612f34906101008b01356157f2565b612f3e9190615948565b60808601526101008501516020860151612f589190613fab565b5085525b606085015115612f7a57612f7a896000015186606001516003613fd8565b845160408a0151610100870151612fb7929190612fa8908c90612f9d90856154d7565b60005460ff16614031565b612fb291906154d7565b613fab565b60a0870152855260808901511561308657845161307e90612fd6611df8565b6001600160a01b03166313487f0d84612ff560608e0160408f01614d83565b8d60600160208101906130089190614d83565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b039182166024840152166044820152606401602060405180830381865afa15801561305a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fb291906154a8565b60c087015285525b60208901516130b39061309f60608b0160408c01614d83565b8b60800151886040015189606001516121d0565b6130e989606001518960600160208101906130ce9190614d83565b6130de60408c0160208d01614d83565b8c60a0015189614089565b8285604001511080156130fe57508860800151155b1561319a576000613114368a90038a018a61548b565b90508560e001518160a00181815161312c91906155cd565b905250604086015160c0820180516131459083906155cd565b90525061010086015160e08201805161315f9083906155cd565b9052506080860151610100820180516131799083906155cd565b90525061318581611e07565b600086815260016020526040902055506131ad565b6000848152600160208190526040909120555b50505050949350505050565b6131d3610f6d6131ce3685900385018561596a565b6144a5565b60006131e73684900384016060850161548b565b6040810151606082015191925090843560000361321757604051637ca44a8f60e11b815260040160405180910390fd5b60005460ff16156133015784602001356000036132465760405162ffe80f60e41b815260040160405180910390fd5b6000613250611df8565b60c08501516080860151604051630e0700c960e41b81526001600160a01b0387811660048301526024820193909352604481019190915291169063e0700c9090606401602060405180830381865afa1580156132b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132d491906154a8565b905080866020013511156132fb576040516373a4c33f60e11b815260040160405180910390fd5b50613324565b602085013515613324576040516373a4c33f60e11b815260040160405180910390fd5b61332d83611e07565b83516000908152600160205260409020541461335c5760405163673f032f60e11b815260040160405180910390fd5b4285604001351015613381576040516362b439dd60e11b815260040160405180910390fd5b6000546125a49060ff166133955781613397565b825b863561254e613855565b336133aa611424565b6001600160a01b0316146113715760405163118cdaa760e01b81523360048201526024016113e0565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1028054606091600080516020615c1583398151915291613483906159dd565b80601f01602080910402602001604051908101604052809291908181526020018280546134af906159dd565b80156134fc5780601f106134d1576101008083540402835291602001916134fc565b820191906000526020600020905b8154815290600101906020018083116134df57829003601f168201915b505050505091505090565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1038054606091600080516020615c1583398151915291613483906159dd565b60007fd9cfa04c18137773e2e5104f35b2ad1f62323bab79de5db55d8111ce70593b66826000015183602001518460400151856060015186608001518760a001518860c00151604051602001611ec098979695949392919097885260ff96909616602088015260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b60006135e283613eba565b90508151604103613708576020828101516040808501516060808701518351600080825296810180865288905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015613651573d6000803e3d6000fd5b505050602060405103519050876001600160a01b0316816001600160a01b031614806136f45750613680611df8565b6040516342e9d87b60e01b81526001600160a01b038a81166004830152838116602483015291909116906342e9d87b90604401602060405180830381865afa1580156136d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136f49190615a11565b15613703575050505050505050565b505050505b6001600160a01b0384163b156137a557604051630b135d3f60e11b81526001600160a01b03851690631626ba7e906137469084908690600401615a2e565b602060405180830381865afa92505050801561377f575060408051601f3d908101601f1916820190925261377c91810190615a4f565b60015b156137a5576374eca2c160e11b6001600160e01b03198216016137a3575050505050565b505b604051638baa579f60e01b815260040160405180910390fd5b6137c66137d1565b611c86600082611b66565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661137157604051631afcd79f60e31b815260040160405180910390fd5b6113716137d1565b61382a6137d1565b611c8681614510565b61383b6137d1565b611371614518565b61384b6137d1565b610f308282614520565b600061385f611df8565b6001600160a01b0316634f0e0ef36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125f2573d6000803e3d6000fd5b6138b6610f6d6138b136859003850185615a79565b614581565b81356000036138d857604051637ca44a8f60e11b815260040160405180910390fd5b42826020013510156138fd576040516362b439dd60e11b815260040160405180910390fd5b60006139113684900384016040850161548b565b905061391c81611e07565b81516000908152600160205260409020541461394b5760405163673f032f60e11b815260040160405180910390fd5b60005460ff1615613a16576000613960611df8565b6001600160a01b0316639f54227a836040015184606001518560a001516040518463ffffffff1660e01b815260040161399b93929190615809565b602060405180830381865afa1580156139b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dc91906154a8565b60c083015190915081906139f19086356154d7565b1115613a10576040516335f36d1160e21b815260040160405180910390fd5b50505050565b60a081015160e0820151843591613a2c916155cd565b101561116d57604051631cbf0b8960e01b815260040160405180910390fd5b606081471015613a705760405163cd78605960e01b81523060048201526024016113e0565b600080856001600160a01b03168486604051613a8c9190615abd565b60006040518083038185875af1925050503d8060008114613ac9576040519150601f19603f3d011682016040523d82523d6000602084013e613ace565b606091505b5091509150613ade8683836145df565b9695505050505050565b60007f6a36946795713cb0d845dfd59fe886128a8f898548f6a3bd0d7211014b54b5b6826000015183602001518460400151856060015186608001518760a001518860c001518960e00151613b418b6101000151613d45565b613b4f8c6101200151611e07565b610140808e015160408051602081019e909e528d019b909b526001600160a01b03998a1660608d015297891660808c015260a08b019690965260c08a019490945260e089019290925261010088015261012087015291850191909152610160840152166101808201526101a001611ec0565b3415613c2757816001600160a01b0316846001600160a01b031614613bf957604051631eb3268560e31b815260040160405180910390fd5b823414613c1957604051637ca44a8f60e11b815260040160405180910390fd5b613c228261463b565b613a10565b613a106001600160a01b038516823086614698565b8215801590613c5357506001600160a01b03811615155b15613c7057613c638383836146c0565b613c6d90846155cd565b92505b61116d613c7b614829565b6001600160a01b0384169085612616565b6000613ca16001600160a01b03841683614870565b90508051600014158015613cc6575080806020019051810190613cc49190615a11565b155b1561116d57604051635274afe760e01b81526001600160a01b03841660048201526024016113e0565b613cf88261487e565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115613d3d5761116d82826148e3565b610f30614950565b805160009081816001600160401b03811115613d6357613d63614e2d565b604051908082528060200260200182016040528015613d8c578160200160208202803683370190505b50905060005b82811015613e89577fae8afe996a65dac2aaac44f3859cc01f3527ee213e83ea2eda3501690d0ea956858281518110613dcd57613dcd6153e6565b602002602001015160000151868381518110613deb57613deb6153e6565b602002602001015160200151878481518110613e0957613e096153e6565b60200260200101516040015180519060200120604051602001613e4e94939291909384526001600160a01b039290921660208401526040830152606082015260800190565b60405160208183030381529060405280519060200120828281518110613e7657613e766153e6565b6020908102919091010152600101613d92565b5080604051602001613e9b9190615ad9565b6040516020818303038152906040528051906020012092505050919050565b6000611346613ec761496f565b8360405161190160f01b8152600281019290925260228201526042902090565b600080613ef2611df8565b6001600160a01b031663e0700c90613f106060870160408801614d83565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260c0870135602482015260808701356044820152606401602060405180830381865afa158015613f68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8c91906154a8565b9050821580613f9a57508083115b15613fa3578092505b509092915050565b60008082841115613fca57613fc083856155cd565b9150829050613fd1565b5060009050825b9250929050565b600082841015613ff157613fec84846155cd565b613ffb565b613ffb83856155cd565b905061400784836157f2565b6140128260646157f2565b1115613a105760405163604e917360e01b815260040160405180910390fd5b60008082614043578460e00135614055565b61405560c086013560a08701356154d7565b614064906101008701356154d7565b905080614076610100870135866157f2565b6140809190615948565b95945050505050565b60a08101516001600160a01b038316156140b5576140a88186856146c0565b6140b290826155cd565b90505b6140bd613855565b6001600160a01b0316856001600160a01b0316036142a55760008260c001518284600001516140ec91906154d7565b6140f691906154d7565b90506000614102613855565b90506001886002811115614118576141186156f1565b036141ce574782111561418f576001600160a01b038116632e1a7d4d61413e47856155cd565b6040518263ffffffff1660e01b815260040161415c91815260200190565b600060405180830381600087803b15801561417657600080fd5b505af115801561418a573d6000803e3d6000fd5b505050505b6141a08361419b614829565b614979565b60c0840151156141ba576141ba8460c0015161419b614a05565b83516141c69087614979565b5050506125a4565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015614215573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061423991906154a8565b9050808311156142a1576001600160a01b03821663d0e30db061425c83866155cd565b6040518263ffffffff1660e01b81526004016000604051808303818588803b15801561428757600080fd5b505af115801561429b573d6000803e3d6000fd5b50505050505b5050505b80156142c7576142c76142b6614829565b6001600160a01b0387169083612616565b60c0820151156142f2576142f26142dc614a05565b60c08401516001600160a01b0388169190612616565b8151156109d557600286600281111561430d5761430d6156f1565b0361448d57600061431d866109dd565b8351604051636eb1769f60e11b81523060048201526001600160a01b038084166024830152929350909188169063dd62ed3e90604401602060405180830381865afa158015614370573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061439491906154a8565b10156144105760405163095ea7b360e01b81526001600160a01b038281166004830152600019602483015287169063095ea7b3906044016020604051808303816000875af11580156143ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061440e9190615a11565b505b8251604051636e553f6560e01b815260048101919091526001600160a01b038681166024830152821690636e553f65906044016020604051808303816000875af1158015614462573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061448691906154a8565b50506109d5565b81516109d5906001600160a01b038716908690612616565b60007fd29b28be0a20bef4afc9413b920d8488b80deaa13a72160368141683cabbbe128260000151836020015184604001516144e48660600151611e07565b6040805160208101969096528501939093526060840191909152608083015260a082015260c001611ec0565b611c536137d1565b6122716137d1565b6145286137d1565b600080516020615c158339815191527fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1026145628482615b56565b50600381016145718382615b56565b5060008082556001909101555050565b60007f22e250be3f7f69a602084f65733b4d3afe1bb6ea86e4500d362b505ba7f135ed826000015183602001516145bb8560400151611e07565b6040805160208101959095528401929092526060830152608082015260a001611ec0565b6060826145f4576145ef82614a4c565b610d03565b815115801561460b57506001600160a01b0384163b155b1561463457604051639996b31560e01b81526001600160a01b03851660048201526024016113e0565b5080610d03565b6000819050806001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561467b57600080fd5b505af115801561468f573d6000803e3d6000fd5b50505050505050565b613a1084856001600160a01b03166323b872dd86868660405160240161264393929190615809565b6000806146cb614a75565b60405163850bf6e360e01b81526001600160a01b0385811660048301526024820188905291925060009183169063850bf6e390604401602060405180830381865afa15801561471e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061474291906154a8565b905080156140805760405163095ea7b360e01b81526001600160a01b0383811660048301526024820183905286169063095ea7b3906044016020604051808303816000875af1158015614799573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147bd9190615a11565b5060405163785a291b60e01b81526001600160a01b0383169063785a291b906147ee90879089908690600401615809565b600060405180830381600087803b15801561480857600080fd5b505af115801561481c573d6000803e3d6000fd5b5050505095945050505050565b6000614833611df8565b6001600160a01b031663b3f006746040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125f2573d6000803e3d6000fd5b6060610d0383836000613a4b565b806001600160a01b03163b6000036148b457604051634c9c8ce360e01b81526001600160a01b03821660048201526024016113e0565b600080516020615c3583398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516149009190615abd565b600060405180830381855af49150503d806000811461493b576040519150601f19603f3d011682016040523d82523d6000602084013e614940565b606091505b50915091506140808583836145df565b34156113715760405163b398979f60e01b815260040160405180910390fd5b6000611e02614abc565b8115610f30576000816001600160a01b03168360405160006040518083038185875af1925050503d80600081146149cc576040519150601f19603f3d011682016040523d82523d6000602084013e6149d1565b606091505b505090508061116d5760405163f733a60960e01b8152600481018490526001600160a01b03831660248201526044016113e0565b6000614a0f611df8565b6001600160a01b0316630c5e67256040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125f2573d6000803e3d6000fd5b805115614a5c5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000614a7f611df8565b6001600160a01b0316639c7ee9306040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125f2573d6000803e3d6000fd5b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f614ae7614b30565b614aef614b9a565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000600080516020615c1583398151915281614b4a613444565b805190915015614b6257805160209091012092915050565b81548015614b71579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b6000600080516020615c1583398151915281614bb4613507565b805190915015614bcc57805160209091012092915050565b60018201548015614b71579392505050565b6040518061012001604052806000815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008083601f840112614c5757600080fd5b5081356001600160401b03811115614c6e57600080fd5b6020830191508360208260051b8501011115613fd157600080fd5b60008060008060008060608789031215614ca257600080fd5b86356001600160401b03811115614cb857600080fd5b8701601f81018913614cc957600080fd5b80356001600160401b03811115614cdf57600080fd5b89602061012083028401011115614cf557600080fd5b6020918201975095508701356001600160401b03811115614d1557600080fd5b614d2189828a01614c45565b90955093505060408701356001600160401b03811115614d4057600080fd5b614d4c89828a01614c45565b979a9699509497509295939492505050565b6001600160a01b0381168114611c8657600080fd5b8035614d7e81614d5e565b919050565b600060208284031215614d9557600080fd5b8135610d0381614d5e565b60006102608284031215614db357600080fd5b50919050565b600060608284031215614db357600080fd5b600080600060a08486031215614de057600080fd5b83356001600160401b03811115614df657600080fd5b614e0286828701614da0565b935050614e128560208601614db9565b91506080840135614e2281614d5e565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715614e6657614e66614e2d565b60405290565b604051606081016001600160401b0381118282101715614e6657614e66614e2d565b60405160c081016001600160401b0381118282101715614e6657614e66614e2d565b60405161016081016001600160401b0381118282101715614e6657614e66614e2d565b604051601f8201601f191681016001600160401b0381118282101715614efb57614efb614e2d565b604052919050565b600082601f830112614f1457600080fd5b81356001600160401b03811115614f2d57614f2d614e2d565b614f40601f8201601f1916602001614ed3565b818152846020838601011115614f5557600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215614f8557600080fd5b8235614f9081614d5e565b915060208301356001600160401b03811115614fab57600080fd5b614fb785828601614f03565b9150509250929050565b803560038110614d7e57600080fd5b60006101c08284031215614db357600080fd5b600080600060a08486031215614ff857600080fd5b61500184614fc1565b925060208401356001600160401b0381111561501c57600080fd5b61502886828701614fd0565b9250506150388560408601614db9565b90509250925092565b6000808284036101e081121561505657600080fd5b61018081121561506557600080fd5b50829150615077846101808501614db9565b90509250929050565b6000806080838503121561509357600080fd5b82356001600160401b038111156150a957600080fd5b6150b585828601614da0565b9250506150778460208501614db9565b60005b838110156150e05781810151838201526020016150c8565b50506000910152565b600081518084526151018160208601602086016150c5565b601f01601f19169290920160200192915050565b60ff60f81b8816815260e06020820152600061513460e08301896150e9565b828103604084015261514681896150e9565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b8181101561519c57835183526020938401939092019160010161517e565b50909b9a5050505050505050505050565b6000602082840312156151bf57600080fd5b5035919050565b602081526000610d0360208301846150e9565b6000806000806000808688036101a08112156151f457600080fd5b6151fd88614fc1565b965060208801356001600160401b0381111561521857600080fd5b6152248a828b01614fd0565b9650506152348960408a01614db9565b945060e0609f198201121561524857600080fd5b5060a0870192506101808701356001600160401b0381111561526957600080fd5b8701601f8101891361527a57600080fd5b80356001600160401b0381111561529057600080fd5b8960208284010111156152a257600080fd5b60208201935080925050509295509295509295565b6000806000806000808688036101a08112156152d257600080fd5b6152db88614fc1565b965060208801359550610120603f19820112156152f757600080fd5b506040870193506101608701356001600160401b0381111561531857600080fd5b61532489828a01614c45565b90945092505061018087013561533981614d5e565b809150509295509295509295565b8015158114611c8657600080fd5b6000806040838503121561536857600080fd5b823561537381615347565b9150602083013561538381614d5e565b809150509250929050565b6000808284036101c08112156153a357600080fd5b6101608112156153b257600080fd5b50829150615077846101608501614db9565b6001600160401b039290921682526001600160a01b0316602082015260400190565b634e487b7160e01b600052603260045260246000fd5b6000610120828403121561540f57600080fd5b615417614e43565b82358152905061542960208301614d73565b602082015261543a60408301614d73565b604082015261544b60608301614d73565b60608201526080828101359082015260a0808301359082015260c0808301359082015260e080830135908201526101009182013591810191909152919050565b6000610120828403121561549e57600080fd5b610d0383836153fc565b6000602082840312156154ba57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115611346576113466154c1565b60006001600160401b0383111561550357615503614e2d565b8260051b61551360208201614ed3565b84815291508201602082018582111561552b57600080fd5b835b828110156155b65780356001600160401b0381111561554b57600080fd5b85016060818903121561555d57600080fd5b615565614e6c565b813561557081614d5e565b81526020828101359082015260408201356001600160401b0381111561559557600080fd5b6155a18a828501614f03565b6040830152508352506020918201910161552d565b5050509392505050565b6000610d033684846154ea565b81810381811115611346576113466154c1565b6000808335601e198436030181126155f757600080fd5b8301803591506001600160401b0382111561561157600080fd5b6020019150600581901b3603821315613fd157600080fd5b60006020828403121561563b57600080fd5b8151610d0381614d5e565b600082601f83011261565757600080fd5b610d03838335602085016154ea565b60006101c0823603121561567957600080fd5b615681614e8e565b8235815260208084013590820152604080840135908201526156a636606085016153fc565b60608201526101808301356001600160401b038111156156c557600080fd5b6156d136828601615646565b6080830152506101a08301356156e681614d5e565b60a082015292915050565b634e487b7160e01b600052602160045260246000fd5b9586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b803560ff81168114614d7e57600080fd5b600060e082840312801561575c57600080fd5b5060405160009060e081016001600160401b038111828210171561578257615782614e2d565b60405261578e84615738565b81526020848101359082015260408085013590820152606080850135908201526080808501359082015260a0808501359082015260c09384013593810193909352509092915050565b6000602082840312156157e957600080fd5b610d0382615738565b8082028115828204841417611346576113466154c1565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601260045260246000fd5b6000610260823603121561585657600080fd5b61585e614eb0565b8235815261586e60208401614d73565b602082015261587f60408401614d73565b6040820152606083810135908201526080808401359082015260a0808401359082015260c0808401359082015260e080840135908201526101008301356001600160401b038111156158d057600080fd5b6158dc36828601615646565b610100830152506158f13661012085016153fc565b6101208201526159046102408401614d73565b61014082015292915050565b6000806040838503121561592357600080fd5b825161592e81615347565b602084015190925063ffffffff8116811461538357600080fd5b60008261596557634e487b7160e01b600052601260045260246000fd5b500490565b600061018082840312801561597e57600080fd5b50604051600090608081016001600160401b03811182821017156159a4576159a4614e2d565b604090815284358252602080860135908301528481013590820181905291506159d085606086016153fc565b6060820152949350505050565b600181811c908216806159f157607f821691505b602082108103614db357634e487b7160e01b600052602260045260246000fd5b600060208284031215615a2357600080fd5b8151610d0381615347565b828152604060208201526000615a4760408301846150e9565b949350505050565b600060208284031215615a6157600080fd5b81516001600160e01b031981168114610d0357600080fd5b6000610160828403128015615a8d57600080fd5b50615a96614e6c565b8235815260208084013590820152615ab184604085016153fc565b60408201529392505050565b60008251615acf8184602087016150c5565b9190910192915050565b8151600090829060208501835b82811015615b04578151845260209384019390910190600101615ae6565b509195945050505050565b601f82111561116d57806000526020600020601f840160051c81016020851015615b365750805b601f840160051c820191505b818110156125a45760008155600101615b42565b81516001600160401b03811115615b6f57615b6f614e2d565b615b8381615b7d84546159dd565b84615b0f565b6020601f821160018114615bb75760008315615b9f5750848201515b600019600385901b1c1916600184901b1784556125a4565b600084815260208120601f198516915b82811015615be75787850151825560209485019460019092019101615bc7565b5084821015615c055786840151600019600387901b60f8161c191681555b50505050600190811b0190555056fea16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122058f1399986fa554b464a906b0ecbaab7b1f17a8afde57287330eaf2c1889bf6864736f6c634300081a0033

Verified Source Code Full Match

Compiler: v0.8.26+commit.8a97fa7a EVM: paris Optimization: Yes (200 runs)
AccessManagerUpgradeable.sol 770 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManager.sol)

pragma solidity ^0.8.20;

import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {MulticallUpgradeable} from "../../utils/MulticallUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev AccessManager is a central contract to store the permissions of a system.
 *
 * A smart contract under the control of an AccessManager instance is known as a target, and will inherit from the
 * {AccessManaged} contract, be connected to this contract as its manager and implement the {AccessManaged-restricted}
 * modifier on a set of functions selected to be permissioned. Note that any function without this setup won't be
 * effectively restricted.
 *
 * The restriction rules for such functions are defined in terms of "roles" identified by an `uint64` and scoped
 * by target (`address`) and function selectors (`bytes4`). These roles are stored in this contract and can be
 * configured by admins (`ADMIN_ROLE` members) after a delay (see {getTargetAdminDelay}).
 *
 * For each target contract, admins can configure the following without any delay:
 *
 * * The target's {AccessManaged-authority} via {updateAuthority}.
 * * Close or open a target via {setTargetClosed} keeping the permissions intact.
 * * The roles that are allowed (or disallowed) to call a given function (identified by its selector) through {setTargetFunctionRole}.
 *
 * By default every address is member of the `PUBLIC_ROLE` and every target function is restricted to the `ADMIN_ROLE` until configured otherwise.
 * Additionally, each role has the following configuration options restricted to this manager's admins:
 *
 * * A role's admin role via {setRoleAdmin} who can grant or revoke roles.
 * * A role's guardian role via {setRoleGuardian} who's allowed to cancel operations.
 * * A delay in which a role takes effect after being granted through {setGrantDelay}.
 * * A delay of any target's admin action via {setTargetAdminDelay}.
 * * A role label for discoverability purposes with {labelRole}.
 *
 * Any account can be added and removed into any number of these roles by using the {grantRole} and {revokeRole} functions
 * restricted to each role's admin (see {getRoleAdmin}).
 *
 * Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that
 * they will be highly secured (e.g., a multisig or a well-configured DAO).
 *
 * NOTE: This contract implements a form of the {IAuthority} interface, but {canCall} has additional return data so it
 * doesn't inherit `IAuthority`. It is however compatible with the `IAuthority` interface since the first 32 bytes of
 * the return data are a boolean as expected by that interface.
 *
 * NOTE: Systems that implement other access control mechanisms (for example using {Ownable}) can be paired with an
 * {AccessManager} by transferring permissions (ownership in the case of {Ownable}) directly to the {AccessManager}.
 * Users will be able to interact with these contracts through the {execute} function, following the access rules
 * registered in the {AccessManager}. Keep in mind that in that context, the msg.sender seen by restricted functions
 * will be {AccessManager} itself.
 *
 * WARNING: When granting permissions over an {Ownable} or {AccessControl} contract to an {AccessManager}, be very
 * mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
 * {{AccessControl-renounceRole}}.
 */
contract AccessManagerUpgradeable is Initializable, ContextUpgradeable, MulticallUpgradeable, IAccessManager {
    using Time for *;

    // Structure that stores the details for a target contract.
    struct TargetConfig {
        mapping(bytes4 selector => uint64 roleId) allowedRoles;
        Time.Delay adminDelay;
        bool closed;
    }

    // Structure that stores the details for a role/account pair. This structures fit into a single slot.
    struct Access {
        // Timepoint at which the user gets the permission.
        // If this is either 0 or in the future, then the role permission is not available.
        uint48 since;
        // Delay for execution. Only applies to restricted() / execute() calls.
        Time.Delay delay;
    }

    // Structure that stores the details of a role.
    struct Role {
        // Members of the role.
        mapping(address user => Access access) members;
        // Admin who can grant or revoke permissions.
        uint64 admin;
        // Guardian who can cancel operations targeting functions that need this role.
        uint64 guardian;
        // Delay in which the role takes effect after being granted.
        Time.Delay grantDelay;
    }

    // Structure that stores the details for a scheduled operation. This structure fits into a single slot.
    struct Schedule {
        // Moment at which the operation can be executed.
        uint48 timepoint;
        // Operation nonce to allow third-party contracts to identify the operation.
        uint32 nonce;
    }

    uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
    uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1

    /// @custom:storage-location erc7201:openzeppelin.storage.AccessManager
    struct AccessManagerStorage {
        mapping(address target => TargetConfig mode) _targets;
        mapping(uint64 roleId => Role) _roles;
        mapping(bytes32 operationId => Schedule) _schedules;

        // Used to identify operations that are currently being executed via {execute}.
        // This should be transient storage when supported by the EVM.
        bytes32 _executionId;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManager")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessManagerStorageLocation = 0x40c6c8c28789853c7efd823ab20824bbd71718a8a5915e855f6f288c9a26ad00;

    function _getAccessManagerStorage() private pure returns (AccessManagerStorage storage $) {
        assembly {
            $.slot := AccessManagerStorageLocation
        }
    }

    /**
     * @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
     * {_getAdminRestrictions}.
     */
    modifier onlyAuthorized() {
        _checkAuthorized();
        _;
    }

    function __AccessManager_init(address initialAdmin) internal onlyInitializing {
        __AccessManager_init_unchained(initialAdmin);
    }

    function __AccessManager_init_unchained(address initialAdmin) internal onlyInitializing {
        if (initialAdmin == address(0)) {
            revert AccessManagerInvalidInitialAdmin(address(0));
        }

        // admin is active immediately and without any execution delay.
        _grantRole(ADMIN_ROLE, initialAdmin, 0, 0);
    }

    // =================================================== GETTERS ====================================================
    /// @inheritdoc IAccessManager
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) public view virtual returns (bool immediate, uint32 delay) {
        if (isTargetClosed(target)) {
            return (false, 0);
        } else if (caller == address(this)) {
            // Caller is AccessManager, this means the call was sent through {execute} and it already checked
            // permissions. We verify that the call "identifier", which is set during {execute}, is correct.
            return (_isExecuting(target, selector), 0);
        } else {
            uint64 roleId = getTargetFunctionRole(target, selector);
            (bool isMember, uint32 currentDelay) = hasRole(roleId, caller);
            return isMember ? (currentDelay == 0, currentDelay) : (false, 0);
        }
    }

    /// @inheritdoc IAccessManager
    function expiration() public view virtual returns (uint32) {
        return 1 weeks;
    }

    /// @inheritdoc IAccessManager
    function minSetback() public view virtual returns (uint32) {
        return 5 days;
    }

    /// @inheritdoc IAccessManager
    function isTargetClosed(address target) public view virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].closed;
    }

    /// @inheritdoc IAccessManager
    function getTargetFunctionRole(address target, bytes4 selector) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].allowedRoles[selector];
    }

    /// @inheritdoc IAccessManager
    function getTargetAdminDelay(address target) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].adminDelay.get();
    }

    /// @inheritdoc IAccessManager
    function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].admin;
    }

    /// @inheritdoc IAccessManager
    function getRoleGuardian(uint64 roleId) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].guardian;
    }

    /// @inheritdoc IAccessManager
    function getRoleGrantDelay(uint64 roleId) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].grantDelay.get();
    }

    /// @inheritdoc IAccessManager
    function getAccess(
        uint64 roleId,
        address account
    ) public view virtual returns (uint48 since, uint32 currentDelay, uint32 pendingDelay, uint48 effect) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        Access storage access = $._roles[roleId].members[account];

        since = access.since;
        (currentDelay, pendingDelay, effect) = access.delay.getFull();

        return (since, currentDelay, pendingDelay, effect);
    }

    /// @inheritdoc IAccessManager
    function hasRole(
        uint64 roleId,
        address account
    ) public view virtual returns (bool isMember, uint32 executionDelay) {
        if (roleId == PUBLIC_ROLE) {
            return (true, 0);
        } else {
            (uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(roleId, account);
            return (hasRoleSince != 0 && hasRoleSince <= Time.timestamp(), currentDelay);
        }
    }

    // =============================================== ROLE MANAGEMENT ===============================================
    /// @inheritdoc IAccessManager
    function labelRole(uint64 roleId, string calldata label) public virtual onlyAuthorized {
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        emit RoleLabel(roleId, label);
    }

    /// @inheritdoc IAccessManager
    function grantRole(uint64 roleId, address account, uint32 executionDelay) public virtual onlyAuthorized {
        _grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
    }

    /// @inheritdoc IAccessManager
    function revokeRole(uint64 roleId, address account) public virtual onlyAuthorized {
        _revokeRole(roleId, account);
    }

    /// @inheritdoc IAccessManager
    function renounceRole(uint64 roleId, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessManagerBadConfirmation();
        }
        _revokeRole(roleId, callerConfirmation);
    }

    /// @inheritdoc IAccessManager
    function setRoleAdmin(uint64 roleId, uint64 admin) public virtual onlyAuthorized {
        _setRoleAdmin(roleId, admin);
    }

    /// @inheritdoc IAccessManager
    function setRoleGuardian(uint64 roleId, uint64 guardian) public virtual onlyAuthorized {
        _setRoleGuardian(roleId, guardian);
    }

    /// @inheritdoc IAccessManager
    function setGrantDelay(uint64 roleId, uint32 newDelay) public virtual onlyAuthorized {
        _setGrantDelay(roleId, newDelay);
    }

    /**
     * @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted.
     *
     * Emits a {RoleGranted} event.
     */
    function _grantRole(
        uint64 roleId,
        address account,
        uint32 grantDelay,
        uint32 executionDelay
    ) internal virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        bool newMember = $._roles[roleId].members[account].since == 0;
        uint48 since;

        if (newMember) {
            since = Time.timestamp() + grantDelay;
            $._roles[roleId].members[account] = Access({since: since, delay: executionDelay.toDelay()});
        } else {
            // No setback here. Value can be reset by doing revoke + grant, effectively allowing the admin to perform
            // any change to the execution delay within the duration of the role admin delay.
            ($._roles[roleId].members[account].delay, since) = $._roles[roleId].members[account].delay.withUpdate(
                executionDelay,
                0
            );
        }

        emit RoleGranted(roleId, account, executionDelay, since, newMember);
        return newMember;
    }

    /**
     * @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}.
     * Returns true if the role was previously granted.
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function _revokeRole(uint64 roleId, address account) internal virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        if ($._roles[roleId].members[account].since == 0) {
            return false;
        }

        delete $._roles[roleId].members[account];

        emit RoleRevoked(roleId, account);
        return true;
    }

    /**
     * @dev Internal version of {setRoleAdmin} without access control.
     *
     * Emits a {RoleAdminChanged} event.
     *
     * NOTE: Setting the admin role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
     * anyone to set grant or revoke such role.
     */
    function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        $._roles[roleId].admin = admin;

        emit RoleAdminChanged(roleId, admin);
    }

    /**
     * @dev Internal version of {setRoleGuardian} without access control.
     *
     * Emits a {RoleGuardianChanged} event.
     *
     * NOTE: Setting the guardian role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
     * anyone to cancel any scheduled operation for such role.
     */
    function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        $._roles[roleId].guardian = guardian;

        emit RoleGuardianChanged(roleId, guardian);
    }

    /**
     * @dev Internal version of {setGrantDelay} without access control.
     *
     * Emits a {RoleGrantDelayChanged} event.
     */
    function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        uint48 effect;
        ($._roles[roleId].grantDelay, effect) = $._roles[roleId].grantDelay.withUpdate(newDelay, minSetback());

        emit RoleGrantDelayChanged(roleId, newDelay, effect);
    }

    // ============================================= FUNCTION MANAGEMENT ==============================================
    /// @inheritdoc IAccessManager
    function setTargetFunctionRole(
        address target,
        bytes4[] calldata selectors,
        uint64 roleId
    ) public virtual onlyAuthorized {
        for (uint256 i = 0; i < selectors.length; ++i) {
            _setTargetFunctionRole(target, selectors[i], roleId);
        }
    }

    /**
     * @dev Internal version of {setTargetFunctionRole} without access control.
     *
     * Emits a {TargetFunctionRoleUpdated} event.
     */
    function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        $._targets[target].allowedRoles[selector] = roleId;
        emit TargetFunctionRoleUpdated(target, selector, roleId);
    }

    /// @inheritdoc IAccessManager
    function setTargetAdminDelay(address target, uint32 newDelay) public virtual onlyAuthorized {
        _setTargetAdminDelay(target, newDelay);
    }

    /**
     * @dev Internal version of {setTargetAdminDelay} without access control.
     *
     * Emits a {TargetAdminDelayUpdated} event.
     */
    function _setTargetAdminDelay(address target, uint32 newDelay) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 effect;
        ($._targets[target].adminDelay, effect) = $._targets[target].adminDelay.withUpdate(newDelay, minSetback());

        emit TargetAdminDelayUpdated(target, newDelay, effect);
    }

    // =============================================== MODE MANAGEMENT ================================================
    /// @inheritdoc IAccessManager
    function setTargetClosed(address target, bool closed) public virtual onlyAuthorized {
        _setTargetClosed(target, closed);
    }

    /**
     * @dev Set the closed flag for a contract. This is an internal setter with no access restrictions.
     *
     * Emits a {TargetClosed} event.
     */
    function _setTargetClosed(address target, bool closed) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (target == address(this)) {
            revert AccessManagerLockedAccount(target);
        }
        $._targets[target].closed = closed;
        emit TargetClosed(target, closed);
    }

    // ============================================== DELAYED OPERATIONS ==============================================
    /// @inheritdoc IAccessManager
    function getSchedule(bytes32 id) public view virtual returns (uint48) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 timepoint = $._schedules[id].timepoint;
        return _isExpired(timepoint) ? 0 : timepoint;
    }

    /// @inheritdoc IAccessManager
    function getNonce(bytes32 id) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._schedules[id].nonce;
    }

    /// @inheritdoc IAccessManager
    function schedule(
        address target,
        bytes calldata data,
        uint48 when
    ) public virtual returns (bytes32 operationId, uint32 nonce) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address caller = _msgSender();

        // Fetch restrictions that apply to the caller on the targeted function
        (, uint32 setback) = _canCallExtended(caller, target, data);

        uint48 minWhen = Time.timestamp() + setback;

        // if call with delay is not authorized, or if requested timing is too soon
        if (setback == 0 || (when > 0 && when < minWhen)) {
            revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
        }

        // Reuse variable due to stack too deep
        when = uint48(Math.max(when, minWhen)); // cast is safe: both inputs are uint48

        // If caller is authorised, schedule operation
        operationId = hashOperation(caller, target, data);

        _checkNotScheduled(operationId);

        unchecked {
            // It's not feasible to overflow the nonce in less than 1000 years
            nonce = $._schedules[operationId].nonce + 1;
        }
        $._schedules[operationId].timepoint = when;
        $._schedules[operationId].nonce = nonce;
        emit OperationScheduled(operationId, nonce, when, caller, target, data);

        // Using named return values because otherwise we get stack too deep
    }

    /**
     * @dev Reverts if the operation is currently scheduled and has not expired.
     * (Note: This function was introduced due to stack too deep errors in schedule.)
     */
    function _checkNotScheduled(bytes32 operationId) private view {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 prevTimepoint = $._schedules[operationId].timepoint;
        if (prevTimepoint != 0 && !_isExpired(prevTimepoint)) {
            revert AccessManagerAlreadyScheduled(operationId);
        }
    }

    /// @inheritdoc IAccessManager
    // Reentrancy is not an issue because permissions are checked on msg.sender. Additionally,
    // _consumeScheduledOp guarantees a scheduled operation is only executed once.
    // slither-disable-next-line reentrancy-no-eth
    function execute(address target, bytes calldata data) public payable virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address caller = _msgSender();

        // Fetch restrictions that apply to the caller on the targeted function
        (bool immediate, uint32 setback) = _canCallExtended(caller, target, data);

        // If caller is not authorised, revert
        if (!immediate && setback == 0) {
            revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
        }

        bytes32 operationId = hashOperation(caller, target, data);
        uint32 nonce;

        // If caller is authorised, check operation was scheduled early enough
        // Consume an available schedule even if there is no currently enforced delay
        if (setback != 0 || getSchedule(operationId) != 0) {
            nonce = _consumeScheduledOp(operationId);
        }

        // Mark the target and selector as authorised
        bytes32 executionIdBefore = $._executionId;
        $._executionId = _hashExecutionId(target, _checkSelector(data));

        // Perform call
        Address.functionCallWithValue(target, data, msg.value);

        // Reset execute identifier
        $._executionId = executionIdBefore;

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function cancel(address caller, address target, bytes calldata data) public virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address msgsender = _msgSender();
        bytes4 selector = _checkSelector(data);

        bytes32 operationId = hashOperation(caller, target, data);
        if ($._schedules[operationId].timepoint == 0) {
            revert AccessManagerNotScheduled(operationId);
        } else if (caller != msgsender) {
            // calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required role.
            (bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender);
            (bool isGuardian, ) = hasRole(getRoleGuardian(getTargetFunctionRole(target, selector)), msgsender);
            if (!isAdmin && !isGuardian) {
                revert AccessManagerUnauthorizedCancel(msgsender, caller, target, selector);
            }
        }

        delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
        uint32 nonce = $._schedules[operationId].nonce;
        emit OperationCanceled(operationId, nonce);

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function consumeScheduledOp(address caller, bytes calldata data) public virtual {
        address target = _msgSender();
        if (IAccessManaged(target).isConsumingScheduledOp() != IAccessManaged.isConsumingScheduledOp.selector) {
            revert AccessManagerUnauthorizedConsume(target);
        }
        _consumeScheduledOp(hashOperation(caller, target, data));
    }

    /**
     * @dev Internal variant of {consumeScheduledOp} that operates on bytes32 operationId.
     *
     * Returns the nonce of the scheduled operation that is consumed.
     */
    function _consumeScheduledOp(bytes32 operationId) internal virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 timepoint = $._schedules[operationId].timepoint;
        uint32 nonce = $._schedules[operationId].nonce;

        if (timepoint == 0) {
            revert AccessManagerNotScheduled(operationId);
        } else if (timepoint > Time.timestamp()) {
            revert AccessManagerNotReady(operationId);
        } else if (_isExpired(timepoint)) {
            revert AccessManagerExpired(operationId);
        }

        delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
        emit OperationExecuted(operationId, nonce);

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function hashOperation(address caller, address target, bytes calldata data) public view virtual returns (bytes32) {
        return keccak256(abi.encode(caller, target, data));
    }

    // ==================================================== OTHERS ====================================================
    /// @inheritdoc IAccessManager
    function updateAuthority(address target, address newAuthority) public virtual onlyAuthorized {
        IAccessManaged(target).setAuthority(newAuthority);
    }

    // ================================================= ADMIN LOGIC ==================================================
    /**
     * @dev Check if the current call is authorized according to admin logic.
     */
    function _checkAuthorized() private {
        address caller = _msgSender();
        (bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
        if (!immediate) {
            if (delay == 0) {
                (, uint64 requiredRole, ) = _getAdminRestrictions(_msgData());
                revert AccessManagerUnauthorizedAccount(caller, requiredRole);
            } else {
                _consumeScheduledOp(hashOperation(caller, address(this), _msgData()));
            }
        }
    }

    /**
     * @dev Get the admin restrictions of a given function call based on the function and arguments involved.
     *
     * Returns:
     * - bool restricted: does this data match a restricted operation
     * - uint64: which role is this operation restricted to
     * - uint32: minimum delay to enforce for that operation (max between operation's delay and admin's execution delay)
     */
    function _getAdminRestrictions(
        bytes calldata data
    ) private view returns (bool restricted, uint64 roleAdminId, uint32 executionDelay) {
        if (data.length < 4) {
            return (false, 0, 0);
        }

        bytes4 selector = _checkSelector(data);

        // Restricted to ADMIN with no delay beside any execution delay the caller may have
        if (
            selector == this.labelRole.selector ||
            selector == this.setRoleAdmin.selector ||
            selector == this.setRoleGuardian.selector ||
            selector == this.setGrantDelay.selector ||
            selector == this.setTargetAdminDelay.selector
        ) {
            return (true, ADMIN_ROLE, 0);
        }

        // Restricted to ADMIN with the admin delay corresponding to the target
        if (
            selector == this.updateAuthority.selector ||
            selector == this.setTargetClosed.selector ||
            selector == this.setTargetFunctionRole.selector
        ) {
            // First argument is a target.
            address target = abi.decode(data[0x04:0x24], (address));
            uint32 delay = getTargetAdminDelay(target);
            return (true, ADMIN_ROLE, delay);
        }

        // Restricted to that role's admin with no delay beside any execution delay the caller may have.
        if (selector == this.grantRole.selector || selector == this.revokeRole.selector) {
            // First argument is a roleId.
            uint64 roleId = abi.decode(data[0x04:0x24], (uint64));
            return (true, getRoleAdmin(roleId), 0);
        }

        return (false, 0, 0);
    }

    // =================================================== HELPERS ====================================================
    /**
     * @dev An extended version of {canCall} for internal usage that checks {_canCallSelf}
     * when the target is this contract.
     *
     * Returns:
     * - bool immediate: whether the operation can be executed immediately (with no delay)
     * - uint32 delay: the execution delay
     */
    function _canCallExtended(
        address caller,
        address target,
        bytes calldata data
    ) private view returns (bool immediate, uint32 delay) {
        if (target == address(this)) {
            return _canCallSelf(caller, data);
        } else {
            return data.length < 4 ? (false, 0) : canCall(caller, target, _checkSelector(data));
        }
    }

    /**
     * @dev A version of {canCall} that checks for admin restrictions in this contract.
     */
    function _canCallSelf(address caller, bytes calldata data) private view returns (bool immediate, uint32 delay) {
        if (data.length < 4) {
            return (false, 0);
        }

        if (caller == address(this)) {
            // Caller is AccessManager, this means the call was sent through {execute} and it already checked
            // permissions. We verify that the call "identifier", which is set during {execute}, is correct.
            return (_isExecuting(address(this), _checkSelector(data)), 0);
        }

        (bool enabled, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data);
        if (!enabled) {
            return (false, 0);
        }

        (bool inRole, uint32 executionDelay) = hasRole(roleId, caller);
        if (!inRole) {
            return (false, 0);
        }

        // downcast is safe because both options are uint32
        delay = uint32(Math.max(operationDelay, executionDelay));
        return (delay == 0, delay);
    }

    /**
     * @dev Returns true if a call with `target` and `selector` is being executed via {executed}.
     */
    function _isExecuting(address target, bytes4 selector) private view returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._executionId == _hashExecutionId(target, selector);
    }

    /**
     * @dev Returns true if a schedule timepoint is past its expiration deadline.
     */
    function _isExpired(uint48 timepoint) private view returns (bool) {
        return timepoint + expiration() <= Time.timestamp();
    }

    /**
     * @dev Extracts the selector from calldata. Panics if data is not at least 4 bytes
     */
    function _checkSelector(bytes calldata data) private pure returns (bytes4) {
        return bytes4(data[0:4]);
    }

    /**
     * @dev Hashing function for execute protection
     */
    function _hashExecutionId(address target, bytes4 selector) private pure returns (bytes32) {
        return keccak256(abi.encode(target, selector));
    }
}
OwnableUpgradeable.sol 119 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
    struct OwnableStorage {
        address _owner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        OwnableStorage storage $ = _getOwnableStorage();
        return $._owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        OwnableStorage storage $ = _getOwnableStorage();
        address oldOwner = $._owner;
        $._owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
UUPSUpgradeable.sol 153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.20;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC1967-compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
ContextUpgradeable.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    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;
    }
}
EIP712Upgradeable.sol 210 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.20;

import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
 * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
 * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
 * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 */
abstract contract EIP712Upgradeable is Initializable, IERC5267 {
    bytes32 private constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /// @custom:storage-location erc7201:openzeppelin.storage.EIP712
    struct EIP712Storage {
        /// @custom:oz-renamed-from _HASHED_NAME
        bytes32 _hashedName;
        /// @custom:oz-renamed-from _HASHED_VERSION
        bytes32 _hashedVersion;

        string _name;
        string _version;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;

    function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
        assembly {
            $.slot := EIP712StorageLocation
        }
    }

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        EIP712Storage storage $ = _getEIP712Storage();
        $._name = name;
        $._version = version;

        // Reset prior values in storage if upgrading
        $._hashedName = 0;
        $._hashedVersion = 0;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator();
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {IERC-5267}.
     */
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        EIP712Storage storage $ = _getEIP712Storage();
        // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
        // and the EIP712 domain is not reliable, as it will be missing name and version.
        require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");

        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Name() internal view virtual returns (string memory) {
        EIP712Storage storage $ = _getEIP712Storage();
        return $._name;
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Version() internal view virtual returns (string memory) {
        EIP712Storage storage $ = _getEIP712Storage();
        return $._version;
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
     */
    function _EIP712NameHash() internal view returns (bytes32) {
        EIP712Storage storage $ = _getEIP712Storage();
        string memory name = _EIP712Name();
        if (bytes(name).length > 0) {
            return keccak256(bytes(name));
        } else {
            // If the name is empty, the contract may have been upgraded without initializing the new storage.
            // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
            bytes32 hashedName = $._hashedName;
            if (hashedName != 0) {
                return hashedName;
            } else {
                return keccak256("");
            }
        }
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
     */
    function _EIP712VersionHash() internal view returns (bytes32) {
        EIP712Storage storage $ = _getEIP712Storage();
        string memory version = _EIP712Version();
        if (bytes(version).length > 0) {
            return keccak256(bytes(version));
        } else {
            // If the version is empty, the contract may have been upgraded without initializing the new storage.
            // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
            bytes32 hashedVersion = $._hashedVersion;
            if (hashedVersion != 0) {
                return hashedVersion;
            } else {
                return keccak256("");
            }
        }
    }
}
MulticallUpgradeable.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)

pragma solidity ^0.8.20;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "./ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 MulticallUpgradeable is Initializable, ContextUpgradeable {
    function __Multicall_init() internal onlyInitializing {
    }

    function __Multicall_init_unchained() internal onlyInitializing {
    }
    /**
     * @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;
    }
}
ReentrancyGuardUpgradeable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
        assembly {
            $.slot := ReentrancyGuardStorageLocation
        }
    }

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if ($._status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        $._status = ENTERED;
    }

    function _nonReentrantAfter() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        return $._status == ENTERED;
    }
}
IAccessManaged.sol 32 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)

pragma solidity ^0.8.20;

interface IAccessManaged {
    /**
     * @dev Authority that manages this contract was updated.
     */
    event AuthorityUpdated(address authority);

    error AccessManagedUnauthorized(address caller);
    error AccessManagedRequiredDelay(address caller, uint32 delay);
    error AccessManagedInvalidAuthority(address authority);

    /**
     * @dev Returns the current authority.
     */
    function authority() external view returns (address);

    /**
     * @dev Transfers control to a new authority. The caller must be the current authority.
     */
    function setAuthority(address) external;

    /**
     * @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
     * being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
     * attacker controlled calls.
     */
    function isConsumingScheduledOp() external view returns (bytes4);
}
IAccessManager.sol 392 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManager.sol)

pragma solidity ^0.8.20;

import {IAccessManaged} from "./IAccessManaged.sol";
import {Time} from "../../utils/types/Time.sol";

interface IAccessManager {
    /**
     * @dev A delayed operation was scheduled.
     */
    event OperationScheduled(
        bytes32 indexed operationId,
        uint32 indexed nonce,
        uint48 schedule,
        address caller,
        address target,
        bytes data
    );

    /**
     * @dev A scheduled operation was executed.
     */
    event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev A scheduled operation was canceled.
     */
    event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev Informational labelling for a roleId.
     */
    event RoleLabel(uint64 indexed roleId, string label);

    /**
     * @dev Emitted when `account` is granted `roleId`.
     *
     * NOTE: The meaning of the `since` argument depends on the `newMember` argument.
     * If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
     * otherwise it indicates the execution delay for this account and roleId is updated.
     */
    event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);

    /**
     * @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
     */
    event RoleRevoked(uint64 indexed roleId, address indexed account);

    /**
     * @dev Role acting as admin over a given `roleId` is updated.
     */
    event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);

    /**
     * @dev Role acting as guardian over a given `roleId` is updated.
     */
    event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);

    /**
     * @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
     */
    event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);

    /**
     * @dev Target mode is updated (true = closed, false = open).
     */
    event TargetClosed(address indexed target, bool closed);

    /**
     * @dev Role required to invoke `selector` on `target` is updated to `roleId`.
     */
    event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);

    /**
     * @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
     */
    event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);

    error AccessManagerAlreadyScheduled(bytes32 operationId);
    error AccessManagerNotScheduled(bytes32 operationId);
    error AccessManagerNotReady(bytes32 operationId);
    error AccessManagerExpired(bytes32 operationId);
    error AccessManagerLockedAccount(address account);
    error AccessManagerLockedRole(uint64 roleId);
    error AccessManagerBadConfirmation();
    error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
    error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
    error AccessManagerUnauthorizedConsume(address target);
    error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
    error AccessManagerInvalidInitialAdmin(address initialAdmin);

    /**
     * @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
     * no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
     * & {execute} workflow.
     *
     * This function is usually called by the targeted contract to control immediate execution of restricted functions.
     * Therefore we only return true if the call can be performed without any delay. If the call is subject to a
     * previously set delay (not zero), then the function should return false and the caller should schedule the operation
     * for future execution.
     *
     * If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
     * the operation can be executed if and only if delay is greater than 0.
     *
     * NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
     * is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
     * to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
     *
     * NOTE: This function does not report the permissions of this manager itself. These are defined by the
     * {_canCallSelf} function instead.
     */
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) external view returns (bool allowed, uint32 delay);

    /**
     * @dev Expiration delay for scheduled proposals. Defaults to 1 week.
     *
     * IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
     * disabling any scheduling usage.
     */
    function expiration() external view returns (uint32);

    /**
     * @dev Minimum setback for all delay updates, with the exception of execution delays. It
     * can be increased without setback (and reset via {revokeRole} in the case event of an
     * accidental increase). Defaults to 5 days.
     */
    function minSetback() external view returns (uint32);

    /**
     * @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
     */
    function isTargetClosed(address target) external view returns (bool);

    /**
     * @dev Get the role required to call a function.
     */
    function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);

    /**
     * @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
     */
    function getTargetAdminDelay(address target) external view returns (uint32);

    /**
     * @dev Get the id of the role that acts as an admin for the given role.
     *
     * The admin permission is required to grant the role, revoke the role and update the execution delay to execute
     * an operation that is restricted to this role.
     */
    function getRoleAdmin(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role that acts as a guardian for a given role.
     *
     * The guardian permission allows canceling operations that have been scheduled under the role.
     */
    function getRoleGuardian(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role current grant delay.
     *
     * Its value may change at any point without an event emitted following a call to {setGrantDelay}.
     * Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
     */
    function getRoleGrantDelay(uint64 roleId) external view returns (uint32);

    /**
     * @dev Get the access details for a given account for a given role. These details include the timepoint at which
     * membership becomes active, and the delay applied to all operation by this user that requires this permission
     * level.
     *
     * Returns:
     * [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
     * [1] Current execution delay for the account.
     * [2] Pending execution delay for the account.
     * [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
     */
    function getAccess(uint64 roleId, address account) external view returns (uint48, uint32, uint32, uint48);

    /**
     * @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
     * permission might be associated with an execution delay. {getAccess} can provide more details.
     */
    function hasRole(uint64 roleId, address account) external view returns (bool, uint32);

    /**
     * @dev Give a label to a role, for improved role discoverability by UIs.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleLabel} event.
     */
    function labelRole(uint64 roleId, string calldata label) external;

    /**
     * @dev Add `account` to `roleId`, or change its execution delay.
     *
     * This gives the account the authorization to call any function that is restricted to this role. An optional
     * execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
     * that is restricted to members of this role. The user will only be able to execute the operation after the delay has
     * passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
     *
     * If the account has already been granted this role, the execution delay will be updated. This update is not
     * immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
     * called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
     * operation executed in the 3 hours that follows this update was indeed scheduled before this update.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - granted role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleGranted} event.
     */
    function grantRole(uint64 roleId, address account, uint32 executionDelay) external;

    /**
     * @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
     * no effect.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - revoked role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function revokeRole(uint64 roleId, address account) external;

    /**
     * @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
     * the role this call has no effect.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function renounceRole(uint64 roleId, address callerConfirmation) external;

    /**
     * @dev Change admin role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleAdminChanged} event
     */
    function setRoleAdmin(uint64 roleId, uint64 admin) external;

    /**
     * @dev Change guardian role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGuardianChanged} event
     */
    function setRoleGuardian(uint64 roleId, uint64 guardian) external;

    /**
     * @dev Update the delay for granting a `roleId`.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGrantDelayChanged} event.
     */
    function setGrantDelay(uint64 roleId, uint32 newDelay) external;

    /**
     * @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetFunctionRoleUpdated} event per selector.
     */
    function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;

    /**
     * @dev Set the delay for changing the configuration of a given target contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetAdminDelayUpdated} event.
     */
    function setTargetAdminDelay(address target, uint32 newDelay) external;

    /**
     * @dev Set the closed flag for a contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetClosed} event.
     */
    function setTargetClosed(address target, bool closed) external;

    /**
     * @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
     * operation is not yet scheduled, has expired, was executed, or was canceled.
     */
    function getSchedule(bytes32 id) external view returns (uint48);

    /**
     * @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
     * been scheduled.
     */
    function getNonce(bytes32 id) external view returns (uint32);

    /**
     * @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
     * choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
     * required for the caller. The special value zero will automatically set the earliest possible time.
     *
     * Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
     * the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
     * scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
     *
     * Emits a {OperationScheduled} event.
     *
     * NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
     * this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
     * contract if it is using standard Solidity ABI encoding.
     */
    function schedule(address target, bytes calldata data, uint48 when) external returns (bytes32, uint32);

    /**
     * @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
     * execution delay is 0.
     *
     * Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
     * operation wasn't previously scheduled (if the caller doesn't have an execution delay).
     *
     * Emits an {OperationExecuted} event only if the call was scheduled and delayed.
     */
    function execute(address target, bytes calldata data) external payable returns (uint32);

    /**
     * @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
     * operation that is cancelled.
     *
     * Requirements:
     *
     * - the caller must be the proposer, a guardian of the targeted function, or a global admin
     *
     * Emits a {OperationCanceled} event.
     */
    function cancel(address caller, address target, bytes calldata data) external returns (uint32);

    /**
     * @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
     * (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
     *
     * This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
     * with all the verifications that it implies.
     *
     * Emit a {OperationExecuted} event.
     */
    function consumeScheduledOp(address caller, bytes calldata data) external;

    /**
     * @dev Hashing function for delayed operations.
     */
    function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);

    /**
     * @dev Changes the authority of a target managed by this manager instance.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     */
    function updateAuthority(address target, address newAuthority) external;
}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}
IERC1271.sol 17 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
IERC4626.sol 230 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}
ERC1967Proxy.sol 40 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.20;

import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "./ERC1967Utils.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
     * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
     *
     * Requirements:
     *
     * - If `data` is empty, `msg.value` must be zero.
     */
    constructor(address implementation, bytes memory _data) payable {
        ERC1967Utils.upgradeToAndCall(implementation, _data);
    }

    /**
     * @dev Returns the current implementation address.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function _implementation() internal view virtual override returns (address) {
        return ERC1967Utils.getImplementation();
    }
}
ERC1967Utils.sol 193 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.20;

import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 */
library ERC1967Utils {
    // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
    // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}
Proxy.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)

pragma solidity ^0.8.20;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
     * function and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

    /**
     * @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 AddressInsufficientBalance(address(this));
        }

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

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
MessageHashUtils.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}
Math.sol 415 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
SafeCast.sol 1153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

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

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

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

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}
StorageSlot.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}
Strings.sol 94 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}
Time.sol 130 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev This library provides helpers for manipulating time-related objects.
 *
 * It uses the following types:
 * - `uint48` for timepoints
 * - `uint32` for durations
 *
 * While the library doesn't provide specific types for timepoints and duration, it does provide:
 * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
 * - additional helper functions
 */
library Time {
    using Time for *;

    /**
     * @dev Get the block timestamp as a Timepoint.
     */
    function timestamp() internal view returns (uint48) {
        return SafeCast.toUint48(block.timestamp);
    }

    /**
     * @dev Get the block number as a Timepoint.
     */
    function blockNumber() internal view returns (uint48) {
        return SafeCast.toUint48(block.number);
    }

    // ==================================================== Delay =====================================================
    /**
     * @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
     * future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
     * This allows updating the delay applied to some operation while keeping some guarantees.
     *
     * In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
     * some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
     * the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
     * still apply for some time.
     *
     *
     * The `Delay` type is 112 bits long, and packs the following:
     *
     * ```
     *   | [uint48]: effect date (timepoint)
     *   |           | [uint32]: value before (duration)
     *   ↓           ↓       ↓ [uint32]: value after (duration)
     * 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
     * ```
     *
     * NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
     * supported.
     */
    type Delay is uint112;

    /**
     * @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
     */
    function toDelay(uint32 duration) internal pure returns (Delay) {
        return Delay.wrap(duration);
    }

    /**
     * @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
     * change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
     */
    function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) {
        (uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack();
        return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
    }

    /**
     * @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
     * effect timepoint is 0, then the pending value should not be considered.
     */
    function getFull(Delay self) internal view returns (uint32, uint32, uint48) {
        return _getFullAt(self, timestamp());
    }

    /**
     * @dev Get the current value.
     */
    function get(Delay self) internal view returns (uint32) {
        (uint32 delay, , ) = self.getFull();
        return delay;
    }

    /**
     * @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
     * enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
     * new delay becomes effective.
     */
    function withUpdate(
        Delay self,
        uint32 newValue,
        uint32 minSetback
    ) internal view returns (Delay updatedDelay, uint48 effect) {
        uint32 value = self.get();
        uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
        effect = timestamp() + setback;
        return (pack(value, newValue, effect), effect);
    }

    /**
     * @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
     */
    function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
        uint112 raw = Delay.unwrap(self);

        valueAfter = uint32(raw);
        valueBefore = uint32(raw >> 32);
        effect = uint48(raw >> 64);

        return (valueBefore, valueAfter, effect);
    }

    /**
     * @dev pack the components into a Delay object.
     */
    function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
        return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
    }
}
IAddressProvider.sol 49 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "../vaults/IWasabiVault.sol";
import "../router/IWasabiRouter.sol";
import "../util/IPartnerFeeManager.sol";

interface IAddressProvider {
    error InvalidAddress();
    error InvalidLiquidationFee();

    /// @dev Returns the Wasabi router
    function wasabiRouter() external view returns (IWasabiRouter);

    /// @dev Returns the fee receiver address
    function feeReceiver() external view returns (address);

    /// @dev Returns the WETH address
    function wethAddress() external view returns (address);

     /// @dev Returns the fee receiver address
    function liquidationFeeReceiver() external view returns (address);

    /// @dev Returns the staking account factory address
    function stakingAccountFactory() external view returns (address);

    /// @dev Returns the partner fee manager
    function partnerFeeManager() external view returns (IPartnerFeeManager);

    /// @dev sets the Wasabi router
    /// @param _wasabiRouter the Wasabi router
    function setWasabiRouter(IWasabiRouter _wasabiRouter) external;

    /// @dev sets the fee receiver
    /// @param _feeReceiver the fee receiver
    function setFeeReceiver(address _feeReceiver) external;

    /// @dev sets the liquidation fee receiver
    /// @param _liquidationFeeReceiver the fee receiver
    function setLiquidationFeeReceiver(address _liquidationFeeReceiver) external;

    /// @dev sets the staking account factory
    /// @param _stakingAccountFactory the staking account factory
    function setStakingAccountFactory(address _stakingAccountFactory) external;

    /// @dev sets the partner fee manager
    /// @param _partnerFeeManager the partner fee manager
    function setPartnerFeeManager(address _partnerFeeManager) external;
}
IDebtController.sol 93 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IDebtController {
    error InvalidValue();
    error IdenticalAddresses();
    error ZeroAddress();
    error PrincipalTooHigh(); // 0xd7cdb444

    event MaxLeverageChanged(address tokenA, address tokenB, uint256 maxLeverage);

    event LiquidationThresholdChanged(address tokenA, address tokenB, uint256 liquidationThresholdBps);

    struct TokenPair {
        address tokenA;
        address tokenB;
    }

    /// @dev Returns the maximum apy
    /// @notice The maximum apy is a percentage, e.g. 300% APY = 300
    function maxApy() external view returns (uint256);

    /// @dev Returns the liquidation fee bps
    function liquidationFeeBps() external view returns (uint256);

    /// @dev Computes the maximum interest
    /// @param _tokenAddress the token address
    /// @param _principal the principal borrowed
    /// @param _lastFundingTimestamp the timestamp where the loan was last funded
    /// @return maxInterest the maximum interest amount to pay for the loan
    function computeMaxInterest(
        address _tokenAddress,
        uint256 _principal,
        uint256 _lastFundingTimestamp
    ) external view returns(uint256 maxInterest);

    /// @dev Computes the maximum principal
    /// @param _collateralToken the collateral token address
    /// @param _principalToken the principal token address
    /// @param _downPayment the down payment the trader is paying
    /// @return maxPrincipal the maximum principal allowed to be borrowed
    function computeMaxPrincipal(
        address _collateralToken,
        address _principalToken,
        uint256 _downPayment
    ) external view returns (uint256 maxPrincipal);

    /// @dev Validates that the leverage is within the maximum allowed by the DebtController
    /// @param _downPayment The down payment amount
    /// @param _total The total value of the position in the same currency as the down payment
    /// @param _collateralToken The collateral token address
    /// @param _principalToken The principal token address
    /// @notice For shorts, _total is the collateral amount, for longs it is the down payment + principal
    function checkMaxLeverage(uint256 _downPayment, uint256 _total, address _collateralToken, address _principalToken) external view;

    /// @dev Returns the maximum leverage for a given token pair
    /// @notice The maximum leverage is a percentage, e.g. 3x leverage = 300
    /// @param _tokenA the token A address
    /// @param _tokenB the token B address
    /// @return maxLeverage the maximum leverage
    function getMaxLeverage(address _tokenA, address _tokenB) external view returns (uint256);

    /// @dev Returns the liquidation fee for a given down payment
    function getLiquidationFee(uint256 _downPayment, address, address) external view returns (uint256);

    /// @dev Returns the liquidation threshold bps for a given token pair
    function getLiquidationThresholdBps(address _tokenA, address _tokenB) external view returns (uint256);

    /// @dev Returns the liquidation threshold for a given token pair and principal amount
    /// @param _tokenA the token A address
    /// @param _tokenB the token B address
    /// @param _size the size of the position
    /// @return the liquidation threshold
    function getLiquidationThreshold(address _tokenA, address _tokenB, uint256 _size) external view returns (uint256);

    /// @dev sets the maximum leverage
    /// @param _tokenPairs the token pairs
    /// @param _maxLeverages the max leverage for each token pair
    function setMaxLeverage(TokenPair[] memory _tokenPairs, uint256[] memory _maxLeverages) external;

    /// @dev sets the maximum apy
    /// @param _maxApy the max APY 
    function setMaxAPY(uint256 _maxApy) external;

    /// @dev sets the liquidation fee bps
    /// @param _liquidationFeeBps the liquidation fee bps
    function setLiquidationFeeBps(uint256 _liquidationFeeBps) external;

    /// @dev sets the liquidation threshold bps for a given token pair
    /// @param _tokenPairs the token pairs
    /// @param _liquidationThresholdBps the liquidation threshold bps for each token pair
    function setLiquidationThresholdBps(TokenPair[] memory _tokenPairs, uint256[] memory _liquidationThresholdBps) external;
}
IPerpManager.sol 45 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IPerpManager {
    error AlreadyMigrated();
    error InvalidLength();

    event AuthorizedSignerChanged(
        address indexed trader,
        address indexed signer,
        bool isAuthorized
    );

    /// @dev check if account is admin and revert if not
    function isAdmin(address account) external view;

    /// @dev check if account has the given role and revert if not
    /// @param roleId role id
    /// @param account account address
    function checkRole(uint64 roleId, address account) external view;

    /// @notice check if a signer is authorized to sign for a trader
    /// @param trader address of the account to sign on behalf of
    /// @param signer address of the signer
    /// @return isAuthorized true if the signer is authorized to sign for the trader, false otherwise
    function isAuthorizedSigner(address trader, address signer) external view returns (bool);

    /// @notice Authorize or deauthorize a signer for a trader
    /// @param signer address of the signer to authorize or deauthorize
    /// @param isAuthorized true to authorize the signer, false to deauthorize
    function setAuthorizedSigner(address signer, bool isAuthorized) external;

    /// @notice Deploy a new vault and add it to the short pool
    /// @dev This contract must be granted the ADMIN_ROLE first
    /// @param implementation The implementation address
    /// @param data The data for the initialize function
    function deployVault(address implementation, bytes calldata data) external returns (address);

    /// @notice Upgrade multiple vaults to a new implementation in a single call
    /// @dev This contract must be granted the ADMIN_ROLE first
    /// @param newImplementation The new implementation address
    /// @param vaults The vaults to upgrade
    /// @param calls The call data for each vault (for upgradeToAndCall)
    function upgradeVaults(address newImplementation, address[] calldata vaults, bytes[] calldata calls) external;
}
PerpManager.sol 338 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/manager/AccessManagerUpgradeable.sol";

import "./IPerpManager.sol";
import "./Roles.sol";
import "./IAddressProvider.sol";
import "./IDebtController.sol";
import "../vaults/IWasabiVault.sol";
import "../router/IWasabiRouter.sol";

contract PerpManager is UUPSUpgradeable, AccessManagerUpgradeable, IPerpManager, IAddressProvider, IDebtController {
    uint256 public constant LEVERAGE_DENOMINATOR = 100;
    uint256 public constant APY_DENOMINATOR = 100;
    uint256 public constant LIQUIDATION_THRESHOLD_DENOMINATOR = 10000;
    uint256 public constant DEFAULT_LIQUIDATION_THRESHOLD_BPS = 500; // 5%
    uint256 public constant DEFAULT_MAX_LEVERAGE = 510; // 5.1x Leverage

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       State Variables                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
    mapping(address trader => mapping(address signer => bool isAuthorized)) private _isAuthorizedSigner;
    mapping(address token0 => mapping(address token1 => uint256 liquidationThresholdBps)) private _liquidationThreshold;
    mapping(address token0 => mapping(address token1 => uint256 maxLeverage)) private _maxLeveragePerPair;

    // IAddressProvider state
    /// @inheritdoc IAddressProvider
    IWasabiRouter public wasabiRouter;
    /// @inheritdoc IAddressProvider
    address public feeReceiver;
    /// @inheritdoc IAddressProvider
    address public wethAddress;
    /// @inheritdoc IAddressProvider
    address public liquidationFeeReceiver;
    /// @inheritdoc IAddressProvider
    address public stakingAccountFactory;
    /// @inheritdoc IAddressProvider
    IPartnerFeeManager public partnerFeeManager;

    // IDebtController state
    /// @inheritdoc IDebtController
    uint256 public maxApy;
    /// @inheritdoc IDebtController
    uint256 public liquidationFeeBps;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         Modifiers                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    modifier onlyAdmin() {
        isAdmin(msg.sender);
        _;
    }

    modifier onlyVaultAdmin() {
        checkRole(Roles.VAULT_ADMIN_ROLE, msg.sender);
        _;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       Initialization                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @dev Initializer for proxy
    /// @param _wasabiRouter The WasabiRouter contract
    /// @param _feeReceiver The fee receiver address
    /// @param _wethAddress The WETH address
    /// @param _liquidationFeeReceiver The liquidation fee receiver address
    /// @param _stakingAccountFactory The StakingAccountFactory contract
    /// @param _partnerFeeManager The PartnerFeeManager contract
    /// @param _maxApy The maximum APY
    function initialize(
        IWasabiRouter _wasabiRouter,
        address _feeReceiver,
        address _wethAddress,
        address _liquidationFeeReceiver,
        address _stakingAccountFactory,
        IPartnerFeeManager _partnerFeeManager,
        uint256 _maxApy
    ) public virtual initializer {
        __PerpManager_init(_wasabiRouter, _feeReceiver, _wethAddress, _liquidationFeeReceiver, _stakingAccountFactory, _partnerFeeManager, _maxApy);
    }

    function __PerpManager_init(
        IWasabiRouter _wasabiRouter,
        address _feeReceiver,
        address _wethAddress,
        address _liquidationFeeReceiver,
        address _stakingAccountFactory,
        IPartnerFeeManager _partnerFeeManager,
        uint256 _maxApy
    ) internal onlyInitializing {
        __AccessManager_init(msg.sender);
        wasabiRouter = _wasabiRouter;
        feeReceiver = _feeReceiver;
        wethAddress = _wethAddress;
        liquidationFeeReceiver = _liquidationFeeReceiver;
        stakingAccountFactory = _stakingAccountFactory;
        partnerFeeManager = _partnerFeeManager;
        maxApy = _maxApy;
        liquidationFeeBps = 500; // 5%
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     IPerpManager Views                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IPerpManager
    function isAdmin(address account) public view {
        checkRole(ADMIN_ROLE, account);
    }

    /// @inheritdoc IPerpManager
    function checkRole(uint64 roleId, address account) public view {
        (bool hasRole, ) = hasRole(roleId, account);
        if (!hasRole) revert AccessManagerUnauthorizedAccount(account, roleId);
    }

    /// @inheritdoc IPerpManager
    function isAuthorizedSigner(address trader, address signer) public view returns (bool) {
        return _isAuthorizedSigner[trader][signer];
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    IDebtController Views                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IDebtController
    function computeMaxInterest(
        address,
        uint256 _principal,
        uint256 _lastFundingTimestamp
    ) public view returns(uint256 maxInterestToPay) {
        uint256 secondsSince = block.timestamp - _lastFundingTimestamp;
        maxInterestToPay = _principal * maxApy * secondsSince / (APY_DENOMINATOR * (365 days));
    }

    /// @inheritdoc IDebtController
    function computeMaxPrincipal(
        address _collateralToken,
        address _principalToken,
        uint256 _downPayment
    ) external view returns (uint256 maxPrincipal) {
        uint256 maxLeverage = getMaxLeverage(_collateralToken, _principalToken);
        maxPrincipal = _downPayment * (maxLeverage - LEVERAGE_DENOMINATOR) / LEVERAGE_DENOMINATOR;
    }

    /// @inheritdoc IDebtController
    function checkMaxLeverage(
        uint256 _downPayment,
        uint256 _total,
        address _collateralToken,
        address _principalToken
    ) external view {
        if (_total * LEVERAGE_DENOMINATOR > getMaxLeverage(_collateralToken, _principalToken) * _downPayment) {
            revert PrincipalTooHigh();
        }
    }

    /// @inheritdoc IDebtController
    function getMaxLeverage(address _tokenA, address _tokenB) public view returns (uint256) {
        (address token0, address token1) = _sortTokens(_tokenA, _tokenB);
        uint256 maxLeverage = _maxLeveragePerPair[token0][token1];
        if (maxLeverage == 0) {
            maxLeverage = DEFAULT_MAX_LEVERAGE;
        }
        return maxLeverage;
    }

    /// @inheritdoc IDebtController
    function getLiquidationFee(uint256 _downPayment, address, address) external view returns (uint256) {
        return (_downPayment * liquidationFeeBps) / 10000;
    }

    /// @inheritdoc IDebtController
    function getLiquidationThresholdBps(address _tokenA, address _tokenB) public view returns (uint256) {
        (address token0, address token1) = _sortTokens(_tokenA, _tokenB);
        uint256 liquidationThresholdBps = _liquidationThreshold[token0][token1];
        if (liquidationThresholdBps == 0) {
            liquidationThresholdBps = DEFAULT_LIQUIDATION_THRESHOLD_BPS;
        }
        return liquidationThresholdBps;
    }

    /// @inheritdoc IDebtController
    function getLiquidationThreshold(address _tokenA, address _tokenB, uint256 _size) external view returns (uint256) {
        uint256 liquidationThresholdBps = getLiquidationThresholdBps(_tokenA, _tokenB);
        return _size * liquidationThresholdBps / LIQUIDATION_THRESHOLD_DENOMINATOR;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     IPerpManager Writes                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IPerpManager
    function setAuthorizedSigner(address signer, bool isAuthorized) public {
        _isAuthorizedSigner[msg.sender][signer] = isAuthorized;
        emit AuthorizedSignerChanged(msg.sender, signer, isAuthorized);
    }

    /// @inheritdoc IPerpManager
    function deployVault(address implementation, bytes calldata data) external onlyVaultAdmin returns (address) {
        address vault = address(new ERC1967Proxy(implementation, data));
        wasabiRouter.shortPool().addVault(IWasabiVault(vault));
        return vault;
    }

    /// @inheritdoc IPerpManager
    function upgradeVaults(address newImplementation, address[] calldata vaults, bytes[] calldata calls) external onlyAdmin {
        uint256 vaultsLength = vaults.length;
        bool hasCalls = calls.length != 0;
        if (vaultsLength != calls.length && hasCalls) revert InvalidLength();
        for (uint256 i; i < vaultsLength; ) {
            UUPSUpgradeable(vaults[i]).upgradeToAndCall(newImplementation, hasCalls ? calls[i] : bytes(""));
            unchecked {
                ++i;
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   IAddressProvider Writes                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IAddressProvider
    function setWasabiRouter(IWasabiRouter _wasabiRouter) external onlyAdmin {
        wasabiRouter = _wasabiRouter;
    }

    /// @inheritdoc IAddressProvider
    function setFeeReceiver(address _feeReceiver) external onlyAdmin {
        if (_feeReceiver == address(0)) revert InvalidAddress();
        feeReceiver = _feeReceiver;
    }

    /// @inheritdoc IAddressProvider
    function setLiquidationFeeReceiver(address _liquidationFeeReceiver) external onlyAdmin {
        if (_liquidationFeeReceiver == address(0)) revert InvalidAddress();
        liquidationFeeReceiver = _liquidationFeeReceiver;
    }

    /// @inheritdoc IAddressProvider
    function setStakingAccountFactory(address _stakingAccountFactory) external onlyAdmin {
        if (_stakingAccountFactory == address(0)) revert InvalidAddress();
        stakingAccountFactory = _stakingAccountFactory;
    }

    /// @inheritdoc IAddressProvider
    function setPartnerFeeManager(address _partnerFeeManager) external onlyAdmin {
        if (_partnerFeeManager == address(0)) revert InvalidAddress();
        partnerFeeManager = IPartnerFeeManager(_partnerFeeManager);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   IDebtController Writes                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IDebtController
    function setMaxLeverage(TokenPair[] memory _tokenPairs, uint256[] memory _maxLeverages) external onlyAdmin {
        uint256 tokenPairsLength = _tokenPairs.length;
        if (tokenPairsLength != _maxLeverages.length) revert InvalidLength();
        for (uint256 i; i < tokenPairsLength; ) {
            TokenPair memory tokenPair = _tokenPairs[i];
            uint256 maxLeverage = _maxLeverages[i];
            (address token0, address token1) = _sortTokens(tokenPair.tokenA, tokenPair.tokenB);

            if (token0 == address(0) || token1 == address(0)) revert InvalidAddress();
            if (maxLeverage == 0) revert InvalidValue();
            if (maxLeverage > 100 * LEVERAGE_DENOMINATOR) revert InvalidValue(); // 100x leverage

            _maxLeveragePerPair[token0][token1] = maxLeverage;
            emit MaxLeverageChanged(token0, token1, maxLeverage);
            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IDebtController
    function setMaxAPY(uint256 _maxApy) external onlyAdmin {
        if (_maxApy == 0) revert InvalidValue();
        if (_maxApy > 1000 * APY_DENOMINATOR) revert InvalidValue(); // 1000% APR
        maxApy = _maxApy;
    }

    /// @inheritdoc IDebtController
    function setLiquidationFeeBps(uint256 _liquidationFeeBps) external onlyAdmin {
        if (_liquidationFeeBps == 0) revert InvalidValue();
        if (_liquidationFeeBps > 1000) revert InvalidValue(); // 10%
        liquidationFeeBps = _liquidationFeeBps;
    }

    /// @inheritdoc IDebtController
    function setLiquidationThresholdBps(TokenPair[] memory _tokenPairs, uint256[] memory _liquidationThresholdBps) external onlyAdmin {
        uint256 tokenPairsLength = _tokenPairs.length;
        if (tokenPairsLength != _liquidationThresholdBps.length) revert InvalidLength();
        for (uint256 i; i < tokenPairsLength; ) {
            TokenPair memory tokenPair = _tokenPairs[i];
            uint256 liquidationThresholdBps = _liquidationThresholdBps[i];
            (address token0, address token1) = _sortTokens(tokenPair.tokenA, tokenPair.tokenB);

            if (liquidationThresholdBps == 0) revert InvalidValue();
            if (liquidationThresholdBps > LIQUIDATION_THRESHOLD_DENOMINATOR) revert InvalidValue(); // 100%
            
            _liquidationThreshold[token0][token1] = liquidationThresholdBps;
            emit LiquidationThresholdChanged(token0, token1, liquidationThresholdBps);
            unchecked {
                ++i;
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     Internal Functions                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc UUPSUpgradeable
    function _authorizeUpgrade(address) internal view override onlyAdmin {}

    /// @dev Sorts two token addresses Uniswap style
    function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        if (tokenA == tokenB) revert IdenticalAddresses();
        (token0, token1) = uint160(tokenA) < uint160(tokenB)
            ? (tokenA, tokenB)
            : (tokenB, tokenA);
        if (token0 == address(0)) revert ZeroAddress();
    }
}
Roles.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

library Roles {
    uint64 public constant LIQUIDATOR_ROLE = 100;
    uint64 public constant ORDER_SIGNER_ROLE = 101;
    uint64 public constant ORDER_EXECUTOR_ROLE = 102;
    uint64 public constant VAULT_ADMIN_ROLE = 103;
}
BaseWasabiPool.sol 437 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/interfaces/IERC1271.sol";

import "./Hash.sol";
import "./PerpUtils.sol";
import "./IWasabiPerps.sol";
import "./admin/IAddressProvider.sol";
import "./admin/PerpManager.sol";
import "./admin/Roles.sol";
import "./util/IPartnerFeeManager.sol";
import "./weth/IWETH.sol";

abstract contract BaseWasabiPool is IWasabiPerps, UUPSUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, EIP712Upgradeable {
    using Address for address;
    using SafeERC20 for IERC20;
    using Hash for OpenPositionRequest;
    using Hash for AddCollateralRequest;
    using Hash for RemoveCollateralRequest;
    using Hash for Position;

    /// @dev indicates if this pool is an long pool
    bool public isLongPool;

    /// @custom:oz-renamed-from addressProvider
    IAddressProvider public _deprecated_addressProvider;

    /// @dev position id to hash
    mapping(uint256 => bytes32) public positions;

    /// @dev the ERC20 vaults
    mapping(address => address) public vaults;

    /// @dev the quote tokens
    /// @custom:oz-renamed-from baseTokens
    mapping(address => bool) public quoteTokens;

    /// @dev magic bytes for closed position
    bytes32 internal constant CLOSED_POSITION_HASH = bytes32(uint256(1));

    /**
     * @dev Checks if the caller has the correct role
     */
    modifier onlyRole(uint64 roleId) {
        _getManager().checkRole(roleId, msg.sender);
        _;
    }

    /**
     * @dev Checks if the caller is an admin
     */
    modifier onlyAdmin() {
        _getManager().isAdmin(msg.sender);
        _;
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @dev Initializes the pool as per UUPSUpgradeable
    /// @param _isLongPool a flag indicating if this is a long pool or a short pool
    /// @param _manager The PerpManager contract that will own this vault
    function __BaseWasabiPool_init(bool _isLongPool, PerpManager _manager) public onlyInitializing {
        __UUPSUpgradeable_init();
        __Ownable_init(address(_manager));
        __ReentrancyGuard_init();
        __EIP712_init(_isLongPool ? "WasabiLongPool" : "WasabiShortPool", "1");

        isLongPool = _isLongPool;
        quoteTokens[_getWethAddress()] = true;
    }

    /// @inheritdoc UUPSUpgradeable
    function _authorizeUpgrade(address) internal view override onlyAdmin {}

    /// @inheritdoc IWasabiPerps
    function getVault(address _asset) public view returns (IWasabiVault) {
        if (vaults[_asset] == address(0)) revert InvalidVault();
        return IWasabiVault(vaults[_asset]);
    }

    /// @inheritdoc IWasabiPerps
    function addVault(IWasabiVault _vault) external onlyRole(Roles.VAULT_ADMIN_ROLE) {
        if (_vault.getPoolAddress(isLongPool) != address(this)) revert InvalidVault();
        address asset = _vault.asset();
        if (vaults[asset] != address(0)) revert VaultAlreadyExists();
        vaults[asset] = address(_vault);
        emit NewVault(address(this), asset, address(_vault));
    }

    /// @inheritdoc IWasabiPerps
    function addQuoteToken(address _token) external onlyAdmin {
        quoteTokens[_token] = true;
    }

    /// @dev Repays a position
    /// @notice This function now handles the actual repayment to the V2 vault
    /// @param _principal the principal
    /// @param _principalCurrency the principal currency
    /// @param _isLiquidation true if this is a liquidation
    /// @param _principalRepaid principal amount repaid
    /// @param _interestPaid interest amount paid
    function _recordRepayment(
        uint256 _principal,
        address _principalCurrency,
        bool _isLiquidation,
        uint256 _principalRepaid,
        uint256 _interestPaid
    ) internal {
        IWasabiVault vault = getVault(_principalCurrency);
        uint256 totalRepayment = _principalRepaid + _interestPaid;
        IERC20(_principalCurrency).safeTransfer(address(vault), totalRepayment);
        vault.recordRepayment(totalRepayment, _principal, _isLiquidation);
    }

    /// @dev Pays the close amounts to the trader and the fee receiver
    /// @param _payoutType whether to send WETH to the trader, send ETH, or deposit tokens to the vault (if `_token != WETH` then `WRAPPED` and `UNWRAPPED` have no effect)
    /// @param _token the payout token (`currency` for longs, `collateralCurrency` for shorts)
    /// @param _trader the trader
    /// @param _referrer the partner that referred the trader, if applicable
    /// @param _closeAmounts the close amounts
    function _payCloseAmounts(
        PayoutType _payoutType,
        address _token,
        address _trader,
        address _referrer,
        CloseAmounts memory _closeAmounts
    ) internal {
        uint256 closeFees = _closeAmounts.closeFee;
        // Deduct partner fees from close fees if referrer is a partner
        if (_referrer != address(0)) {
            closeFees -= _handlePartnerFees(closeFees, _token, _referrer);
        }

        // Check if the payout token is ETH/WETH or another ERC20 token
        if (_token == _getWethAddress()) {
            uint256 total = _closeAmounts.payout + closeFees + _closeAmounts.liquidationFee;
            IWETH wethToken = IWETH(_getWethAddress());
            if (_payoutType == PayoutType.UNWRAPPED) {
                if (total > address(this).balance) {
                    wethToken.withdraw(total - address(this).balance);
                }
                PerpUtils.payETH(closeFees, _getFeeReceiver());

                if (_closeAmounts.liquidationFee > 0) { 
                    PerpUtils.payETH(_closeAmounts.liquidationFee, _getLiquidationFeeReceiver());
                }

                PerpUtils.payETH(_closeAmounts.payout, _trader);
                // Do NOT fall through to ERC20 transfer
                return;
            } else {
                uint256 balance = wethToken.balanceOf(address(this));
                if (total > balance) {
                    wethToken.deposit{value: total - balance}();
                }
                // Fall through to ERC20 transfer
            }
        }

        if (closeFees != 0) {
            IERC20(_token).safeTransfer(_getFeeReceiver(), closeFees);
        }

        if (_closeAmounts.liquidationFee != 0) {
            IERC20(_token).safeTransfer(_getLiquidationFeeReceiver(), _closeAmounts.liquidationFee);
        }

        if (_closeAmounts.payout != 0) {
            if (_payoutType == PayoutType.VAULT_DEPOSIT) {
                IWasabiVault vault = getVault(_token);
                if (IERC20(_token).allowance(address(this), address(vault)) < _closeAmounts.payout) {
                    IERC20(_token).approve(address(vault), type(uint256).max);
                }
                vault.deposit(_closeAmounts.payout, _trader);
            } else {
                IERC20(_token).safeTransfer(_trader, _closeAmounts.payout);
            }
        }
    }

    /// @dev Computes the interest to be paid
    /// @param _position the position
    /// @param _interest the interest amount
    function _computeInterest(Position calldata _position, uint256 _interest) internal view returns (uint256) {
        uint256 maxInterest = _getManager()
            .computeMaxInterest(_position.currency, _position.principal, _position.lastFundingTimestamp);
        if (_interest == 0 || _interest > maxInterest) {
            _interest = maxInterest;
        }
        return _interest;
    }

    /// @dev Validates an open position request
    /// @param _request the request
    /// @param _signature the signature
    function _validateOpenPositionRequest(
        OpenPositionRequest calldata _request,
        Signature calldata _signature
    ) internal {
        // Validations
        _validateSignature(_request.hash(), _signature);
        Position memory existingPosition = _request.existingPosition;
        address currency = _request.currency;
        address collateralCurrency = _request.targetCurrency;
        if (existingPosition.id != 0) {
            if (positions[existingPosition.id] != existingPosition.hash()) revert InvalidPosition();
            if (currency != existingPosition.currency) revert InvalidCurrency();
            if (collateralCurrency != existingPosition.collateralCurrency) revert InvalidTargetCurrency();
        } else {
            if (positions[_request.id] != bytes32(0)) revert PositionAlreadyTaken();
            if (!_isQuoteToken(isLongPool ? currency : collateralCurrency)) revert InvalidCurrency();
            if (currency == collateralCurrency) revert InvalidTargetCurrency();
            if (
                existingPosition.downPayment +
                existingPosition.principal +
                existingPosition.collateralAmount +
                existingPosition.feesToBePaid != 0
            ) revert InvalidPosition();
        }
        if (_request.functionCallDataList.length == 0) revert SwapFunctionNeeded();
        if (_request.principal == 0) revert InsufficientPrincipalUsed();
        if (_request.expiration < block.timestamp) revert OrderExpired();

        // Receive payment
        PerpUtils.receivePayment(
            isLongPool ? currency : collateralCurrency,
            _request.downPayment + _request.fee,
            _getWethAddress(),
            msg.sender
        );

        // Pay open fees
        _handleOpenFees(
            _request.fee,
            isLongPool ? _request.currency : _request.targetCurrency,
            _request.referrer
        );
    }

    /// @dev Validates an add collateral request
    /// @param _request the request
    /// @param _signature the signature
    function _validateAddCollateralRequest(
        AddCollateralRequest calldata _request,
        Signature calldata _signature
    ) internal {
        // Validations
        _validateSignature(_request.hash(), _signature);
        Position memory existingPosition = _request.position;
        address currency = existingPosition.currency;
        address collateralCurrency = existingPosition.collateralCurrency;

        if (_request.amount == 0) revert InsufficientAmountProvided();
        if (isLongPool) {
            if (_request.interest == 0) revert InsufficientInterest();
            uint256 maxInterest = _getManager()
                .computeMaxInterest(currency, existingPosition.principal, existingPosition.lastFundingTimestamp);
            if (_request.interest > maxInterest) revert InvalidInterestAmount();
        } else {
            if (_request.interest != 0) revert InvalidInterestAmount();
        }
        if (positions[existingPosition.id] != existingPosition.hash()) revert InvalidPosition();
        if (_request.expiration < block.timestamp) revert OrderExpired();

        // Receive payment
        PerpUtils.receivePayment(
            isLongPool ? currency : collateralCurrency,
            _request.amount,
            _getWethAddress(),
            msg.sender
        );
    }

    /// @dev Validates a remove collateral request
    /// @param _request the request
    /// @param _signature the signature
    function _validateRemoveCollateralRequest(
        RemoveCollateralRequest calldata _request,
        Signature calldata _signature
    ) internal view {
        // Validations
        _validateSignature(_request.hash(), _signature);
        if (_request.amount == 0) revert InsufficientAmountProvided();
        if (_request.expiration < block.timestamp) revert OrderExpired();
        Position memory existingPosition = _request.position;
        if (positions[existingPosition.id] != existingPosition.hash()) revert InvalidPosition();
        // For longs, do not allow exceeding the max leverage
        // For both longs and shorts, must validate off-chain that amount <= current profit
        if (isLongPool) {
            uint256 maxPrincipal = _getManager().computeMaxPrincipal(
                existingPosition.currency,
                existingPosition.collateralCurrency,
                existingPosition.downPayment
            );
            if (_request.amount + existingPosition.principal > maxPrincipal) revert PrincipalTooHigh();
        } else {
            if (existingPosition.collateralAmount - existingPosition.downPayment < _request.amount) revert TooMuchCollateralSpent();
        }
    }

    /// @dev Checks if the signature is valid for the given struct hash for the order signer role
    /// @param _structHash the struct hash
    /// @param _signature the signature
    function _validateSignature(bytes32 _structHash, IWasabiPerps.Signature calldata _signature) internal view {
        bytes32 typedDataHash = _hashTypedDataV4(_structHash);
        address signer = ecrecover(typedDataHash, _signature.v, _signature.r, _signature.s);

        (bool isValidSigner, ) = _getManager().hasRole(Roles.ORDER_SIGNER_ROLE, signer);
        if (!isValidSigner) {
            revert IWasabiPerps.InvalidSignature();
        }
    }

    /// @dev Checks if the signer for the given structHash and signature is the expected signer
    /// @param _expectedSigner the expected signer, i.e., the trader
    /// @param _structHash the struct hash
    /// @param _signature the signature
    function _validateSigner(address _expectedSigner, bytes32 _structHash, bytes memory _signature) internal view {
        bytes32 typedDataHash = _hashTypedDataV4(_structHash);

        // Cases to consider:
        // ==================
        // 1. EOA signer validation
        //   1a. Recovered EOA matches the expected signer
        //   1b. Recovered EOA is authorized to sign for the expected signer, which might be a contract
        // 2. Contract signer (ERC-1271) validation
        // If both cases fail, revert

        // Case 1: EOA signer
        if (_signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly {
                r := mload(add(_signature, 32))
                s := mload(add(_signature, 64))
                v := byte(0, mload(add(_signature, 96)))
            }

            address signer = ecrecover(typedDataHash, v, r, s);

            if (
                signer == _expectedSigner ||
                _getManager().isAuthorizedSigner(_expectedSigner, signer)
            ) {
                return; // success
            }
        }

        // Case 2: Contract signer (ERC-1271)
        if (_expectedSigner.code.length != 0) {
            try IERC1271(_expectedSigner).isValidSignature(typedDataHash, _signature) returns (bytes4 magicValue) {
                if (magicValue == IERC1271.isValidSignature.selector) {
                    return; // success
                }
            } catch {
                // ignore, will revert below
            }
        }

        // If all checks fail
        revert IWasabiPerps.InvalidSignature();
    }

    function _handleOpenFees(uint256 _fee, address _currency, address _referrer) internal {
        // Handle partner fees if the referrer is a partner
        if (_fee != 0 && _referrer != address(0)) {
            _fee -= _handlePartnerFees(_fee, _currency, _referrer);
        }
        // Send the remaining fees to the fee receiver
        IERC20(_currency).safeTransfer(_getFeeReceiver(), _fee);
    }

    function _handlePartnerFees(uint256 _fee, address _currency, address _referrer) internal returns (uint256) {
        IPartnerFeeManager partnerFeeManager = _getPartnerFeeManager();
        uint256 partnerFees = partnerFeeManager.computePartnerFees(_referrer, _fee);
        if (partnerFees != 0) {
            IERC20(_currency).approve(address(partnerFeeManager), partnerFees);
            partnerFeeManager.accrueFees(_referrer, _currency, partnerFees);
        }
        return partnerFees;
    }

    /// @dev returns {true} if the given token is a quote token
    function _isQuoteToken(address _token) internal view returns(bool) {
        return quoteTokens[_token];
    }

    /// @dev checks if the caller can close out the given trader's position
    function _checkCanClosePosition(address _trader) internal view {
        if (msg.sender == _trader) return;

        (bool isLiquidator, ) = _getManager().hasRole(Roles.LIQUIDATOR_ROLE, msg.sender);
        if (!isLiquidator) revert SenderNotTrader();
    }

    /// @dev returns the manager of the contract
    function _getManager() internal view returns (PerpManager) {
        return PerpManager(owner());
    }

    /// @dev returns the WETH address
    function _getWethAddress() internal view returns (address) {
        return _getManager().wethAddress();
    }

    /// @dev returns the fee receiver
    function _getFeeReceiver() internal view returns (address) {
        return _getManager().feeReceiver();
    }

    /// @dev returns the liquidation fee receiver
    function _getLiquidationFeeReceiver() internal view returns (address) {
        return _getManager().liquidationFeeReceiver();
    }

    /// @dev returns the partner fee manager
    function _getPartnerFeeManager() internal view returns (IPartnerFeeManager) {
        return _getManager().partnerFeeManager();
    }

    /// @dev returns the WasabiRouter contract
    function _getWasabiRouter() internal view returns (IWasabiRouter) {
        return _getManager().wasabiRouter();
    }

    receive() external payable virtual {}
}
Hash.sol 127 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

library Hash {
    bytes32 private constant _FUNCTION_CALL_DATA_HASH =
        keccak256("FunctionCallData(address to,uint256 value,bytes data)");
    bytes32 private constant _OPEN_POSITION_REQUEST_HASH =
        keccak256("OpenPositionRequest(uint256 id,address currency,address targetCurrency,uint256 downPayment,uint256 principal,uint256 minTargetAmount,uint256 expiration,uint256 fee,FunctionCallData[] functionCallDataList,Position existingPosition,address referrer)FunctionCallData(address to,uint256 value,bytes data)Position(uint256 id,address trader,address currency,address collateralCurrency,uint256 lastFundingTimestamp,uint256 downPayment,uint256 principal,uint256 collateralAmount,uint256 feesToBePaid)");
    bytes32 private constant _POSITION_HASH =
        keccak256("Position(uint256 id,address trader,address currency,address collateralCurrency,uint256 lastFundingTimestamp,uint256 downPayment,uint256 principal,uint256 collateralAmount,uint256 feesToBePaid)");
    bytes32 private constant _CLOSE_POSITION_REQUEST_HASH =
        keccak256("ClosePositionRequest(uint256 expiration,uint256 interest,uint256 amount,Position position,FunctionCallData[] functionCallDataList,address referrer)FunctionCallData(address to,uint256 value,bytes data)Position(uint256 id,address trader,address currency,address collateralCurrency,uint256 lastFundingTimestamp,uint256 downPayment,uint256 principal,uint256 collateralAmount,uint256 feesToBePaid)");
    bytes32 private constant _CLOSE_POSITION_ORDER_HASH =
        keccak256("ClosePositionOrder(uint8 orderType,uint256 positionId,uint256 createdAt,uint256 expiration,uint256 makerAmount,uint256 takerAmount,uint256 executionFee)");
    bytes32 private constant _ADD_COLLATERAL_REQUEST_HASH =
        keccak256("AddCollateralRequest(uint256 amount,uint256 interest,uint256 expiration,Position position)Position(uint256 id,address trader,address currency,address collateralCurrency,uint256 lastFundingTimestamp,uint256 downPayment,uint256 principal,uint256 collateralAmount,uint256 feesToBePaid)");
    bytes32 private constant _REMOVE_COLLATERAL_REQUEST_HASH =
        keccak256("RemoveCollateralRequest(uint256 amount,uint256 expiration,Position position)Position(uint256 id,address trader,address currency,address collateralCurrency,uint256 lastFundingTimestamp,uint256 downPayment,uint256 principal,uint256 collateralAmount,uint256 feesToBePaid)");

    /// @dev hashes the given FunctionCallData list
    /// @param functionCallDataList The list of function call data to hash
    function hashFunctionCallDataList(IWasabiPerps.FunctionCallData[] memory functionCallDataList) internal pure returns (bytes32) {
        uint256 length = functionCallDataList.length;
        bytes32[] memory functionCallDataHashes = new bytes32[](length);
        for (uint256 i = 0; i < length; ++i) {
            functionCallDataHashes[i] = keccak256(
                abi.encode(
                    _FUNCTION_CALL_DATA_HASH,
                    functionCallDataList[i].to,
                    functionCallDataList[i].value,
                    keccak256(functionCallDataList[i].data)
                )
            );
        }
        return keccak256(abi.encodePacked(functionCallDataHashes));
    }

    /// @dev Hashes the given Position
    /// @param _position the position
    function hash(IWasabiPerps.Position memory _position) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _POSITION_HASH,
            _position.id,
            _position.trader,
            _position.currency,
            _position.collateralCurrency,
            _position.lastFundingTimestamp,
            _position.downPayment,
            _position.principal,
            _position.collateralAmount,
            _position.feesToBePaid
        ));
    }

    /// @dev Hashes the given OpenPositionRequest
    /// @param _request The request to hash
    function hash(IWasabiPerps.OpenPositionRequest memory _request) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _OPEN_POSITION_REQUEST_HASH,
            _request.id,
            _request.currency,
            _request.targetCurrency,
            _request.downPayment,
            _request.principal,
            _request.minTargetAmount,
            _request.expiration,
            _request.fee,
            hashFunctionCallDataList(_request.functionCallDataList),
            hash(_request.existingPosition),
            _request.referrer
        ));
    }

    /// @dev Hashes the given AddCollateralRequest
    /// @param _request The request to hash
    function hash(IWasabiPerps.AddCollateralRequest memory _request) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _ADD_COLLATERAL_REQUEST_HASH,
            _request.amount,
            _request.interest,
            _request.expiration,
            hash(_request.position)
        ));
    }

    /// @dev Hashes the given RemoveCollateralRequest
    /// @param _request The request to hash
    function hash(IWasabiPerps.RemoveCollateralRequest memory _request) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _REMOVE_COLLATERAL_REQUEST_HASH,
            _request.amount,
            _request.expiration,
            hash(_request.position)
        ));
    }

    /// @dev Hashes the given ClosePositionRequest
    /// @param _request The request to hash
    function hash(IWasabiPerps.ClosePositionRequest memory _request) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _CLOSE_POSITION_REQUEST_HASH,
            _request.expiration,
            _request.interest,
            _request.amount,
            hash(_request.position),
            hashFunctionCallDataList(_request.functionCallDataList),
            _request.referrer
        ));
    }

    /// @dev Hashes the given ClosePositionOrder
    /// @param _order The order to hash
    function hash(IWasabiPerps.ClosePositionOrder memory _order) internal pure returns (bytes32) {
        return keccak256(abi.encode(
            _CLOSE_POSITION_ORDER_HASH,
            _order.orderType,
            _order.positionId,
            _order.createdAt,
            _order.expiration,
            _order.makerAmount,
            _order.takerAmount,
            _order.executionFee
        ));
    }
}
IWasabiPerps.sol 397 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./vaults/IWasabiVault.sol";

interface IWasabiPerps {

    error LiquidationThresholdNotReached(); // 0xc4d82e43
    error InvalidSignature(); // 0x8baa579f
    error PositionAlreadyTaken(); // 0xe168e4db
    error SwapFunctionNeeded(); // 0xac8da8e3
    error OrderExpired(); // 0xc56873ba
    error InvalidOrder(); // 0xaf610693
    error PriceTargetNotReached(); // 0x5d5ce003
    error InvalidCurrency(); // 0xf5993428
    error InvalidTargetCurrency(); // 0x0415b9ce
    error InsufficientAmountProvided(); // 0xf948951e
    error PrincipalTooHigh(); // 0xd7cdb444
    error InsufficientPrincipalUsed(); // 0xb1084a42
    error InsufficientPrincipalRepaid(); // 0xb0f8fc9b
    error InsufficientCollateralReceived(); // 0x406220a9
    error InsufficientCollateralSpent(); // 0x5a86eb3b
    error InsufficientInterest(); // 0x0ffe80f0
    error TooMuchCollateralSpent(); // 0x1cbf0b89
    error SenderNotTrader(); // 0x79184208
    error InvalidPosition(); // 0xce7e065e
    error EthTransferFailed(uint256 amount, address _target); // 0xf733a609
    error InvalidVault(); // 0xd03a6320
    error VaultAlreadyExists(); // 0x04aabf33
    error ValueDeviatedTooMuch(); // 0x604e9173
    error EthReceivedForNonEthCurrency(); // 0x94427663
    error InvalidInterestAmount(); // 0xe749867e
    error InvalidInput(); // 0xb4fa3fb3

    event PositionOpened(
        uint256 positionId,
        address trader,
        address currency,
        address collateralCurrency,
        uint256 downPayment,
        uint256 principal,
        uint256 collateralAmount,
        uint256 feesToBePaid
    );

    event PositionClosed(
        uint256 id,
        address trader,
        uint256 payout,
        uint256 principalRepaid,
        uint256 interestPaid,
        uint256 feeAmount
    );

    event PositionClosedWithOrder(
        uint256 id,
        address trader,
        uint8 orderType,
        uint256 payout,
        uint256 principalRepaid,
        uint256 interestPaid,
        uint256 feeAmount
    );

    event PositionLiquidated(
        uint256 id,
        address trader,
        uint256 payout,
        uint256 principalRepaid,
        uint256 interestPaid,
        uint256 feeAmount
    );

    event PositionIncreased(
        uint256 id,
        address trader,
        uint256 downPaymentAdded,
        uint256 principalAdded,
        uint256 collateralAdded,
        uint256 feesAdded
    );

    event PositionDecreased(
        uint256 id,
        address trader,
        uint256 payout,
        uint256 principalRepaid,
        uint256 interestPaid,
        uint256 closeFee,
        uint256 pastFees,
        uint256 collateralReduced,
        uint256 downPaymentReduced
    );

    event PositionDecreasedWithOrder(
        uint256 id,
        address trader,
        uint8 orderType,
        uint256 payout,
        uint256 principalRepaid,
        uint256 interestPaid,
        uint256 closeFee,
        uint256 pastFees,
        uint256 collateralReduced,
        uint256 downPaymentReduced
    );

    event CollateralAdded(
        uint256 id,
        address trader,
        uint256 downPaymentAdded,
        uint256 collateralAdded,
        uint256 principalReduced,
        uint256 interestPaid
    );

    event CollateralRemoved(
        uint256 id,
        address trader,
        uint256 downPaymentReduced,
        uint256 collateralReduced,
        uint256 principalAdded
    );

    event NativeYieldClaimed(
        address vault,
        address token,
        uint256 amount
    );

    event InterestPaid(
        uint256 id,
        uint256 interestPaid,
        uint256 principalAdded,
        uint256 collateralReduced,
        uint256 downPaymentReduced
    );

    /// @dev Emitted when a new vault is created
    event NewVault(address indexed pool, address indexed asset, address vault);

    /// @dev Flag specifying whether to send WETH to the trader, send ETH to the trader, or deposit WETH to the vault
    enum PayoutType {
        WRAPPED,
        UNWRAPPED,
        VAULT_DEPOSIT
    }

    /// @dev Defines a function call
    struct FunctionCallData {
        address to;
        uint256 value;
        bytes data;
    }

    /// @dev Defines a position
    /// @param id The unique identifier for the position.
    /// @param trader The address of the trader who opened the position.
    /// @param currency The address of the currency to be paid for the position.
    /// @param collateralCurrency The address of the currency to be received for the position.
    /// @param lastFundingTimestamp The timestamp of the last funding payment.
    /// @param downPayment The initial down payment amount required to open the position (is in `currency` for long, `collateralCurrency` for short positions)
    /// @param principal The total principal amount to be borrowed for the position (is in `currency`)
    /// @param collateralAmount The total collateral amount to be received for the position (is in `collateralCurrency`)
    /// @param feesToBePaid The total fees to be paid for the position (is in `currency`)
    struct Position {
        uint256 id;
        address trader;
        address currency;
        address collateralCurrency;
        uint256 lastFundingTimestamp;
        uint256 downPayment;
        uint256 principal;
        uint256 collateralAmount;
        uint256 feesToBePaid;
    }

    /// @dev Defines a request to open a position.
    /// @param id The unique identifier for the position.
    /// @param currency The address of the currency to be paid for the position.
    /// @param targetCurrency The address of the currency to be received for the position.
    /// @param downPayment The initial down payment amount required to open the position (is in `currency` for long, `collateralCurrency` for short positions)
    /// @param principal The total principal amount to be borrowed for the position.
    /// @param minTargetAmount The minimum amount of target currency to be received for the position to be valid.
    /// @param expiration The timestamp when this position request expires.
    /// @param fee The fee to be paid for the position
    /// @param functionCallDataList A list of FunctionCallData structures representing functions to call to open the position.
    /// @param existingPosition The existing position to be increased, or an empty position if a new position is to be opened.
    /// @param referrer The address of the partner that referred the trader
    struct OpenPositionRequest {
        uint256 id;
        address currency;
        address targetCurrency;
        uint256 downPayment;
        uint256 principal;
        uint256 minTargetAmount;
        uint256 expiration;
        uint256 fee;
        FunctionCallData[] functionCallDataList;
        Position existingPosition;
        address referrer;
    }

    /// @dev Defines a request to add collateral to a position.
    /// @param amount The amount of collateral to add.
    /// @param interest The interest to be paid for the position.
    /// @param expiration The timestamp when this request expires.
    /// @param position The position to add collateral to.
    struct AddCollateralRequest {
        uint256 amount;
        uint256 interest;
        uint256 expiration;
        Position position;
    }

    /// @dev Defines a request to remove collateral from a position.
    /// @param amount The amount of collateral to remove.
    /// @param expiration The timestamp when this request expires.
    /// @param position The position to remove collateral from.
    struct RemoveCollateralRequest {
        uint256 amount;
        uint256 expiration;
        Position position;
    }

    /// @dev Defines the amounts to be paid when closing a position.
    /// @param payout The amount to be paid to the trader.
    /// @param collateralSold The amount of the collateral used to swap for principal.
    /// @param principalRepaid The amount of the principal to be repaid.
    /// @param interestPaid The amount of the interest to be paid.
    /// @param pastFees The amount of past fees to be paid.
    /// @param closeFee The amount of the close fee to be paid.
    /// @param liquidationFee The amount of the liquidation fee to be paid.
    /// @param downPaymentReduced The amount by which the down payment was reduced.
    /// @param collateralReduced The total amount by which the collateral was reduced. Not the same as `collateralSold` for shorts.
    struct CloseAmounts {
        uint256 payout;
        uint256 collateralSold;
        uint256 principalRepaid;
        uint256 interestPaid;
        uint256 pastFees;
        uint256 closeFee;
        uint256 liquidationFee;
        uint256 downPaymentReduced;
        uint256 collateralReduced;
    }

    /// @dev Defines an order to close a position.
    /// @param orderType The type of the order (0 = Take Profit, 1 = Stop Loss)
    /// @param positionId The unique identifier for the position.
    /// @param createdAt The timestamp when this order was created.
    /// @param expiration The timestamp when this order expires.
    /// @param makerAmount The amount that will be sold from the position (is in `position.collateralCurrency`)
    /// @param takerAmount The amount that will be bought to close the position (is in `position.currency`)
    /// @param executionFee The amount of the execution fee to be paid. (gas)
    struct ClosePositionOrder {
        uint8 orderType;
        uint256 positionId;
        uint256 createdAt;
        uint256 expiration;
        uint256 makerAmount;
        uint256 takerAmount;
        uint256 executionFee;
    }

    /// @dev Defines a request to close a position.
    /// @param expiration The timestamp when this position request expires.
    /// @param interest The interest to be paid for the position.
    /// @param amount The amount of collateral to sell (for longs) or amount of principal to buy back (for shorts), or 0 to fully close the position.
    /// @param position The position to be closed.
    /// @param functionCallDataList A list of FunctionCallData structures representing functions to call to close the position.
    /// @param referrer The address of the partner that referred the trader
    struct ClosePositionRequest {
        uint256 expiration;
        uint256 interest;
        uint256 amount;
        Position position;
        FunctionCallData[] functionCallDataList;
        address referrer;
    }

    /// @dev Defines the arguments needed for the internal close position function.
    /// @param _interest the interest amount to be paid
    /// @param _amount the amount of collateral to sell (for longs) or amount of principal to buy back (for shorts), or 0 to fully close the position.
    /// @param _executionFee the execution fee
    /// @param _payoutType whether to send WETH to the trader, send ETH, or deposit WETH to the vault
    /// @param _isLiquidation flag indicating if the close is a liquidation
    /// @param _referrer the address of the partner that referred the trader
    struct ClosePositionInternalArgs {
        uint256 _interest;
        uint256 _amount;
        uint256 _executionFee;
        PayoutType _payoutType;
        bool _isLiquidation;
        address _referrer;
    }

    /// @dev Defines a signature
    struct Signature {
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    /// @dev Opens a position
    /// @param _request the request to open a position
    /// @param _signature the signature of the request
    function openPosition(
        OpenPositionRequest calldata _request,
        Signature calldata _signature
    ) external payable returns (Position memory);

    /// @dev Opens a position on behalf of a user
    /// @param _request the request to open a position
    /// @param _signature the signature of the request
    /// @param _trader the address of the user for whom the position is opened
    function openPositionFor(
        OpenPositionRequest calldata _request,
        Signature calldata _signature,
        address _trader
    ) external payable returns (Position memory);

    /// @dev Adds collateral to a position
    /// @param _request the request to add collateral
    /// @param _signature the signature of the request
    function addCollateral(
        AddCollateralRequest calldata _request,
        Signature calldata _signature
    ) external payable returns (Position memory);

    /// @dev Removes collateral from a position
    /// @param _request the request to remove collateral
    /// @param _signature the signature of the request
    function removeCollateral(
        RemoveCollateralRequest calldata _request,
        Signature calldata _signature
    ) external payable returns (Position memory);

    /// @dev Closes a position
    /// @param _payoutType whether to send WETH to the trader, send ETH, or deposit WETH to the vault
    /// @param _request the request to close a position
    /// @param _signature the signature of the request
    function closePosition(
        PayoutType _payoutType,
        ClosePositionRequest calldata _request,
        Signature calldata _signature
    ) external payable;

    /// @dev Closes a position
    /// @param _payoutType whether to send WETH to the trader, send ETH, or deposit WETH to the vault
    /// @param _request the request to close a position
    /// @param _signature the signature of the request, signed by the ORDER_SIGNER_ROLE
    /// @param _order the order to close the position
    /// @param _orderSignature the signature of the order, signed by the owner of the position
    function closePosition(
        PayoutType _payoutType,
        ClosePositionRequest calldata _request,
        Signature calldata _signature,
        ClosePositionOrder calldata _order,
        bytes calldata _orderSignature
    ) external payable;

    /// @dev Liquidates a position
    /// @param _payoutType whether to send WETH to the trader, send ETH, or deposit WETH to the vault
    /// @param _interest the interest to be paid
    /// @param _position the position to liquidate
    /// @param _swapFunctions the swap functions to use to liquidate the position
    /// @param _referrer the address of the partner that referred the trader
    function liquidatePosition(
        PayoutType _payoutType,
        uint256 _interest,
        Position calldata _position,
        FunctionCallData[] calldata _swapFunctions,
        address _referrer
    ) external payable;

    /// @dev Records interest for a position and updates the position
    /// @notice Only callable by the InterestRecorder contract
    /// @param _positions the positions to record interest for
    /// @param _interests the interests to record
    /// @param _swapFunctions the swap functions to use for short positions
    function recordInterest(Position[] calldata _positions, uint256[] calldata _interests, FunctionCallData[] calldata _swapFunctions) external;

    /// @dev Returns the vault used for the given asset
    function getVault(address _asset) external view returns (IWasabiVault);

    /// @dev Adds a new vault
    function addVault(IWasabiVault _vault) external;

    /// @dev Adds a new quote token
    function addQuoteToken(address _token) external;

    /// @dev Returns whether this pool is a long pool
    function isLongPool() external view returns (bool);
}
PerpUtils.sol 102 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./weth/IWETH.sol";
import {IWasabiPerps} from "./IWasabiPerps.sol";

library PerpUtils {
    using Address for address;
    using SafeERC20 for IERC20;

    /// @dev Pays ETH to a given address
    /// @param _amount The amount to pay
    /// @param _target The address to pay to
    function payETH(uint256 _amount, address _target) internal {
        if (_amount > 0) {
            (bool sent, ) = payable(_target).call{value: _amount}("");
            if (!sent) {
                revert IWasabiPerps.EthTransferFailed(_amount, _target);
            }
        }
    }

    /// @dev Computes the close fee for a position by looking at the position size
    /// @param _position the position to compute the close fee for
    /// @param _size the size to close
    /// @param _isLong whether the position is long or short
    function computeCloseFee(
        IWasabiPerps.Position calldata _position,
        uint256 _size,
        bool _isLong
    ) internal pure returns(uint256) {
        uint256 denominator = _position.feesToBePaid + (
            _isLong ? _position.downPayment + _position.principal : _position.collateralAmount
        );
        return (_size * _position.feesToBePaid) / denominator;
    }

    /// @dev Receives payment from a given address
    /// @param _currency the currency to receive
    /// @param _amount the amount to receive
    /// @param _wethAddress the WETH address
    /// @param _sender the address to receive from
    function receivePayment(
        address _currency,
        uint256 _amount,
        address _wethAddress,
        address _sender
    ) internal {
        if (msg.value > 0) {
            if (_currency != _wethAddress) revert IWasabiPerps.InvalidCurrency();
            if (msg.value != _amount) revert IWasabiPerps.InsufficientAmountProvided();
            wrapWETH(_wethAddress);
        } else {
            IERC20(_currency).safeTransferFrom(_sender, address(this), _amount);
        }
    }

    /// @dev Wraps the whole ETH in this contract
    function wrapWETH(address _wethAddress) internal {
        IWETH weth = IWETH(_wethAddress);
        weth.deposit{value: address(this).balance}();
    }

    /// @dev Executes a given list of functions and returns the balance changes
    /// @param _marketplaceCallData List of marketplace calldata
    /// @param _tokenIn the token to swap from
    /// @param _tokenOut the token to swap to
    /// @return amountIn the amount of tokenIn swapped
    /// @return amountOut the amount of tokenOut received
    function executeSwapFunctions(
        IWasabiPerps.FunctionCallData[] memory _marketplaceCallData,
        IERC20 _tokenIn,
        IERC20 _tokenOut
    ) internal returns (uint256 amountIn, uint256 amountOut) {
        amountIn = _tokenIn.balanceOf(address(this));
        amountOut = _tokenOut.balanceOf(address(this));
        uint256 length = _marketplaceCallData.length;
        for (uint256 i; i < length; ++i) {
            IWasabiPerps.FunctionCallData memory functionCallData = _marketplaceCallData[i];
            functionCallData.to.functionCallWithValue(functionCallData.data, functionCallData.value);
        }
        amountIn = amountIn - _tokenIn.balanceOf(address(this));
        amountOut = _tokenOut.balanceOf(address(this)) - amountOut;
    }

    /// @dev Deducts the given amount from the total amount
    /// @param _amount the amount to deduct from
    /// @param _deductAmount the amount to deduct
    /// @return remaining the remaining amount
    /// @return deducted the total deducted
    function deduct(uint256 _amount, uint256 _deductAmount) internal pure returns(uint256 remaining, uint256 deducted) {
        if (_amount > _deductAmount) {
            remaining = _amount - _deductAmount;
            deducted = _deductAmount;
        } else {
            remaining = 0;
            deducted = _amount;
        }
    }
}
IWasabiRouter.sol 214 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "../IWasabiPerps.sol";
import "../weth/IWETH.sol";

interface IWasabiRouter {

    event PositionOpenedWithOrder(address _trader, bytes32 _orderHash);

    error InvalidTrader(); // 0xfb7595a2
    error InvalidSignature(); // 0x8baa579f
    error InvalidPool(); // 0x2083cd40
    error InvalidETHReceived(); // 0x3daee882
    error InvalidFeeBips(); // 0x82c96382
    error FeeReceiverNotSet(); // 0x0b37568b
    error OrderAlreadyUsed(); // 0x88b39043
    error SwapRouterNotWhitelisted(); // 0x13d6cc36
    error SwapFunctionNotWhitelisted(bytes4 selector); // 0x83bc03fe
    error IdenticalTokens(); // 0x5c6d7b73

    // State variables
    function longPool() external view returns (IWasabiPerps);
    function shortPool() external view returns (IWasabiPerps);
    function weth() external view returns (IWETH);
    function swapRouter() external view returns (address);
    function feeReceiver() external view returns (address);
    function withdrawFeeBips() external view returns (uint256);
    function isWhitelistedSwapRouter(address _swapRouter) external view returns (bool);
    
    /// @dev Opens a position using the caller's vault deposits
    /// @param _pool The pool to open the position on
    /// @param _request The request to open the position
    /// @param _signature The signature for the request (from ORDER_SIGNER_ROLE)
    function openPosition(
        IWasabiPerps _pool,
        IWasabiPerps.OpenPositionRequest calldata _request,
        IWasabiPerps.Signature calldata _signature
    ) external;

    /// @dev Opens a position on behalf of a trader using their vault deposits
    /// @param _trader The trader to open the position for
    /// @param _pool The pool to open the position on
    /// @param _request The request to open the position
    /// @param _signature The signature for the request (from ORDER_SIGNER_ROLE, validated by the pool)
    /// @param _traderSignature The signature from the trader or their authorized signer (validated using ERC-1271 if `_trader` is a smart contract wallet)
    /// @param _executionFee The fee to be paid to the order executor
    function openPosition(
        address _trader,
        IWasabiPerps _pool,
        IWasabiPerps.OpenPositionRequest calldata _request,
        IWasabiPerps.Signature calldata _signature,
        bytes calldata _traderSignature,
        uint256 _executionFee
    ) external;

    /// @dev Adds collateral to a position on behalf of a trader using their vault deposits
    /// @param _pool The pool to add collateral to
    /// @param _request The request to add collateral
    /// @param _signature The signature for the request (from ORDER_SIGNER_ROLE)
    function addCollateral(
        IWasabiPerps _pool,
        IWasabiPerps.AddCollateralRequest calldata _request,
        IWasabiPerps.Signature calldata _signature
    ) external;
    
    /// @dev Withdraws assets from one vault, swaps and deposits them into another vault on the sender's behalf
    /// @param _amount The amount of `_tokenIn` to withdraw
    /// @param _tokenIn The asset to withdraw and swap
    /// @param _tokenOut The asset to swap for and deposit
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapVaultToVault(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        bytes calldata _swapCalldata
    ) external;
    
    /// @dev Withdraws assets from one vault, swaps and deposits them into another vault on the sender's behalf
    /// @param _amount The amount of `_tokenIn` to withdraw
    /// @param _tokenIn The asset to withdraw and swap
    /// @param _tokenOut The asset to swap for and deposit
    /// @param _swapRouter The address of the swap router to use (must be whitelisted)
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapVaultToVault(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address _swapRouter,
        bytes calldata _swapCalldata
    ) external;

    /// @dev Withdraws assets from a vault on the sender's behalf, swaps for another asset and sends the output to the sender
    /// @param _amount The amount of `_tokenIn` to withdraw
    /// @param _tokenIn The asset to withdraw and swap
    /// @param _tokenOut The asset to swap for and send to the user
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapVaultToToken(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        bytes calldata _swapCalldata
    ) external;

    /// @dev Withdraws assets from a vault on the sender's behalf, swaps for another asset and sends the output to the sender
    /// @param _amount The amount of `_tokenIn` to withdraw
    /// @param _tokenIn The asset to withdraw and swap
    /// @param _tokenOut The asset to swap for and send to the user
    /// @param _swapRouter The address of the swap router to use (must be whitelisted)
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapVaultToToken(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address _swapRouter,
        bytes calldata _swapCalldata
    ) external;

    /// @dev Transfers assets in from the sender, swaps for another asset and deposits the output into the corresponding vault
    /// @param _amount The amount of `_tokenIn` to transfer from the user
    /// @param _tokenIn The asset to transfer from the user and swap, or the zero address for swapping native ETH
    /// @param _tokenOut The asset to swap for and deposit
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapTokenToVault(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        bytes calldata _swapCalldata
    ) external payable;

    /// @dev Transfers assets in from the sender, swaps for another asset and deposits the output into the corresponding vault
    /// @param _amount The amount of `_tokenIn` to transfer from the user
    /// @param _tokenIn The asset to transfer from the user and swap, or the zero address for swapping native ETH
    /// @param _tokenOut The asset to swap for and deposit
    /// @param _swapRouter The address of the swap router to use (must be whitelisted)
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapTokenToVault(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address _swapRouter,
        bytes calldata _swapCalldata
    ) external payable;

    /// @dev Transfers assets in from the sender, swaps for another asset and sends the output to the sender
    /// @param _amount The amount of `_tokenIn` to transfer from the user
    /// @param _tokenIn The asset to transfer from the user and swap, or the zero address for swapping native ETH
    /// @param _tokenOut The asset to swap for and send to the user
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapTokenToToken(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        bytes calldata _swapCalldata
    ) external payable;

    /// @dev Transfers assets in from the sender, swaps for another asset and sends the output to the sender
    /// @param _amount The amount of `_tokenIn` to transfer from the user
    /// @param _tokenIn The asset to transfer from the user and swap, or the zero address for swapping native ETH
    /// @param _tokenOut The asset to swap for and send to the user
    /// @param _swapRouter The address of the swap router to use (must be whitelisted)
    /// @param _swapCalldata The encoded calldata to send to the swap router
    function swapTokenToToken(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address _swapRouter,
        bytes calldata _swapCalldata
    ) external payable;

    /// @dev Transfers any assets stuck in the contract to the admin
    /// @param _token The token to sweep, or the zero address to sweep ETH
    function sweepToken(address _token) external;

    /// @dev Updates the address of the swap router contract
    /// @param _newSwapRouter The address of the new swap router to use
    function setSwapRouter(
        address _newSwapRouter
    ) external;

    /// @dev Updates the whitelist status of a swap router
    /// @param _swapRouter The address of the swap router
    /// @param _isWhitelisted The whitelist status to set
    function setWhitelistedSwapRouter(
        address _swapRouter,
        bool _isWhitelisted
    ) external;

    /// @dev Updates the whitelist status of a set of function selectors
    /// @param _selectors The function selectors to whitelist
    /// @param _isWhitelisted The whitelist status to set
    function setWhitelistedFunctionSelectors(
        bytes4[] calldata _selectors,
        bool _isWhitelisted
    ) external;

    /// @dev Updates the address of the WETH contract
    /// @param _newWETH The WETH contract
    function setWETH(
        IWETH _newWETH
    ) external;

    /// @dev Sets the address that receives fees for withdrawing from a vault w/o swapping
    /// @param _newFeeReceiver The fee receiver address
    function setFeeReceiver(
        address _newFeeReceiver
    ) external;

    /// @dev Updates the fee percentage charged for withdrawing from a vault w/o swapping
    /// @param _feeBips The new fee percentage in basis points
    function setWithdrawFeeBips(
        uint256 _feeBips
    ) external;
}
IPartnerFeeManager.sol 49 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IPartnerFeeManager {
    error AddressNotPartner();
    error CallerNotPool();
    error InvalidFeeShareBips();

    event FeesAccrued(address indexed partner, address indexed feeToken, uint256 amount);
    event FeesClaimed(address indexed partner, address indexed feeToken, uint256 amount);

    /// @dev Returns true if the given address is a partner
    /// @param partner the address to check 
    /// @return true if the given address is a partner, false otherwise
    function isPartner(address partner) external view returns (bool);

    /// @dev Returns the accrued fees for the given partner and fee token
    /// @param partner the partner to get accrued fees for
    /// @param feeToken the fee token to get accrued fees for
    /// @return the accrued fees for the given partner and fee token
    function getAccruedFees(address partner, address feeToken) external view returns (uint256);

    /// @dev Computes the portion of the total fees that will be paid to the given partner
    /// @param partner the partner to compute fees for
    /// @param totalFees the total fees for the trade
    /// @return the partner fees for the given partner and total fees
    function computePartnerFees(address partner, uint256 totalFees) external view returns (uint256);

    /// @dev Accrues fees for the given partner and fee token
    /// @param partner the partner to accrue fees to
    /// @param feeToken the fee token to accrue fees in
    /// @param partnerFees the fees to accrue to the partner
    function accrueFees(address partner, address feeToken, uint256 partnerFees) external;

    /// @dev Claims fees for the given fee tokens if the caller is a partner
    /// @param feeTokens the fee tokens to claim
    function claimFees(address[] calldata feeTokens) external;

    /// @dev Sets the partner fee share percentage
    /// @param partner the partner to set the fee share for
    /// @param feeShareBips the fee share in basis points
    function setFeeShareBips(address partner, uint256 feeShareBips) external;

    /// @dev Adds fees to the accrued fees for the given partner and fee token
    /// @param partner the partner to accrue fees to
    /// @param feeToken the fee token to accrue fees in
    /// @param amount the amount of fees to accrue to the partner
    function adminAddFees(address partner, address feeToken, uint256 amount) external;
}
IWasabiVault.sol 109 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/interfaces/IERC4626.sol";

interface IWasabiVault is IERC4626  {
    error AlreadyMigrated(); // 0xca1c3cbc
    error PrincipalTooHigh(); // 0xd7cdb444
    error InsufficientAvailablePrincipal(); // 0x836ee0c2
    error InsufficientPrincipalRepaid(); // 0xb0f8fc9b
    error CannotClaimNonYieldBearingAsset(address _asset); // 0x4cfaa278
    error EthTransferFailed(); // 0x6d963f88
    error CannotDepositEth(); // 0x2e15428f
    error CallerNotPool(); // 0xe9211597
    error InvalidEthAmount(); // 0x0772327b
    error InvalidAmount(); // 0x2c5211c6
    error NoDustToClean(); // 0x37e34f38
    error AmountExceedsDebt(); // 0x64ddcf37
    error InvalidStrategy(); // 0x4e236e9a
    error InterestFeeTooHigh(); // 0x8e395cd1

    event NativeYieldClaimed(
        address token,
        uint256 amount
    );

    event DepositCapUpdated(
        uint256 newDepositCap
    );

    event InterestFeeBipsUpdated(
        uint256 newInterestFeeBips
    );

    event InterestReceived(
        uint256 interestReceived,
        uint256 interestFeeShares,
        address feeReceiver
    );

    event StrategyDeposit(
        address strategy,
        address collateral,
        uint256 amountDeposited,
        uint256 collateralReceived
    );

    event StrategyWithdraw(
        address strategy,
        address collateral,
        uint256 amountWithdraw,
        uint256 collateralSold
    );

    event StrategyClaim(
        address strategy,
        address collateral,
        uint256 amount
    );

    /// @dev Deposits ETH into the vault (only WETH vault)
    function depositEth(address receiver) external payable returns (uint256);
    
    /// @dev Returns the long or short pool address
    /// @param _long True for long, false for short
    function getPoolAddress(bool _long) external view returns (address);

    /// @dev Returns the current deposit cap
    function getDepositCap() external view returns (uint256);

    /// @dev Called by the pools to borrow assets when a position is opened
    /// @param _amount The amount of assets to borrow
    function borrow(uint256 _amount) external;

    /// @dev Called by the pools to repay assets when a position is closed
    /// @param _totalRepaid The amount of assets being repaid
    /// @param _principal The amount original principal borrowed
    /// @param _isLiquidation Flag to indicate if the repayment is due to liquidation and can cause bad debt
    function recordRepayment(uint256 _totalRepaid, uint256 _principal, bool _isLiquidation) external;

    /// @dev Called by the admin to deposit assets from this vault into a strategy
    /// @param _strategy The address of the strategy account
    /// @param _depositAmount The amount of assets to deposit into the strategy
    function strategyDeposit(address _strategy, uint256 _depositAmount) external;

    /// @dev Called by the admin to withdraw assets from a strategy back to this vault
    /// @param _strategy The address of the strategy
    /// @param _withdrawAmount The amount of assets to withdraw from the strategy
    function strategyWithdraw(address _strategy, uint256 _withdrawAmount) external;

    /// @dev Called by the admin to record interest earned from a strategy, without paying it out yet
    /// @param _strategy The address of the strategy
    function strategyClaim(address _strategy) external;

    /// @dev Called by the admin to donate assets to the vault, which is recorded as interest
    /// @param _amount The amount of assets to donate
    function donate(uint256 _amount) external;

    /// @dev Called by the admin to remove any leftover assets if `totalSupply` is 0 and `totalAssetValue` is > 0
    function cleanDust() external;

    /// @dev Sets the cap on the amount of assets that can be deposited by all users
    /// @param _newDepositCap The new deposit cap
    function setDepositCap(uint256 _newDepositCap) external;

    /// @dev Sets the fee charged on interest in basis points
    /// @param _newInterestFeeBips The new interest fee in basis points
    function setInterestFeeBips(uint256 _newInterestFeeBips) external;
}
WasabiShortPool.sol 527 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/utils/math/Math.sol";

import "./BaseWasabiPool.sol";
import "./Hash.sol";
import "./PerpUtils.sol";
import "./admin/IAddressProvider.sol";

contract WasabiShortPool is BaseWasabiPool {
    using Hash for Position;
    using Hash for ClosePositionRequest;
    using Hash for ClosePositionOrder;
    using SafeERC20 for IERC20;
    using Math for uint256;

    /// @dev initializer for proxy
    /// @param _manager the PerpManager contract
    function initialize(PerpManager _manager) public virtual initializer {
        __WasabiShortPool_init(_manager);
    }

    function __WasabiShortPool_init(PerpManager _manager) internal virtual onlyInitializing {
        __BaseWasabiPool_init(false, _manager);
    }

    /// @inheritdoc IWasabiPerps
    function openPosition(
        OpenPositionRequest calldata _request,
        Signature calldata _signature
    ) external payable returns (Position memory) {
        return openPositionFor(_request, _signature, msg.sender);
    }

    /// @inheritdoc IWasabiPerps
    function openPositionFor(
        OpenPositionRequest calldata _request,
        Signature calldata _signature,
        address _trader
    ) public payable nonReentrant returns (Position memory) {
        // Validate Request
        _validateOpenPositionRequest(_request, _signature);

        // Validate sender
        if (msg.sender != _trader && msg.sender != address(_getWasabiRouter())) {
            revert SenderNotTrader();
        }
        if (_request.existingPosition.id != 0 && _request.existingPosition.trader != _trader) {
            revert SenderNotTrader();
        }
    
        // Borrow principal from the vault
        IERC20 principalToken = IERC20(_request.currency);
        IWasabiVault vault = getVault(_request.currency);

        vault.borrow(_request.principal);

        // Purchase target token
        (uint256 amountSpent, uint256 collateralAmount) = PerpUtils.executeSwapFunctions(
            _request.functionCallDataList,
            principalToken,
            IERC20(_request.targetCurrency)
        );

        if (collateralAmount < _request.minTargetAmount) revert InsufficientCollateralReceived();

        _getManager().checkMaxLeverage(
            _request.downPayment,
            collateralAmount,
            _request.currency,
            _request.targetCurrency
        );

        // Check the principal usage and return any excess principal to the vault
        if (amountSpent > _request.principal) {
            revert PrincipalTooHigh();
        } else if (amountSpent < _request.principal) {
            principalToken.safeTransfer(address(vault), _request.principal - amountSpent);
        }
        
        return _finalizePosition(_trader, _request, collateralAmount, amountSpent);
    }

    function addCollateral(
        AddCollateralRequest calldata _request,
        Signature calldata _signature
    ) external payable nonReentrant returns (Position memory) {
        // Validate Request
        _validateAddCollateralRequest(_request, _signature);

        // Validate sender
        if (msg.sender != _request.position.trader) {
            if (msg.sender != address(_getWasabiRouter())) {
                revert SenderNotTrader();
            }
        }

        // Update position
        Position memory position = _request.position;
        position.downPayment += _request.amount;
        position.collateralAmount += _request.amount;
        positions[_request.position.id] = position.hash();

        emit CollateralAdded(_request.position.id, _request.position.trader, _request.amount, _request.amount, 0, 0);

        return position;
    }

    /// @inheritdoc IWasabiPerps
    function removeCollateral(
        RemoveCollateralRequest calldata _request,
        Signature calldata _signature
    ) external virtual payable nonReentrant returns (Position memory) {
        // Validate Request
        _validateRemoveCollateralRequest(_request, _signature);

        // Validate sender
        if (msg.sender != _request.position.trader) {
            revert SenderNotTrader();
        }

        // Reduce collateral amount and down payment
        Position memory position = _request.position;
        position.collateralAmount -= _request.amount;
        positions[_request.position.id] = position.hash();

        // Pay out amount to the trader
        IERC20(position.collateralCurrency).safeTransfer(_request.position.trader, _request.amount);

        emit CollateralRemoved(_request.position.id, _request.position.trader, 0, _request.amount, 0);

        return position;
    }

    /// @inheritdoc IWasabiPerps
    function closePosition(
        PayoutType _payoutType,
        ClosePositionRequest calldata _request,
        Signature calldata _signature,
        ClosePositionOrder calldata _order,
        bytes calldata _orderSignature // signed by trader
    ) external payable nonReentrant onlyRole(Roles.ORDER_EXECUTOR_ROLE) {
        uint256 id = _request.position.id;
        if (id != _order.positionId) revert InvalidOrder();
        if (_order.expiration < block.timestamp) revert OrderExpired();
        if (_request.expiration < block.timestamp) revert OrderExpired();

        address trader = _request.position.trader;
        _validateSigner(trader, _order.hash(), _orderSignature);
        _validateSignature(_request.hash(), _signature);

        ClosePositionInternalArgs memory args = ClosePositionInternalArgs({
            _interest: _request.interest,
            _amount: _request.amount,
            _executionFee: _order.executionFee,
            _payoutType: _payoutType,
            _isLiquidation: false,
            _referrer: _request.referrer
        });
        CloseAmounts memory closeAmounts = _closePositionInternal(
            args, 
            _request.position, 
            _request.functionCallDataList
        );

        uint256 interestPaid = closeAmounts.interestPaid;
        uint256 principalRepaid = closeAmounts.principalRepaid;
        uint256 actualMakerAmount = closeAmounts.collateralSold;
        uint256 actualTakerAmount = interestPaid + principalRepaid;

        // order price      = order.makerAmount / order.takerAmount
        // executed price   = actualMakerAmount / actualTakerAmount
        // TP: executed price <= order price
        //      actualMakerAmount / actualTakerAmount <= order.makerAmount / order.takerAmount
        //      actualMakerAmount * order.takerAmount <= order.makerAmount * actualTakerAmount
        // SL: executed price >= order price
        //      actualMakerAmount / actualTakerAmount >= order.makerAmount / order.takerAmount
        //      actualMakerAmount * order.takerAmount >= order.makerAmount * actualTakerAmount

        uint8 orderType = _order.orderType;
        if (orderType > 1) {
            revert InvalidOrder();
        } else if (orderType == 0 
            ? actualMakerAmount * _order.takerAmount > _order.makerAmount * actualTakerAmount
            : actualMakerAmount * _order.takerAmount < _order.makerAmount * actualTakerAmount
        ) {
            revert PriceTargetNotReached();
        }

        if (positions[id] == CLOSED_POSITION_HASH) {
            emit PositionClosedWithOrder(
                id,
                trader,
                orderType,
                closeAmounts.payout,
                principalRepaid,
                interestPaid,
                closeAmounts.closeFee
            );
        } else {
            emit PositionDecreasedWithOrder(
                id, 
                trader, 
                orderType,
                closeAmounts.payout,
                principalRepaid,
                interestPaid,
                closeAmounts.closeFee,
                closeAmounts.pastFees,
                closeAmounts.collateralReduced,
                closeAmounts.downPaymentReduced
            );
        }
    }

    /// @inheritdoc IWasabiPerps
    function closePosition(
        PayoutType _payoutType,
        ClosePositionRequest calldata _request,
        Signature calldata _signature
    ) external payable nonReentrant {
        _validateSignature(_request.hash(), _signature);
        address trader = _request.position.trader;
        _checkCanClosePosition(trader);
        if (_request.expiration < block.timestamp) revert OrderExpired();
        
        ClosePositionInternalArgs memory args = ClosePositionInternalArgs({
            _interest: _request.interest,
            _amount: _request.amount,
            _executionFee: 0,
            _payoutType: _payoutType,
            _isLiquidation: false,
            _referrer: _request.referrer
        });
        CloseAmounts memory closeAmounts = _closePositionInternal(
            args, 
            _request.position, 
            _request.functionCallDataList
        );

        uint256 id = _request.position.id;
        if (positions[id] == CLOSED_POSITION_HASH) {
            emit PositionClosed(
                id,
                trader,
                closeAmounts.payout,
                closeAmounts.principalRepaid,
                closeAmounts.interestPaid,
                closeAmounts.closeFee
            );
        } else {
            emit PositionDecreased(
                id, 
                trader, 
                closeAmounts.payout,
                closeAmounts.principalRepaid,
                closeAmounts.interestPaid,
                closeAmounts.closeFee,
                closeAmounts.pastFees,
                closeAmounts.collateralReduced,
                closeAmounts.downPaymentReduced
            );
        }
    }

    /// @inheritdoc IWasabiPerps
    function liquidatePosition(
        PayoutType _payoutType,
        uint256 _interest,
        Position calldata _position,
        FunctionCallData[] calldata _swapFunctions,
        address _referrer
    ) external payable nonReentrant onlyRole(Roles.LIQUIDATOR_ROLE) {
        ClosePositionInternalArgs memory args = ClosePositionInternalArgs({
            _interest: _interest,
            _amount: 0,
            _executionFee: 0,
            _payoutType: _payoutType,
            _isLiquidation: true,
            _referrer: _referrer
        });
        CloseAmounts memory closeAmounts =
            _closePositionInternal(args, _position, _swapFunctions);
        uint256 liquidationThreshold = _getManager().getLiquidationThreshold(
            _position.currency, 
            _position.collateralCurrency, 
            _position.collateralAmount
        );
        if (closeAmounts.payout + closeAmounts.liquidationFee > liquidationThreshold) revert LiquidationThresholdNotReached();

        emit PositionLiquidated(
            _position.id,
            _position.trader,
            closeAmounts.payout,
            closeAmounts.principalRepaid,
            closeAmounts.interestPaid,
            closeAmounts.closeFee
        );
    }

    /// @inheritdoc IWasabiPerps
    function recordInterest(Position[] calldata _positions, uint256[] calldata _interests, FunctionCallData[] calldata _swapFunctions) external nonReentrant onlyRole(Roles.ORDER_EXECUTOR_ROLE) {
        if (_positions.length != _interests.length) revert InvalidInput();
        if (_swapFunctions.length == 0) revert SwapFunctionNeeded(); // Swap functions are needed for short interest

        uint256 length = _positions.length;
        address currency = _positions[0].currency;
        address collateralCurrency = _positions[0].collateralCurrency;
        uint256 totalInterest;

        for (uint256 i = 0; i < length; ) {
            Position memory position = _positions[i];
            if (positions[position.id] != position.hash()) revert InvalidPosition();
            if (position.currency != currency) revert InvalidCurrency();
            if (position.collateralCurrency != collateralCurrency) revert InvalidTargetCurrency();

            uint256 interest = _interests[i];
            uint256 maxInterest = _getManager()
                .computeMaxInterest(position.currency, position.principal, position.lastFundingTimestamp);
            if (interest > maxInterest || interest == 0) revert InvalidInterestAmount();

            totalInterest += interest;

            unchecked {
                i++;
            }
        }

        (uint256 collateralSold, uint256 interestReceived) = PerpUtils.executeSwapFunctions(
            _swapFunctions,
            IERC20(collateralCurrency),
            IERC20(currency)
        );

        if (interestReceived != totalInterest) revert InsufficientPrincipalRepaid();

        for (uint256 i = 0; i < length; ) {
            Position memory position = _positions[i];
            uint256 interest = _interests[i];

            uint256 collateralReduced = i == length - 1 ? collateralSold : collateralSold.mulDiv(interest, totalInterest);
            uint256 downPaymentReduced = collateralReduced.mulDiv(position.downPayment, position.collateralAmount);

            position.collateralAmount -= collateralReduced;
            position.downPayment -= downPaymentReduced;
            position.lastFundingTimestamp = block.timestamp;
            positions[position.id] = position.hash();

            collateralSold -= collateralReduced;
            totalInterest -= interest;
            
            emit InterestPaid(
                position.id,
                interest,
                0,
                collateralReduced,
                downPaymentReduced
            );

            unchecked {
                i++;
            }
        }

        // Transfers the interest to the vault and records it (with 0 principal repaid)
        _recordRepayment(0, currency, false, 0, interestReceived);
    }

    /// @dev Closes a given position
    /// @param _args the close position arguments
    /// @param _position the position
    /// @param _swapFunctions the swap functions
    /// @return closeAmounts the close amounts
    function _closePositionInternal(
        ClosePositionInternalArgs memory _args,
        Position calldata _position,
        FunctionCallData[] calldata _swapFunctions
    ) internal virtual returns (CloseAmounts memory closeAmounts) {
        uint256 id = _position.id;
        if (positions[id] != _position.hash()) revert InvalidPosition();
        if (_swapFunctions.length == 0) revert SwapFunctionNeeded();

        uint256 principal = _position.principal;
        uint256 collateralAmount = _position.collateralAmount;
        uint256 downPayment = _position.downPayment;

        if (_args._amount == 0 || _args._amount > principal) {
            _args._amount = principal;
        }
        _args._interest = _computeInterest(_position, _args._interest);

        // Sell tokens
        (closeAmounts.collateralSold, closeAmounts.principalRepaid) = PerpUtils.executeSwapFunctions(
            _swapFunctions,
            IERC20(_position.collateralCurrency),
            IERC20(_position.currency)
        );

        if (closeAmounts.collateralSold > collateralAmount) revert TooMuchCollateralSpent();

        // 1. Deduct interest
        (closeAmounts.interestPaid, closeAmounts.principalRepaid) = PerpUtils.deduct(closeAmounts.principalRepaid, _args._amount);
        if (!_args._isLiquidation && closeAmounts.principalRepaid < _args._amount) revert InsufficientPrincipalRepaid();
        
        if (_args._amount == principal) {
            // Full close
            // Payout and fees are paid in collateral
            closeAmounts.pastFees = _position.feesToBePaid;
            closeAmounts.downPaymentReduced = downPayment;
            closeAmounts.collateralReduced = collateralAmount;
            (closeAmounts.payout, ) = PerpUtils.deduct(collateralAmount, closeAmounts.collateralSold);
        } else {
            // Partial close
            // Scale the collateral by the fraction of the principal repaid
            closeAmounts.collateralReduced = collateralAmount * closeAmounts.principalRepaid / principal;
            closeAmounts.downPaymentReduced = downPayment * closeAmounts.principalRepaid / principal;
            closeAmounts.pastFees = _position.feesToBePaid * closeAmounts.principalRepaid / principal;
            (closeAmounts.payout, ) = PerpUtils.deduct(closeAmounts.collateralReduced, closeAmounts.collateralSold);
        }

        if (closeAmounts.interestPaid > 0) {
            _validateDifference(_args._interest, closeAmounts.interestPaid, 3);
        }

        // 2. Deduct fees
        (closeAmounts.payout, closeAmounts.closeFee) =
            PerpUtils.deduct(
                closeAmounts.payout,
                PerpUtils.computeCloseFee(_position, closeAmounts.payout + closeAmounts.collateralReduced, isLongPool) + _args._executionFee);

        // 3. Deduct liquidation fee
        if (_args._isLiquidation) {
            (closeAmounts.payout, closeAmounts.liquidationFee) = PerpUtils.deduct(
                closeAmounts.payout, 
                _getManager().getLiquidationFee(downPayment, _position.currency, _position.collateralCurrency)
            );
        }
        
        // Repay principal + interest to the vault
        _recordRepayment(
            _args._amount,
            _position.currency,
            _args._isLiquidation,
            closeAmounts.principalRepaid,
            closeAmounts.interestPaid
        );

        _payCloseAmounts(
            _args._payoutType,
            _position.collateralCurrency,
            _position.trader,
            _args._referrer,
            closeAmounts
        );

        if (closeAmounts.principalRepaid < principal && !_args._isLiquidation) {
            Position memory position = _position;
            position.downPayment -= closeAmounts.downPaymentReduced;
            position.principal -= closeAmounts.principalRepaid;
            position.collateralAmount -= closeAmounts.collateralReduced;
            position.feesToBePaid -= closeAmounts.pastFees;
            positions[id] = position.hash();
        } else {
            positions[id] = CLOSED_POSITION_HASH;
        }
    }


    function _finalizePosition(
        address _trader,
        OpenPositionRequest calldata _request,
        uint256 _collateralAmount,
        uint256 _amountSpent
    ) internal returns (Position memory) {
        bool isEdit = _request.existingPosition.id != 0;
        uint256 id = isEdit ? _request.existingPosition.id : _request.id;

        Position memory position = Position(
            id,
            _trader,
            _request.currency,
            _request.targetCurrency,
            isEdit ? _request.existingPosition.lastFundingTimestamp : block.timestamp,
            _request.existingPosition.downPayment + _request.downPayment,
            _request.existingPosition.principal + _amountSpent,
            _request.existingPosition.collateralAmount + _collateralAmount + _request.downPayment,
            _request.existingPosition.feesToBePaid + _request.fee
        );

        positions[id] = position.hash();

        if (isEdit) {
            emit PositionIncreased(
                id, 
                _trader,
                _request.downPayment, 
                _amountSpent, 
                _collateralAmount + _request.downPayment, 
                _request.fee
            );
        } else {
            emit PositionOpened(
                id,
                _trader,
                _request.currency,
                _request.targetCurrency,
                _request.downPayment,
                _amountSpent,
                position.collateralAmount,
                _request.fee
            );
        }

        return position;
    }

    /// @dev Validates if the value is deviated x percentage from the value to compare
    /// @param _value the value
    /// @param _valueToCompare the value to compare
    /// @param _percentage the percentage difference
    function _validateDifference(uint256 _value, uint256 _valueToCompare, uint256 _percentage) internal pure {
        // Check if interest paid is within 3% range of expected interest
        uint256 diff = _value >= _valueToCompare ? _value - _valueToCompare : _valueToCompare - _value;
        if (diff * 100 > _percentage * _value) revert ValueDeviatedTooMuch();
    }
}
IWETH.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint) external;
}

Read Contract

UPGRADE_INTERFACE_VERSION 0xad3cb1cc → string
_deprecated_addressProvider 0xf4b70990 → address
eip712Domain 0x84b0196e → bytes1, string, string, uint256, address, bytes32, uint256[]
getVault 0x0eb9af38 → address
isLongPool 0x21de18f1 → bool
owner 0x8da5cb5b → address
positions 0x99fbab88 → bytes32
proxiableUUID 0x52d1902d → bytes32
quoteTokens 0x45aab48a → bool
vaults 0xa622ee7c → address

Write Contract 15 functions

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

__BaseWasabiPool_init 0xe1ca8f15
bool _isLongPool
address _manager
addCollateral 0xdf200aef
tuple _request
tuple _signature
returns: tuple
addQuoteToken 0x57581511
address _token
addVault 0x256b5a02
address _vault
closePosition 0x1d10f5e6
uint8 _payoutType
tuple _request
tuple _signature
closePosition 0xe4437c70
uint8 _payoutType
tuple _request
tuple _signature
tuple _order
bytes _orderSignature
initialize 0xc4d66de8
address _manager
liquidatePosition 0x7325c9af
uint8 _payoutType
uint256 _interest
tuple _position
tuple[] _swapFunctions
address _referrer
openPosition 0xfeaea850
tuple _request
tuple _signature
returns: tuple
openPositionFor 0xe1d949c1
tuple _request
tuple _signature
address _trader
returns: tuple
recordInterest 0xf26c56e1
tuple[] _positions
uint256[] _interests
tuple[] _swapFunctions
removeCollateral 0x7f590923
tuple _request
tuple _signature
returns: tuple
renounceOwnership 0x715018a6
No parameters
transferOwnership 0xf2fde38b
address newOwner
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data

Recent Transactions

No transactions found for this address