Address Contract Verified
Address
0xd5a09A0827c3D91E8530dEEE3b73eDAE6b33ae95
Balance
0 ETH
Nonce
1
Code Size
23722 bytes
Creator
0x5C629f8C...B0c8 at tx 0xe0d679f7...11c214
Indexed Transactions
0
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