Forkchoice Ethereum Mainnet

Address Contract Partially Verified

Address 0x09e8adfa8D829DaC1c305544A86b53Ed0dDD536a
Balance 0 ETH
Nonce 1
Code Size 24556 bytes
Indexed Transactions 0 (1 on-chain, 1.3% indexed)
External Etherscan · Sourcify

Contract Bytecode

24556 bytes
0x608060405234801561001057600080fd5b50600436106102e45760003560e01c8063775c300c11610189578063a9059cbb116100df578063a9059cbb14610606578063aa9239f514610619578063aced16611461062c578063b6b55f2514610640578063bfa4c00c14610653578063c215bc4d14610667578063c2ee3a0814610671578063cc03c2e314610679578063ce74602414610683578063db006a751461068b578063dd62ed3e1461069e578063e1a2209f146106b1578063e36894f9146106c4578063eafe7a74146106d7578063f2fde38b146106df57600080fd5b8063775c300c1461051757806379cc67901461051f5780637c0ca111146105325780637d7c2a1c14610552578063825890381461055a5780638456cb591461056e5780638da5cb5b1461057657806393334d2e1461057e57806395d89b4114610588578063977902171461059057806397b3fcaa146105a35780639db5dbe4146105ab578063a2b29206146105be578063a457c2d7146105e0578063a8f4cd51146105f357600080fd5b80633aa180881161023e5780633aa18088146103f85780633f280af8146104205780633f45e08f1461042a5780633f4ba83a1461043457806342966c681461043c5780635c975abb1461044f57806362b232a41461045a57806369f6f92c1461046d5780636df71ab3146104805780636f307dc3146104a257806370a08231146104c3578063715018a6146104ec57806372617687146104f45780637375c5d6146104fc57806375d5179f1461050f57600080fd5b806306fdde03146102e957806308e9ff2314610307578063095ea7b3146103115780630cd865ec146103345780630e07f8541461034757806318160ddd1461035a5780631cd66a951461036c57806323b872dd1461037f5780632ad537e314610392578063306cf684146103a5578063313ce567146103ad57806333bce7f7146103c25780633434dc4e146103d557806339509351146103dd57806339697825146103f0575b600080fd5b6102f16106f2565b6040516102fe91906154f5565b60405180910390f35b61030f610784565b005b61032461031f36600461553d565b6107b7565b60405190151581526020016102fe565b61030f610342366004615569565b6107d1565b61030f610355366004615663565b6108a9565b6035545b6040519081526020016102fe565b61035e61037a3660046156eb565b610ab5565b61032461038d366004615704565b610c81565b61035e6103a0366004615569565b610ca7565b61030f610df5565b60125b60405160ff90911681526020016102fe565b61035e6103d0366004615745565b610e2f565b61035e610e9b565b6103246103eb36600461553d565b610f4f565b6103b0602f81565b61040b6104063660046156eb565b610f71565b604080519283526020830191909152016102fe565b61035e6101365481565b61035e6101305481565b61030f6111bd565b61030f61044a3660046156eb565b6111f3565b60c95460ff16610324565b610324610468366004615569565b611200565b61030f61047b366004615569565b611227565b61049361048e3660046156eb565b6112da565b6040516102fe93929190615767565b61012d546104b6906001600160a01b031681565b6040516102fe919061578a565b61035e6104d1366004615569565b6001600160a01b031660009081526033602052604090205490565b61030f611522565b61030f611534565b61035e61050a366004615569565b611544565b6103b0600881565b61030f6115c7565b61030f61052d36600461553d565b611782565b610545610540366004615745565b61179b565b6040516102fe91906157ed565b61030f611abc565b610132546104b6906001600160a01b031681565b61030f611bba565b6104b6611bee565b61035e6101345481565b6102f1611bfd565b61030f61059e366004615569565b611c0c565b61035e611c37565b61030f6105b9366004615704565b611e11565b6105d16105cc366004615745565b611e85565b6040516102fe93929190615800565b6103246105ee36600461553d565b612333565b61030f61060136600461581f565b6123b9565b61032461061436600461553d565b6123f6565b6104b66106273660046156eb565b612404565b610133546104b6906001600160a01b031681565b61035e61064e3660046156eb565b612461565b610131546104b6906001600160a01b031681565b61035e6101355481565b61035e61269c565b61035e6298968081565b61030f6126ab565b6105456106993660046156eb565b612989565b61035e6106ac36600461584b565b612c73565b6104936106bf3660046156eb565b612c9e565b61035e6106d23660046156eb565b612ddf565b61035e613030565b61030f6106ed366004615569565b61304d565b60606036805461070190615884565b80601f016020809104026020016040519081016040528092919081815260200182805461072d90615884565b801561077a5780601f1061074f5761010080835404028352916020019161077a565b820191906000526020600020905b81548152906001019060200180831161075d57829003601f168201915b5050505050905090565b610133546001600160a01b031633146107b057604051637bf6a16f60e01b815260040160405180910390fd5b4261013655565b6000336107c58185856130c3565b60019150505b92915050565b600260fb54036107fc5760405162461bcd60e51b81526004016107f3906158be565b60405180910390fd5b600260fb556108096131e7565b61081561012e8261322d565b1561083f576108238161324f565b61012d54610839906001600160a01b0316613485565b506108a1565b610131546001600160a01b039081169082168190036108885761086181613535565b61086a81613485565b5061012d54610881906001600160a01b0316613485565b50506108a1565b604051632c88119960e11b815260040160405180910390fd5b50600160fb55565b600054610100900460ff16158080156108c95750600054600160ff909116105b806108e35750303b1580156108e3575060005460ff166001145b6109465760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016107f3565b6000805460ff191660011790558015610969576000805461ff0019166101001790555b610973858561367a565b61097b6136ab565b6109836136d2565b61098b613701565b610993613730565b826001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f591906158f5565b61012d80546001600160a01b03199081166001600160a01b03938416179091556101318054909116918516919091179055610a2f82611227565b610a3a61059e611bee565b600061013081905561013481905561013555426101365561012d54610a67906001600160a01b0316613485565b508015610aae576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050565b6000600260fb5403610ad95760405162461bcd60e51b81526004016107f3906158be565b600260fb55610ae66131e7565b6101315461012d546040516370a0823160e01b81526001600160a01b03928316929091169060009082906370a0823190610b2490309060040161578a565b602060405180830381865afa158015610b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b659190615912565b90506000610b7286612c9e565b5090915050801580610b82575085155b15610ba05760405163173825e960e21b815260040160405180910390fd5b610bb56001600160a01b03851633308961375f565b610bbe84613535565b610bd26001600160a01b03841633836137b7565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190610c0190309060040161578a565b602060405180830381865afa158015610c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c429190615912565b905082811015610c655760405163bb55fd2760e01b815260040160405180910390fd5b610c6e84613485565b50909450505050505b600160fb55919050565b600033610c8f8582856137d6565b610c9a85858561384a565b60019150505b9392505050565b600080826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610cd6919061578a565b602060405180830381865afa158015610cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d179190615912565b61012d549091506001600160a01b0390811690841603610d375792915050565b610d4361012e8461322d565b15610dec5782629896808211610d5a576000610de4565b61012d5460405163bb23660160e01b815273e0028c40c8a09449852ea4d2e9aa4d25895f285f9163bb23660191610da39185916001600160a01b0390911690879060040161592b565b602060405180830381865af4158015610dc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de49190615912565b949350505050565b50600092915050565b610133546001600160a01b03163314610e2157604051637bf6a16f60e01b815260040160405180910390fd5b6001600160401b0361013655565b600080610e3b60355490565b905060008111610e5757610e52620f424085615965565b610e6b565b610e6b84610e63611c37565b839190613a06565b9150610de483610e7d6008600a615a60565b610e879190615a6f565b610e936008600a615a60565b849190613a06565b6000600260fb5403610ebf5760405162461bcd60e51b81526004016107f3906158be565b600260fb5561013254610131546001600160a01b039182169163e55a0edf91610ee89116613ab5565b6040518263ffffffff1660e01b8152600401610f049190615a82565b602060405180830381865afa158015610f21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f459190615912565b9050600160fb5590565b6000336107c5818585610f628383612c73565b610f6c9190615a99565b6130c3565b600080600260fb5403610f965760405162461bcd60e51b81526004016107f3906158be565b600260fb55610fa36131e7565b6101315461012d546001600160a01b0391821691166000610fc383613ab5565b9050600061106c610fd66008600a615a60565b61013260009054906101000a90046001600160a01b03166001600160a01b031663d0b976d16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561102a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104e9190615912565b61105a6008600a615a60565b6110649190615a99565b899190613a06565b9050600061107a8289615a6f565b90506110eb856001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e19190615912565b8451849190613a06565b96506110f8816000610e2f565b955061110f6001600160a01b03851633308b61375f565b61118385858560000151886001600160a01b0316638b5393826040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611159573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117d9190615912565b8b613b41565b6111976001600160a01b03861633896137b7565b6111a13387613e81565b6111aa84613485565b505050505050600160fb81905550915091565b610133546001600160a01b031633146111e957604051637bf6a16f60e01b815260040160405180910390fd5b6111f1613f4e565b565b6111fd3382613f9a565b50565b61012d546000906001600160a01b03838116911614806107cb57506107cb61012e8361322d565b61122f6140d6565b600860ff16816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611272573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112969190615aac565b60ff16146112b75760405163c51d798560e01b815260040160405180910390fd5b61013280546001600160a01b0319166001600160a01b0392909216919091179055565b6000806112e5615474565b610131546001600160a01b031660006112fd82613ab5565b90506000611370836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611342573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113669190615912565b8351899190613a06565b6101325460405163e55a0edf60e01b81529192506000916001600160a01b0390911690635bb899d690829063e55a0edf906113af908890600401615a82565b602060405180830381865afa1580156113cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f09190615912565b61013260009054906101000a90046001600160a01b03166001600160a01b031663e55a0edf60405180604001604052808e8a600001516114309190615a99565b815260200189602001518152506040518263ffffffff1660e01b81526004016114599190615a82565b602060405180830381865afa158015611476573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149a9190615912565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401602060405180830381865afa1580156114db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ff9190615912565b905061151181610e7d6008600a615a60565b986000985092965091945050505050565b61152a6140d6565b6111f16000614135565b61153c6126ab565b6111f16115c7565b600061154f82611200565b61155a5760006107cb565b6040516370a0823160e01b81526001600160a01b038316906370a082319061158690309060040161578a565b602060405180830381865afa1580156115a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cb9190615912565b600260fb54036115e95760405162461bcd60e51b81526004016107f3906158be565b600260fb556115f66131e7565b61012d54610131546040516370a0823160e01b81526001600160a01b03928316929091169060009083906370a082319061163490309060040161578a565b602060405180830381865afa158015611651573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116759190615912565b90506000611689611684611c37565b614187565b9050600081831161169b5760006116a5565b6116a58284615a6f565b90506101305481116116ca5760405163167bb86960e11b815260040160405180910390fd5b611738846001600160a01b0316638fb69c4b6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561170d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173191906158f5565b86836141b2565b61174284866141e0565b61175f5760405163167bb86960e11b815260040160405180910390fd5b61012d54611775906001600160a01b0316613485565b5050600160fb5550505050565b61178d8233836137d6565b6117978282613f9a565b5050565b606060006117a860355490565b905060006117b761012e61447b565b6117c2906001615acf565b90508060ff166001600160401b038111156117df576117df615586565b60405190808252806020026020018201604052801561182457816020015b60408051808201909152600080825260208201528152602001906001900390816117fd5790505b5061012d546040805180820182526001600160a01b0390921680835290516370a0823160e01b81529295509160208201906118bb908990879086906370a082319061187390309060040161578a565b602060405180830381865afa158015611890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b49190615912565b9190613a06565b815250846000815181106118d1576118d1615ae8565b6020908102919091010152611930856118ec6008600a615a60565b6118f69190615a6f565b6119026008600a615a60565b8660008151811061191557611915615ae8565b602002602001015160200151613a069092919063ffffffff16565b8460008151811061194357611943615ae8565b602090810291909101810151015260015b8260ff168160ff161015611ab257600061197e611972600184615afe565b61012e9060ff16614485565b90506040518060400160405280826001600160a01b031681526020016119cb8a88856001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611873919061578a565b815250868360ff16815181106119e3576119e3615ae8565b6020908102919091010152611a29876119fe6008600a615a60565b611a089190615a6f565b611a146008600a615a60565b888560ff168151811061191557611915615ae8565b868360ff1681518110611a3e57611a3e615ae8565b6020026020010151602001818152505062989680868360ff1681518110611a6757611a67615ae8565b6020026020010151602001511015611aa1576000868360ff1681518110611a9057611a90615ae8565b602002602001015160200181815250505b50611aab81615b17565b9050611954565b5050505092915050565b600260fb5403611ade5760405162461bcd60e51b81526004016107f3906158be565b600260fb55611aeb6131e7565b61013260009054906101000a90046001600160a01b03166001600160a01b031663ead5f6306040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b639190615912565b61013654611b719190615a99565b4211611b9057604051630a3b3f0960e41b815260040160405180910390fd5b6101315461012d54611bae916001600160a01b039081169116614491565b4261013655600160fb55565b610133546001600160a01b03163314611be657604051637bf6a16f60e01b815260040160405180910390fd5b6111f16148de565b6097546001600160a01b031690565b60606037805461070190615884565b611c146140d6565b61013380546001600160a01b0319166001600160a01b0392909216919091179055565b61012d546040516370a0823160e01b815260009182916001600160a01b03909116906370a0823190611c6d90309060040161578a565b602060405180830381865afa158015611c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cae9190615912565b90506000611cbd61012e61447b565b905060005b8160ff168160ff161015611e09576000611ce161012e60ff8416614485565b90506000816001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d11919061578a565b602060405180830381865afa158015611d2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d529190615912565b905062989680811115611df65761012d5460405163bb23660160e01b815273e0028c40c8a09449852ea4d2e9aa4d25895f285f9163bb23660191611da89186916001600160a01b0390911690869060040161592b565b602060405180830381865af4158015611dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de99190615912565b611df39086615a99565b94505b505080611e0290615b17565b9050611cc2565b509092915050565b611e196140d6565b600260fb5403611e3b5760405162461bcd60e51b81526004016107f3906158be565b600260fb55611e4983611200565b15611e67576040516328071e2f60e11b815260040160405180910390fd5b611e7b6001600160a01b03841683836137b7565b5050600160fb5550565b6000806060600260fb5403611eac5760405162461bcd60e51b81526004016107f3906158be565b600260fb55611eb96131e7565b61013154604080516318160ddd60e01b815290516001600160a01b039092169160009183916318160ddd916004808201926020929091908290030181865afa158015611f09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2d9190615912565b90506000611f3a60355490565b8896509050611f4a818784613a06565b945086851115611f6557869450611f62828683613a06565b95505b50506000611f7484600061179b565b9050611f803385613f9a565b611f956001600160a01b03831633308861375f565b6101315460405163db006a7560e01b8152600481018790526000916001600160a01b03169063db006a75906024016000604051808303816000875af1158015611fe2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261200a9190810190615b59565b905060018251825161201c9190615a99565b6120269190615a6f565b6001600160401b0381111561203d5761203d615586565b60405190808252806020026020018201604052801561208257816020015b604080518082019091526000808252602082015281526020019060019003908161205b5790505b5093506040518060400160405280826000815181106120a3576120a3615ae8565b6020026020010151600001516001600160a01b03168152602001836000815181106120d0576120d0615ae8565b602002602001015160200151836000815181106120ef576120ef615ae8565b6020026020010151602001516121059190615a99565b8152508460008151811061211b5761211b615ae8565b6020026020010181905250612180338560008151811061213d5761213d615ae8565b6020026020010151602001518660008151811061215c5761215c615ae8565b6020026020010151600001516001600160a01b03166137b79092919063ffffffff16565b60015b815160ff168160ff16101561221b57818160ff16815181106121a7576121a7615ae8565b6020026020010151858260ff16815181106121c4576121c4615ae8565b602002602001018190525061220933868360ff16815181106121e8576121e8615ae8565b602002602001015160200151848460ff168151811061215c5761215c615ae8565b8061221381615b17565b915050612183565b5060015b825160ff168160ff1610156122f857828160ff168151811061224357612243615ae8565b602002602001015185835160018461225b9190615afe565b60ff166122689190615a99565b8151811061227857612278615ae8565b60200260200101819052506122bd33848360ff168151811061229c5761229c615ae8565b602002602001015160200151858460ff168151811061215c5761215c615ae8565b6122e6838260ff16815181106122d5576122d5615ae8565b60200260200101516000015161491b565b806122f081615b17565b91505061221f565b506123208460008151811061230f5761230f615ae8565b602002602001015160000151613485565b50505050600160fb819055509250925092565b600033816123418286612c73565b9050838110156123a15760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016107f3565b6123ae82868684036130c3565b506001949350505050565b610133546001600160a01b031633146123e557604051637bf6a16f60e01b815260040160405180910390fd5b610130929092556101345561013555565b6000336107c581858561384a565b60008160000361242057505061012d546001600160a01b031690565b61242b61012e61447b565b8211612448576107cb61243f600184615a6f565b61012e90614485565b604051632d0483c560e21b815260040160405180910390fd5b6000600260fb54036124855760405162461bcd60e51b81526004016107f3906158be565b600260fb556124926131e7565b610131546000906124ab906001600160a01b0316613ab5565b6101325460405163e55a0edf60e01b81529192506000916001600160a01b0390911690635bb899d690829063e55a0edf906124ea908790600401615a82565b602060405180830381865afa158015612507573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252b9190615912565b6101325460408051808201909152865181526020808801516001600160a01b039093169263e55a0edf9291820190612564908c90615a99565b8152506040518263ffffffff1660e01b81526004016125839190615a82565b602060405180830381865afa1580156125a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c49190615912565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401602060405180830381865afa158015612605573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126299190615912565b905060006126378583610e2f565b9050841580612644575080155b156126555760009350505050610c77565b61012d5461266e906001600160a01b031633308861375f565b6126783382613e81565b61012d5461268e906001600160a01b0316613485565b50600160fb55949350505050565b6126a86008600a615a60565b81565b600260fb54036126cd5760405162461bcd60e51b81526004016107f3906158be565b600260fb556126da6131e7565b60006126e761012e61447b565b905060005b8160ff168160ff16101561293557600061270b61012e60ff8416614485565b90506000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161273b919061578a565b602060405180830381865afa158015612758573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061277c9190615912565b90506000811161278d575050612925565b6000826001600160a01b03166364c9ec6f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f191906158f5565b90506000612807826001600160a01b03166149a0565b9050600061281d836001600160a01b0316614b33565b116128925760405163284ff44560e11b815273e0028c40c8a09449852ea4d2e9aa4d25895f285f9063509fe88a9061285d9085908890889060040161592b565b60006040518083038186803b15801561287557600080fd5b505af4158015612889573d6000803e3d6000fd5b50505050612920565b8051516001600160a01b0385811691161480156128b157506298968083115b1561292057604051631d4dd53d60e31b815273e0028c40c8a09449852ea4d2e9aa4d25895f285f9063ea6ea9e8906128ef9085908590600401615c11565b60006040518083038186803b15801561290757600080fd5b505af415801561291b573d6000803e3d6000fd5b505050505b505050505b61292e81615b17565b90506126ec565b50805b60ff81161561296957612957612952611972600184615afe565b61491b565b8061296181615c88565b915050612938565b5061012d54612980906001600160a01b0316613485565b5050600160fb55565b6060600260fb54036129ad5760405162461bcd60e51b81526004016107f3906158be565b600260fb556129ba6131e7565b60008211612a075760408051600080825260208201909252906129ff565b60408051808201909152600080825260208201528152602001906001900390816129d85790505b509050610c77565b61013154600090612a20906001600160a01b0316613ab5565b90506000612a2d60355490565b6101325460405163e55a0edf60e01b81529192506000916001600160a01b0390911690635bb899d690829063e55a0edf90612a6c908890600401615a82565b602060405180830381865afa158015612a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aad9190615912565b6101325460408051808201909152875181526001600160a01b039091169063e55a0edf9060208101612aee612ae28d8b615a6f565b60208c0151908b613a06565b8152506040518263ffffffff1660e01b8152600401612b0d9190615a82565b602060405180830381865afa158015612b2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b4e9190615912565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401602060405180830381865afa158015612b8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb39190615912565b90506000612bc1868361179b565b9050612bcd3387613f9a565b805160005b8160ff168160ff161015610c6e57828160ff1681518110612bf557612bf5615ae8565b60200260200101516020015160000315612c6357612c2233848360ff168151811061229c5761229c615ae8565b8060ff16600003612c4b57612c45838260ff168151811061230f5761230f615ae8565b50612c63565b612c63838260ff16815181106122d5576122d5615ae8565b612c6c81615b17565b9050612bd2565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b600080612ca9615474565b610131546001600160a01b03166000612cc182613ab5565b90506000612d2f8260000151846001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d0b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110649190615912565b6101325460405163e55a0edf60e01b81529192506000916001600160a01b0390911690635bb899d690829063e55a0edf90612d6e908890600401615a82565b602060405180830381865afa158015612d8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612daf9190615912565b610132546040805180820190915287516001600160a01b039092169163e55a0edf91908190611430908a90615a6f565b6000600260fb5403612e035760405162461bcd60e51b81526004016107f3906158be565b600260fb55612e106131e7565b6101315461012d546040516370a0823160e01b81526001600160a01b03928316929091169060009082906370a0823190612e4e90309060040161578a565b602060405180830381865afa158015612e6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8f9190615912565b9050600080612e9d876112da565b9250509150600082111580612eb0575086155b15612ece5760405163173825e960e21b815260040160405180910390fd5b612ee36001600160a01b03851633308a61375f565b612f5785858360000151886001600160a01b0316638b5393826040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612f2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f519190615912565b86613b41565b612f6b6001600160a01b03861633846137b7565b6040516370a0823160e01b81526000906001600160a01b038616906370a0823190612f9a90309060040161578a565b602060405180830381865afa158015612fb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fdb9190615912565b90508381108015612ff85750612ff48260200151614187565b8111155b156130165760405163bb55fd2760e01b815260040160405180910390fd5b61301f85613485565b5050600160fb555095945050505050565b600061303d61012e61447b565b613048906001615a99565b905090565b6130556140d6565b6001600160a01b0381166130ba5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107f3565b6111fd81614135565b6001600160a01b0383166131255760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016107f3565b6001600160a01b0382166131865760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016107f3565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60c95460ff16156111f15760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016107f3565b6001600160a01b03811660009081526001830160205260408120541515610ca0565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319061327e90309060040161578a565b602060405180830381865afa15801561329b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bf9190615912565b9050600081116132d2576117978261491b565b6000826001600160a01b03166364c9ec6f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613312573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061333691906158f5565b9050600061334c826001600160a01b0316614b33565b116133ca5760405163284ff44560e11b815273e0028c40c8a09449852ea4d2e9aa4d25895f285f9063509fe88a9061338c9084908790879060040161592b565b60006040518083038186803b1580156133a457600080fd5b505af41580156133b8573d6000803e3d6000fd5b505050506133c58361491b565b505050565b6298968082111561347c5760006133e9826001600160a01b03166149a0565b604051631d4dd53d60e31b815290915073e0028c40c8a09449852ea4d2e9aa4d25895f285f9063ea6ea9e8906134259085908590600401615c11565b60006040518083038186803b15801561343d57600080fd5b505af4158015613451573d6000803e3d6000fd5b505082516134699250905060005b602002015161491b565b805161347690600161345f565b50505050565b6133c58361491b565b6040516370a0823160e01b81526000906001600160a01b038316906370a08231906134b490309060040161578a565b602060405180830381865afa1580156134d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134f59190615912565b90507f3498084e435368f22f5e58d4957c351579c10be5ab20874cc78c9d0e28fa04098282604051613528929190615ca5565b60405180910390a1919050565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319061356490309060040161578a565b602060405180830381865afa158015613581573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135a59190615912565b9050600081116135b3575050565b60405163db006a7560e01b8152600481018290526000906001600160a01b0384169063db006a75906024016000604051808303816000875af11580156135fd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136259190810190615b59565b805190915060015b8160ff168160ff161015610aae576000838260ff168151811061365257613652615ae8565b60200260200101516000015190506136698161324f565b5061367381615b17565b905061362d565b600054610100900460ff166136a15760405162461bcd60e51b81526004016107f390615cbe565b6117978282614bb2565b600054610100900460ff166111f15760405162461bcd60e51b81526004016107f390615cbe565b600054610100900460ff166136f95760405162461bcd60e51b81526004016107f390615cbe565b6111f1614bf2565b600054610100900460ff166137285760405162461bcd60e51b81526004016107f390615cbe565b6111f1614c22565b600054610100900460ff166137575760405162461bcd60e51b81526004016107f390615cbe565b6111f1614c55565b613476846323b872dd60e01b8585856040516024016137809392919061592b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614c83565b6133c58363a9059cbb60e01b8484604051602401613780929190615ca5565b60006137e28484612c73565b90506000198114613476578181101561383d5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016107f3565b61347684848484036130c3565b6001600160a01b0383166138ae5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016107f3565b6001600160a01b0382166139105760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016107f3565b6001600160a01b038316600090815260336020526040902054818110156139885760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016107f3565b6001600160a01b038085166000908152603360205260408082208585039055918516815290812080548492906139bf908490615a99565b92505081905550826001600160a01b0316846001600160a01b0316600080516020615f97833981519152846040516139f991815260200190565b60405180910390a3613476565b6000808060001985870985870292508281108382030391505080600003613a4057838281613a3657613a36615d09565b0492505050610ca0565b808411613a4c57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b613abd615474565b6040518060400160405280836001600160a01b03166397b3fcaa6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613b08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b2c9190615912565b8152602001613b39611c37565b905292915050565b6000856001600160a01b0316638fb69c4b6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ba791906158f5565b90506000866001600160a01b031663332d83d56040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613beb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0f91906158f5565b9050600080613dd86040518060c001604052808981526020018b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8a9190615912565b81526020018a6001600160a01b03166370a08231886040518263ffffffff1660e01b8152600401613cbb919061578a565b602060405180830381865afa158015613cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cfc9190615912565b8152602001866001600160a01b031663fc7b9c186040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d639190615912565b8152602001856001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613da6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dca9190615912565b815260200188905286614d55565b91509150613de78489846141b2565b613dfb6001600160a01b0384168a83614e0e565b6040516311f9fbc960e21b81526001600160a01b038a16906347e7ef2490613e299086908590600401615ca5565b6020604051808303816000875af1158015613e48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e6c9190615912565b50613e768361491b565b505050505050505050565b6001600160a01b038216613ed75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016107f3565b8060356000828254613ee99190615a99565b90915550506001600160a01b03821660009081526033602052604081208054839290613f16908490615a99565b90915550506040518181526001600160a01b03831690600090600080516020615f978339815191529060200160405180910390a35050565b613f56614ea0565b60c9805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b604051613f90919061578a565b60405180910390a1565b6001600160a01b038216613ffa5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016107f3565b6001600160a01b0382166000908152603360205260409020548181101561406e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016107f3565b6001600160a01b038316600090815260336020526040812083830390556035805484929061409d908490615a6f565b90915550506040518281526000906001600160a01b03851690600080516020615f978339815191529060200160405180910390a3505050565b336140df611bee565b6001600160a01b0316146111f15760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107f3565b609780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006107cb610134546141ad610135546008600a6141a59190615a60565b869190613a06565b614ee9565b60006141c86001600160a01b0385168484614f00565b90506141d581600061345f565b61347681600161345f565b600080836001600160a01b031663364d22fc6040518163ffffffff1660e01b81526004016000604051808303816000875af1158015614223573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261424b9190810190615d1f565b9050600080856001600160a01b031663332d83d56040518163ffffffff1660e01b81526004016020604051808303816000875af1158015614290573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142b491906158f5565b90506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016142e4919061578a565b602060405180830381865afa158015614301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143259190615912565b905061433b6001600160a01b0383168883614e0e565b835160005b8160ff168160ff161080156143555750600083115b15614465576000868260ff168151811061437157614371615ae8565b6020026020010151905060008a6001600160a01b0316632bf8f1a58784886040518463ffffffff1660e01b81526004016143ad9392919061592b565b60408051808303816000875af11580156143cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143ef9190615dad565b80519091506143ff575050614455565b896001600160a01b0316888460ff168151811061441e5761441e615ae8565b60200260200101516001600160a01b03161461443d5761443d8261324f565b602081015161444c9086615a6f565b94506001965050505b61445e81615b17565b9050614340565b5061446f8361324f565b50919695505050505050565b60006107cb825490565b6000610ca08383614f8d565b604051630ad0753560e11b81526001600160a01b038316906315a0ea6a906144bd90309060040161578a565b600060405180830381600087803b1580156144d757600080fd5b505af11580156144eb573d6000803e3d6000fd5b505050506144f882613535565b600061450383613ab5565b6101325460405163cb826e4d60e01b81529192506000916001600160a01b039091169063cb826e4d9061453a908590600401615a82565b602060405180830381865afa158015614557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061457b9190615912565b905060008112156145fc57836001600160a01b031663f41dd7df61459e83614fb7565b6040518263ffffffff1660e01b81526004016145bc91815260200190565b600060405180830381600087803b1580156145d657600080fd5b505af11580156145ea573d6000803e3d6000fd5b505050506145f784613535565b614749565b6000811315614749576000614677856001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614648573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061466c9190615912565b84516118b485614fce565b90506146ed85858560000151886001600160a01b0316638b5393826040518163ffffffff1660e01b81526004016020604051808303816000875af11580156146c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146e79190615912565b85613b41565b604051630852cd8d60e31b8152600481018290526001600160a01b038616906342966c6890602401600060405180830381600087803b15801561472f57600080fd5b505af1158015614743573d6000803e3d6000fd5b50505050505b6101325460408051632d91602f60e01b815290516000926001600160a01b031691632d91602f9160048083019260209291908290030181865afa158015614794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147b89190615912565b905080156148c357610132546040805163850a150160e01b815290516000926001600160a01b03169163850a15019160048083019260209291908290030181865afa15801561480b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061482f91906158f5565b6101315460405163078aa91160e51b81529192506001600160a01b03169063f1552220906148639084908690600401615ca5565b600060405180830381600087803b15801561487d57600080fd5b505af1158015614891573d6000803e3d6000fd5b505050506148c1816148bc6148a560355490565b856148b26008600a615a60565b6141a59190615a6f565b613e81565b505b6148cc85613485565b506148d684613485565b505050505050565b6148e66131e7565b60c9805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613f833390565b600061492682613485565b9050600061493661012e8461322d565b9050600082118015614946575080155b156149845761495761012e84615020565b50602f61496561012e61447b565b11156133c557604051633d816dad60e01b815260040160405180910390fd5b8115801561498f5750805b156133c55761347661012e84615035565b6149a861548e565b816001600160a01b03166359eb82246040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a0a9190615912565b600214614a2a5760405163673f20ed60e11b815260040160405180910390fd5b6040516313612cb160e11b8152600060048201526001600160a01b038316906326c25962906024016040805180830381865afa158015614a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a929190615ddf565b82516020840151600060200201919091526001600160a01b0391821690526040516313612cb160e11b815260016004820152908316906326c25962906024016040805180830381865afa158015614aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b119190615ddf565b82516020808501518101929092526001600160a01b0392909216910152919050565b600080826001600160a01b031663d59624b46040518163ffffffff1660e01b8152600401602060405180830381865afa158015614b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b989190615912565b9050428111614ba8576000610ca0565b610ca04282615a6f565b600054610100900460ff16614bd95760405162461bcd60e51b81526004016107f390615cbe565b6036614be58382615e53565b5060376133c58282615e53565b600054610100900460ff16614c195760405162461bcd60e51b81526004016107f390615cbe565b6111f133614135565b600054610100900460ff16614c495760405162461bcd60e51b81526004016107f390615cbe565b60c9805460ff19169055565b600054610100900460ff16614c7c5760405162461bcd60e51b81526004016107f390615cbe565b600160fb55565b6000614cd8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661504a9092919063ffffffff16565b8051909150156133c55780806020019051810190614cf69190615f12565b6133c55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f3565b600080600080856020015111614d6b5783614d7f565b84516020860151614d7f9186916001615059565b90506000614d95866080015187604001516150b6565b90506000866080015111614da95781614dbc565b6080860151614dbc908390836001615059565b9150600080876060015111614dd15782614de8565b60408701516060880151614de89185916001615059565b60a0880151909150614e019082906103e8906001615059565b9792965091945050505050565b604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e90614e3f9030908790600401615f34565b602060405180830381865afa158015614e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614e809190615912565b905081811015613476576134766001600160a01b038516846000196150c5565b60c95460ff166111f15760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016107f3565b600081831015614ef95781610ca0565b5090919050565b614f086154b3565b6000614f13856149a0565b9050614f296001600160a01b0385168685614e0e565b60405163b6b55f2560e01b8152600481018490526001600160a01b0386169063b6b55f2590602401600060405180830381600087803b158015614f6b57600080fd5b505af1158015614f7f573d6000803e3d6000fd5b505091519695505050505050565b6000826000018281548110614fa457614fa4615ae8565b9060005260206000200154905092915050565b600080821215614fca57816000036107cb565b5090565b600080821215614fca5760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f73697469766560448201526064016107f3565b6000610ca0836001600160a01b0384166151c8565b6000610ca0836001600160a01b038416615217565b6060610de4848460008561530a565b600080615067868686613a06565b9050600183600281111561507d5761507d615f4e565b14801561509a57506000848061509557615095615d09565b868809115b156150ad576150aa600182615a99565b90505b95945050505050565b6000818310614ef95781610ca0565b80158061513e5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906150fb9030908690600401615f34565b602060405180830381865afa158015615118573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061513c9190615912565b155b6151a95760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016107f3565b6133c58363095ea7b360e01b8484604051602401613780929190615ca5565b600081815260018301602052604081205461520f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556107cb565b5060006107cb565b6000818152600183016020526040812054801561530057600061523b600183615a6f565b855490915060009061524f90600190615a6f565b90508181146152b457600086600001828154811061526f5761526f615ae8565b906000526020600020015490508087600001848154811061529257615292615ae8565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806152c5576152c5615f64565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506107cb565b60009150506107cb565b60608247101561536b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107f3565b6001600160a01b0385163b6153c25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f3565b600080866001600160a01b031685876040516153de9190615f7a565b60006040518083038185875af1925050503d806000811461541b576040519150601f19603f3d011682016040523d82523d6000602084013e615420565b606091505b509150915061543082828661543b565b979650505050505050565b6060831561544a575081610ca0565b82511561545a5782518084602001fd5b8160405162461bcd60e51b81526004016107f391906154f5565b604051806040016040528060008152602001600081525090565b60405180604001604052806154a16154b3565b81526020016154ae6154b3565b905290565b60405180604001604052806002906020820280368337509192915050565b60005b838110156154ec5781810151838201526020016154d4565b50506000910152565b60208152600082518060208401526155148160408501602087016154d1565b601f01601f19169190910160400192915050565b6001600160a01b03811681146111fd57600080fd5b6000806040838503121561555057600080fd5b823561555b81615528565b946020939093013593505050565b60006020828403121561557b57600080fd5b8135610ca081615528565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156155be576155be615586565b60405290565b604051601f8201601f191681016001600160401b03811182821017156155ec576155ec615586565b604052919050565b600082601f83011261560557600080fd5b81356001600160401b0381111561561e5761561e615586565b615631601f8201601f19166020016155c4565b81815284602083860101111561564657600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561567957600080fd5b84356001600160401b038082111561569057600080fd5b61569c888389016155f4565b955060208701359150808211156156b257600080fd5b506156bf878288016155f4565b93505060408501356156d081615528565b915060608501356156e081615528565b939692955090935050565b6000602082840312156156fd57600080fd5b5035919050565b60008060006060848603121561571957600080fd5b833561572481615528565b9250602084013561573481615528565b929592945050506040919091013590565b6000806040838503121561575857600080fd5b50508035926020909101359150565b838152602080820184905282516040830152820151606082015260808101610de4565b6001600160a01b0391909116815260200190565b600081518084526020808501945080840160005b838110156157e257815180516001600160a01b0316885283015183880152604090960195908201906001016157b2565b509495945050505050565b602081526000610ca0602083018461579e565b8381528260208201526060604082015260006150ad606083018461579e565b60008060006060848603121561583457600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561585e57600080fd5b823561586981615528565b9150602083013561587981615528565b809150509250929050565b600181811c9082168061589857607f821691505b6020821081036158b857634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60006020828403121561590757600080fd5b8151610ca081615528565b60006020828403121561592457600080fd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176107cb576107cb61594f565b600181815b808511156159b757816000190482111561599d5761599d61594f565b808516156159aa57918102915b93841c9390800290615981565b509250929050565b6000826159ce575060016107cb565b816159db575060006107cb565b81600181146159f157600281146159fb57615a17565b60019150506107cb565b60ff841115615a0c57615a0c61594f565b50506001821b6107cb565b5060208310610133831016604e8410600b8410161715615a3a575081810a6107cb565b615a44838361597c565b8060001904821115615a5857615a5861594f565b029392505050565b6000610ca060ff8416836159bf565b818103818111156107cb576107cb61594f565b8151815260208083015190820152604081016107cb565b808201808211156107cb576107cb61594f565b600060208284031215615abe57600080fd5b815160ff81168114610ca057600080fd5b60ff81811683821601908111156107cb576107cb61594f565b634e487b7160e01b600052603260045260246000fd5b60ff82811682821603908111156107cb576107cb61594f565b600060ff821660ff8103615b2d57615b2d61594f565b60010192915050565b60006001600160401b03821115615b4f57615b4f615586565b5060051b60200190565b60006020808385031215615b6c57600080fd5b82516001600160401b03811115615b8257600080fd5b8301601f81018513615b9357600080fd5b8051615ba6615ba182615b36565b6155c4565b81815260069190911b82018301908381019087831115615bc557600080fd5b928401925b828410156154305760408489031215615be35760008081fd5b615beb61559c565b8451615bf681615528565b81528486015186820152825260409093019290840190615bca565b6001600160a01b038381168252825160a08301919060209060008286015b6002821015615c505782518516815291830191600191909101908301615c2f565b5050508085015191506060840160005b6002811015615c7d57835182529282019290820190600101615c60565b505050509392505050565b600060ff821680615c9b57615c9b61594f565b6000190192915050565b6001600160a01b03929092168252602082015260400190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b60006020808385031215615d3257600080fd5b82516001600160401b03811115615d4857600080fd5b8301601f81018513615d5957600080fd5b8051615d67615ba182615b36565b81815260059190911b82018301908381019087831115615d8657600080fd5b928401925b82841015615430578351615d9e81615528565b82529284019290840190615d8b565b600060408284031215615dbf57600080fd5b615dc761559c565b82518152602083015160208201528091505092915050565b60008060408385031215615df257600080fd5b8251615dfd81615528565b6020939093015192949293505050565b601f8211156133c557600081815260208120601f850160051c81016020861015615e345750805b601f850160051c820191505b818110156148d657828155600101615e40565b81516001600160401b03811115615e6c57615e6c615586565b615e8081615e7a8454615884565b84615e0d565b602080601f831160018114615eb55760008415615e9d5750858301515b600019600386901b1c1916600185901b1785556148d6565b600085815260208120601f198616915b82811015615ee457888601518255948401946001909101908401615ec5565b5085821015615f025787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615f2457600080fd5b81518015158114610ca057600080fd5b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60008251615f8c8184602087016154d1565b919091019291505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220a1ea120cfd9406878e72198e85d1c3d0766d3af7bc6adc66276fce7d85d003d364736f6c63430008140033

Verified Source Code Partial Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: Yes (50 runs)
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../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.
 *
 * By default, the owner account will be the one that deploys the contract. 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 {
    address private _owner;

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

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

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @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) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
Initializable.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @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]
 * ```
 * 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 Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 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. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _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.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so 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.
     *
     * 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.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _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() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @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.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }
}
PausableUpgradeable.sol 117 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
ReentrancyGuardUpgradeable.sol 75 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../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;

    uint256 private _status;

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

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _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() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
ERC20Upgradeable.sol 395 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}
draft-IERC20PermitUpgradeable.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @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.
 */
interface IERC20PermitUpgradeable {
    /**
     * @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].
     */
    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);
}
ERC20BurnableUpgradeable.sol 52 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
    function __ERC20Burnable_init() internal onlyInitializing {
    }

    function __ERC20Burnable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
IERC20MetadataUpgradeable.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @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);
}
IERC20Upgradeable.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount
    ) external returns (bool);
}
SafeERC20Upgradeable.sol 116 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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(IERC20Upgradeable 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, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
AddressUpgradeable.sol 195 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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(errorMessage);
            }
        }
    }
}
ContextUpgradeable.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../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;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
MathUpgradeable.sol 226 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
     *
     * 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)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 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) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}
SafeCastUpgradeable.sol 1135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @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.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {
    /**
     * @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
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        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
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        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
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        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
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        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
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        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
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        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
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        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
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        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
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        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
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248) {
        require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
        return int248(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
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240) {
        require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
        return int240(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
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232) {
        require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
        return int232(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
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224) {
        require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
        return int224(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
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216) {
        require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
        return int216(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
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208) {
        require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
        return int208(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
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200) {
        require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
        return int200(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
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192) {
        require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
        return int192(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
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184) {
        require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
        return int184(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
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176) {
        require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
        return int176(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
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168) {
        require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
        return int168(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
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160) {
        require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
        return int160(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
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152) {
        require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
        return int152(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
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144) {
        require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
        return int144(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
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136) {
        require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
        return int136(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
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(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
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120) {
        require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
        return int120(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
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112) {
        require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
        return int112(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
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104) {
        require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
        return int104(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
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96) {
        require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
        return int96(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
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88) {
        require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
        return int88(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
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80) {
        require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
        return int80(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
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72) {
        require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
        return int72(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
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(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
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56) {
        require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
        return int56(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
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48) {
        require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
        return int48(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
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40) {
        require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
        return int40(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
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(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
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24) {
        require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
        return int24(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
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(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
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}
SignedMathUpgradeable.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMathUpgradeable {
    /**
     * @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);
        }
    }
}
EnumerableSetUpgradeable.sol 367 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }
}
IBondController.sol 32 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

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

interface IBondController {
    function collateralToken() external view returns (address);

    function maturityDate() external view returns (uint256);

    function creationDate() external view returns (uint256);

    function totalDebt() external view returns (uint256);

    function feeBps() external view returns (uint256);

    function isMature() external view returns (bool);

    function tranches(uint256 i) external view returns (ITranche token, uint256 ratio);

    function trancheCount() external view returns (uint256 count);

    function trancheTokenAddresses(ITranche token) external view returns (bool);

    function deposit(uint256 amount) external;

    function redeem(uint256[] memory amounts) external;

    function mature() external;

    function redeemMature(address tranche, uint256 amount) external;
}
ITranche.sol 8 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface ITranche is IERC20Upgradeable {
    function bond() external view returns (address);
}
CommonTypes.sol 46 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

struct TokenAmount {
    /// @notice The asset token redeemed.
    IERC20Upgradeable token;
    /// @notice The amount redeemed.
    uint256 amount;
}

/// @notice The system TVL parameters.
struct SystemTVL {
    /// @notice The current TVL of perp denominated in the underlying.
    uint256 perpTVL;
    /// @notice The current TVL of the vault denominated in the underlying.
    uint256 vaultTVL;
}

struct RolloverData {
    /// @notice The amount of tokens rolled out.
    uint256 tokenOutAmt;
    /// @notice The amount of trancheIn tokens rolled in.
    uint256 trancheInAmt;
}

/// @notice A data structure to define a numeric Range.
struct Range {
    // @dev Lower bound of the range.
    uint256 lower;
    // @dev Upper bound of the range.
    uint256 upper;
}

/// @notice A data structure to define a geometric Line with two points.
struct Line {
    // @dev x-coordinate of the first point.
    uint256 x1;
    // @dev y-coordinate of the first point.
    uint256 y1;
    // @dev x-coordinate of the second point.
    uint256 x2;
    // @dev y-coordinate of the second point.
    uint256 y2;
}
IBondIssuer.sol 42 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { IBondController } from "./buttonwood/IBondController.sol";

interface IBondIssuer {
    /// @notice Event emitted when a new bond is issued by the issuer.
    /// @param bond The newly issued bond.
    event BondIssued(IBondController bond);

    /// @notice Event emitted when a bond has matured.
    /// @param bond The matured bond.
    event BondMature(IBondController bond);

    /// @notice The address of the underlying collateral token to be used for issued bonds.
    /// @return Address of the collateral token.
    function collateral() external view returns (address);

    /// @notice Invokes `mature` on issued active bonds.
    function matureActive() external;

    /// @notice Issues a new bond if sufficient time has elapsed since the last issue.
    function issue() external;

    /// @notice Checks if a given bond has been issued by the issuer.
    /// @param bond Address of the bond to check.
    /// @return if the bond has been issued by the issuer.
    function isInstance(IBondController bond) external view returns (bool);

    /// @notice Fetches the most recently issued bond.
    /// @return Address of the most recent bond.
    function getLatestBond() external returns (IBondController);

    /// @notice Returns the total number of bonds issued by this issuer.
    /// @return Number of bonds.
    function issuedCount() external view returns (uint256);

    /// @notice The bond address from the issued list by index.
    /// @param index The index of the bond in the issued list.
    /// @return Address of the bond.
    function issuedBondAt(uint256 index) external view returns (IBondController);
}
IERC20Burnable.sol 6 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IERC20Burnable {
    function burn(uint256 value) external;
}
IFeePolicy.sol 36 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { SystemTVL } from "./CommonTypes.sol";

interface IFeePolicy {
    /// @return The percentage of the mint perp tokens to be charged as fees,
    ///         as a fixed-point number with {DECIMALS} decimal places.
    /// @dev Perp mint fees are paid to the vault.
    function computeFeePerc(uint256 drPre, uint256 drPost) external view returns (uint256);

    /// @return Number of decimals representing a multiplier of 1.0. So, 100% = 1*10**decimals.
    function decimals() external view returns (uint8);

    /// @param s The TVL of both the perp and vault systems.
    /// @return The deviation ratio given the system TVL.
    function computeDeviationRatio(SystemTVL memory s) external view returns (uint256);

    /// @return The target system ratio as a fixed-point number with {DECIMALS} decimal places.
    function targetSystemRatio() external view returns (uint256);

    /// @notice Computes magnitude and direction of value flow between perp and the rollover vault
    ///         expressed in underlying tokens.
    /// @param s The TVL of both the perp and vault systems.
    /// @return underlyingAmtIntoPerp The value in underlying tokens, that need to flow from the vault into perp.
    function computeRebalanceAmount(SystemTVL memory s) external view returns (int256 underlyingAmtIntoPerp);

    /// @return The share of the system tvl paid to the protocol owner as fees per rebalance.
    function protocolSharePerc() external view returns (uint256);

    /// @return Frequency of the periodic rebalance operation.
    function rebalanceFreqSec() external view returns (uint256);

    /// @return The fee collector address.
    function protocolFeeCollector() external view returns (address);
}
IPerpetualTranche.sol 157 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

import { IBondIssuer } from "./IBondIssuer.sol";
import { IFeePolicy } from "./IFeePolicy.sol";
import { IBondController } from "./buttonwood/IBondController.sol";
import { ITranche } from "./buttonwood/ITranche.sol";
import { IRolloverVault } from "./IRolloverVault.sol";
import { TokenAmount, RolloverData } from "./CommonTypes.sol";

interface IPerpetualTranche is IERC20Upgradeable {
    //--------------------------------------------------------------------------
    // Events

    /// @notice Event emitted the reserve's current token balance is recorded after change.
    /// @param token Address of token.
    /// @param balance The recorded ERC-20 balance of the token held by the reserve.
    event ReserveSynced(IERC20Upgradeable token, uint256 balance);

    /// @notice Event emitted when the active deposit bond is updated.
    /// @param bond Address of the new deposit bond.
    event UpdatedDepositBond(IBondController bond);

    //--------------------------------------------------------------------------
    // Methods

    /// @notice Deposits tranche tokens into the system and mint perp tokens.
    /// @param trancheIn The address of the tranche token to be deposited.
    /// @param trancheInAmt The amount of tranche tokens deposited.
    /// @return The amount of perp tokens minted.
    function deposit(ITranche trancheIn, uint256 trancheInAmt) external returns (uint256);

    /// @notice Burn perp tokens and redeem the share of reserve assets.
    /// @param perpAmtBurnt The amount of perp tokens burnt from the caller.
    /// @return tokensOut The list of reserve tokens and amounts redeemed.
    function redeem(uint256 perpAmtBurnt) external returns (TokenAmount[] memory tokensOut);

    /// @notice Rotates newer tranches in for reserve tokens.
    /// @param trancheIn The tranche token deposited.
    /// @param tokenOut The reserve token to be redeemed.
    /// @param trancheInAmt The amount of trancheIn tokens deposited.
    /// @return r The rollover amounts in various denominations.
    function rollover(
        ITranche trancheIn,
        IERC20Upgradeable tokenOut,
        uint256 trancheInAmt
    ) external returns (RolloverData memory r);

    /// @notice Sends the collected mint/burn fees to the provided recipient address.
    /// @param to The recipient address.
    function claimFees(address to) external;

    /// @notice Pays the protocol fee collector a share of the TVL.
    /// @param to The recipient address.
    /// @param protocolSharePerc The share of tvl to be paid to the protocol as fees.
    function payProtocolFee(address to, uint256 protocolSharePerc) external;

    /// @notice Debases the value of perp tokens, by transferring value to the vault.
    /// @param underlyingAmtToTransfer The value in underlying tokens to be transferred to the vault.
    function rebalanceToVault(uint256 underlyingAmtToTransfer) external;

    /// @notice External contract that stores a predefined bond config and frequency,
    ///         and issues new bonds when poked.
    /// @return The address of the bond issuer.
    function bondIssuer() external view returns (IBondIssuer);

    /// @notice Reference to the address that has the ability to pause/unpause operations.
    /// @return The address of the keeper.
    function keeper() external view returns (address);

    /// @notice The address of the underlying rebasing ERC-20 collateral token backing the tranches.
    /// @return Address of the underlying collateral token.
    function underlying() external view returns (IERC20Upgradeable);

    /// @return Address of perp's rollover vault.
    function vault() external view returns (IRolloverVault);

    /// @notice The parent bond whose tranches are currently accepted to mint perp tokens.
    /// @return Address of the deposit bond.
    function getDepositBond() external returns (IBondController);

    /// @notice The tranche token contract currently accepted to mint perp tokens.
    /// @return Address of the deposit tranche ERC-20 token.
    function getDepositTranche() external returns (ITranche);

    /// @return The tranche ratio of the current deposit tranche.
    function getDepositTrancheRatio() external returns (uint256);

    /// @notice The policy contract with the fee computation logic for the perp and vault systems.
    /// @return Address of the policy contract.
    function feePolicy() external view returns (IFeePolicy);

    /// @notice Total count of tokens held in the reserve.
    /// @return The reserve token count.
    function getReserveCount() external returns (uint256);

    /// @notice The token address from the reserve list by index.
    /// @param index The index of a token.
    /// @return The reserve token address.
    function getReserveAt(uint256 index) external returns (IERC20Upgradeable);

    /// @notice Checks if the given token is part of the reserve.
    /// @param token The address of a token to check.
    /// @return If the token is part of the reserve.
    function inReserve(IERC20Upgradeable token) external returns (bool);

    /// @notice Fetches the reserve's token balance.
    /// @param token The address of the tranche token held by the reserve.
    /// @return The ERC-20 balance of the reserve token.
    function getReserveTokenBalance(IERC20Upgradeable token) external returns (uint256);

    /// @notice Calculates the reserve's token value,
    ///         in a standard denomination as defined by the implementation.
    /// @param token The address of the tranche token held by the reserve.
    /// @return The value of the reserve token balance held by the reserve, in a standard denomination.
    function getReserveTokenValue(IERC20Upgradeable token) external returns (uint256);

    /// @notice Computes the total value of assets currently held in the reserve.
    /// @return The total value of the perp system, in a standard denomination.
    function getTVL() external returns (uint256);

    /// @notice Fetches the list of reserve tokens which are up for rollover.
    /// @return The list of reserve tokens up for rollover.
    function getReserveTokensUpForRollover() external returns (IERC20Upgradeable[] memory);

    /// @notice Computes the amount of perp tokens minted when `trancheInAmt` `trancheIn` tokens
    ///         are deposited into the system.
    /// @param trancheIn The tranche token deposited.
    /// @param trancheInAmt The amount of tranche tokens deposited.
    /// @return The amount of perp tokens to be minted.
    function computeMintAmt(ITranche trancheIn, uint256 trancheInAmt) external returns (uint256);

    /// @notice Computes the amount reserve tokens redeemed when burning given number of perp tokens.
    /// @param perpAmtBurnt The amount of perp tokens to be burnt.
    /// @return tokensOut The list of reserve tokens and amounts redeemed.
    function computeRedemptionAmts(uint256 perpAmtBurnt) external returns (TokenAmount[] memory tokensOut);

    /// @notice Computes the amount reserve tokens that are rolled out for the given number
    ///         of `trancheIn` tokens rolled in.
    /// @param trancheIn The tranche token rolled in.
    /// @param tokenOut The reserve token to be rolled out.
    /// @param trancheInAmtAvailable The amount of trancheIn tokens rolled in.
    /// @return r The rollover amounts in various denominations.
    function computeRolloverAmt(
        ITranche trancheIn,
        IERC20Upgradeable tokenOut,
        uint256 trancheInAmtAvailable
    ) external returns (RolloverData memory r);

    /// @notice Updates time dependent storage state.
    function updateState() external;

    /// @return The system's current deviation ratio.
    function deviationRatio() external returns (uint256);
}
IRolloverVault.sol 57 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { IVault } from "./IVault.sol";
import { SystemTVL, TokenAmount } from "./CommonTypes.sol";

interface IRolloverVault is IVault {
    /// @notice Gradually transfers value between the perp and vault, to bring the system back into balance.
    /// @dev The rebalance function can be executed at-most once a day.
    function rebalance() external;

    /// @notice Batch operation to mint both perp and rollover vault tokens.
    /// @param underlyingAmtIn The amount of underlying tokens to be tranched.
    /// @return perpAmt The amount of perp tokens minted.
    /// @return vaultNoteAmt The amount of vault notes minted.
    function mint2(uint256 underlyingAmtIn) external returns (uint256 perpAmt, uint256 vaultNoteAmt);

    /// @notice Batch operation to redeem both perp and rollover vault tokens for the underlying collateral and tranches.
    /// @param perpAmtAvailable The amount of perp tokens available to redeem.
    /// @param vaultNoteAmtAvailable The amount of vault notes available to redeem.
    /// @return perpAmtBurnt The amount of perp tokens redeemed.
    /// @return vaultNoteAmtBurnt The amount of vault notes redeemed.
    /// @return returnedTokens The list of asset tokens and amounts returned.
    function redeem2(
        uint256 perpAmtAvailable,
        uint256 vaultNoteAmtAvailable
    ) external returns (uint256 perpAmtBurnt, uint256 vaultNoteAmtBurnt, TokenAmount[] memory returnedTokens);

    /// @notice Allows users to swap their underlying tokens for perps held by the vault.
    /// @param underlyingAmtIn The amount of underlying tokens swapped in.
    /// @return The amount of perp tokens swapped out.
    function swapUnderlyingForPerps(uint256 underlyingAmtIn) external returns (uint256);

    /// @notice Allows users to swap their perp tokens for underlying tokens held by the vault.
    /// @param perpAmtIn The amount of perp tokens swapped in.
    /// @return The amount of underlying tokens swapped out.
    function swapPerpsForUnderlying(uint256 perpAmtIn) external returns (uint256);

    /// @notice Computes the amount of perp tokens that are returned when user swaps a given number of underlying tokens.
    /// @param underlyingAmtIn The number of underlying tokens the user swaps in.
    /// @return perpAmtOut The number of perp tokens returned to the user.
    /// @return perpFeeAmtToBurn The amount of perp tokens to be paid to the perp contract as mint fees.
    /// @return s The pre-swap perp and vault TVL.
    function computeUnderlyingToPerpSwapAmt(
        uint256 underlyingAmtIn
    ) external returns (uint256, uint256, SystemTVL memory);

    /// @notice Computes the amount of underlying tokens that are returned when user swaps a given number of perp tokens.
    /// @param perpAmtIn The number of perp tokens the user swaps in.
    /// @return underlyingAmtOut The number of underlying tokens returned to the user.
    /// @return perpFeeAmtToBurn The amount of perp tokens to be paid to the perp contract as burn fees.
    /// @return s The pre-swap perp and vault TVL.
    function computePerpToUnderlyingSwapAmt(uint256 perpAmtIn) external returns (uint256, uint256, SystemTVL memory);

    /// @return The system's current deviation ratio.
    function deviationRatio() external returns (uint256);
}
IVault.sol 80 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { TokenAmount } from "./CommonTypes.sol";

/*
 *  @title IVault
 *
 *  @notice The standard interface for a generic vault as described by the "Vault Framework".
 *          http://thinking.farm/essays/2022-10-05-mechanical-finance/
 *
 *          Users deposit a "underlying" asset and mint "notes" (or vault shares).
 *          The vault "deploys" underlying asset in a rules-based fashion (through a hard-coded strategy).
 *          It "recovers" deployed assets once the investment matures.
 *
 *          The vault operates through two external poke functions which off-chain keepers can execute.
 *              1) `deploy`: When executed, the vault "puts to work" the underlying assets it holds. The vault
 *                           usually returns other ERC-20 tokens which act as receipts of the deployment.
 *              2) `recover`: When executed, the vault turns in the receipts and retrieves the underlying asset and
 *                            usually collects some yield for this work.
 *
 *          The rules of the deployment and recovery are specific to the vault strategy.
 *
 *          At any time the vault will hold multiple ERC20 tokens, together referred to as the vault's "assets".
 *          They can be a combination of the underlying asset and the deployed assets (receipts).
 *
 *          On redemption users burn their "notes" to receive a proportional slice of all the vault's assets.
 *
 */

interface IVault is IERC20Upgradeable {
    /// @notice Recovers deployed funds and redeploys them.
    function recoverAndRedeploy() external;

    /// @notice Deploys deposited funds.
    function deploy() external;

    /// @notice Recovers deployed funds.
    function recover() external;

    /// @notice Recovers a given deployed asset.
    /// @param token The ERC-20 token address of the deployed asset.
    function recover(IERC20Upgradeable token) external;

    /// @notice Deposits the underlying asset from {msg.sender} into the vault and mints notes.
    /// @param amount The amount tokens to be deposited into the vault.
    /// @return The amount of notes.
    function deposit(uint256 amount) external returns (uint256);

    /// @notice Burns notes and sends a proportional share of vault's assets back to {msg.sender}.
    /// @param notes The amount of notes to be burnt.
    /// @return The list of asset tokens and amounts redeemed.
    function redeem(uint256 notes) external returns (TokenAmount[] memory);

    /// @return The total value of assets currently held by the vault, denominated in a standard unit of account.
    function getTVL() external view returns (uint256);

    /// @param token The address of the asset ERC-20 token held by the vault.
    /// @return The vault's asset token value, denominated in a standard unit of account.
    function getVaultAssetValue(IERC20Upgradeable token) external view returns (uint256);

    /// @notice The ERC20 token that can be deposited into this vault.
    function underlying() external view returns (IERC20Upgradeable);

    /// @return Total count of ERC-20 tokens held by the vault.
    function assetCount() external view returns (uint256);

    /// @param i The index of a token.
    /// @return The vault's asset token address by index.
    function assetAt(uint256 i) external view returns (IERC20Upgradeable);

    /// @param token The address of the asset ERC-20 token held by the vault.
    /// @return The vault's asset token balance.
    function vaultAssetBalance(IERC20Upgradeable token) external view returns (uint256);

    /// @param token The address of a token to check.
    /// @return If the given token is held by the vault.
    function isVaultAsset(IERC20Upgradeable token) external view returns (bool);
}
ProtocolErrors.sol 80 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

//-------------------------------------------------------------------------
// Generic

/// @notice Expected contract call to be triggered by authorized caller.
error UnauthorizedCall();

/// @notice Expected transfer out asset to not be a reserve asset.
error UnauthorizedTransferOut();

/// @notice Expected contract reference to not be `address(0)`.
error UnacceptableReference();

/// @notice Expected interface contract to return a fixed point with a different number of decimals.
error UnexpectedDecimals();

/// @notice Expected asset to be a valid reserve/vault asset.
error UnexpectedAsset();

/// @notice Expected to mint a non-zero amount of notes.
error UnacceptableDeposit();

/// @notice Expected to redeem a non-zero amount of notes.
error UnacceptableRedemption();

/// @notice Updated parameters violate defined constraints.
error UnacceptableParams();

/// @notice Storage array access out of bounds.
error OutOfBounds();

/// @notice Expected the number of reserve assets to be under the limit.
error ReserveCountOverLimit();

/// @notice Expected range to be non-decreasing.
error InvalidRange();

//-------------------------------------------------------------------------
// Perp

/// @notice Expected rollover to be acceptable.
error UnacceptableRollover();

/// @notice Expected supply to be lower than the defined max supply.
error ExceededMaxSupply();

/// @notice Expected the total mint amount per tranche to be lower than the limit.
error ExceededMaxMintPerTranche();

//-------------------------------------------------------------------------
// Vault

/// @notice Expected more underlying token liquidity to perform operation.
error InsufficientLiquidity();

/// @notice Expected to swap non-zero assets.
error UnacceptableSwap();

/// @notice Expected more assets to be deployed.
error InsufficientDeployment();

/// @notice Expected the number of vault assets deployed to be under the limit.
error DeployedCountOverLimit();

/// @notice Expected parent bond to have only 2 children tranches.
error UnacceptableTrancheLength();

/// @notice Not enough time has elapsed since last successful rebalance.
error LastRebalanceTooRecent();

//-------------------------------------------------------------------------
// FeePolicy

/// @notice Expected perc value to be at most (1 * 10**DECIMALS), i.e) 1.0 or 100%.
error InvalidPerc();

/// @notice Expected valid fee function definitions.
error InvalidFees();
BondHelpers.sol 115 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { IBondController } from "../_interfaces/buttonwood/IBondController.sol";
import { ITranche } from "../_interfaces/buttonwood/ITranche.sol";
import { TokenAmount } from "../_interfaces/CommonTypes.sol";
import { UnacceptableDeposit, UnacceptableTrancheLength } from "../_interfaces/ProtocolErrors.sol";

import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { BondTranches } from "./BondTranchesHelpers.sol";
import { ERC20Helpers } from "./ERC20Helpers.sol";

/**
 *  @title BondHelpers
 *
 *  @notice Library with helper functions for ButtonWood's Bond contract.
 *
 */
library BondHelpers {
    using SafeCastUpgradeable for uint256;
    using MathUpgradeable for uint256;
    using ERC20Helpers for IERC20Upgradeable;

    // Replicating value used here:
    // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
    uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000;

    /// @notice Given a bond, calculates the time remaining to maturity.
    /// @param b The address of the bond contract.
    /// @return The number of seconds before the bond reaches maturity.
    function secondsToMaturity(IBondController b) internal view returns (uint256) {
        uint256 maturityDate = b.maturityDate();
        return maturityDate > block.timestamp ? maturityDate - block.timestamp : 0;
    }

    /// @notice Given a bond, retrieves all of the bond's tranches.
    /// @param b The address of the bond contract.
    /// @return bt The bond's tranche data.
    function getTranches(IBondController b) internal view returns (BondTranches memory bt) {
        if (b.trancheCount() != 2) {
            revert UnacceptableTrancheLength();
        }
        (bt.tranches[0], bt.trancheRatios[0]) = b.tranches(0);
        (bt.tranches[1], bt.trancheRatios[1]) = b.tranches(1);
    }

    /// @notice Given a bond, returns the tranche at the specified index.
    /// @param b The address of the bond contract.
    /// @param i Index of the tranche.
    /// @return t The tranche address.
    function trancheAt(IBondController b, uint8 i) internal view returns (ITranche t) {
        (t, ) = b.tranches(i);
    }

    /// @notice Given a bond, returns the address of the most senior tranche.
    /// @param b The address of the bond contract.
    /// @return t The senior tranche address.
    function getSeniorTranche(IBondController b) internal view returns (ITranche t) {
        (t, ) = b.tranches(0);
    }

    /// @notice Given a bond, returns the tranche ratio of the most senior tranche.
    /// @param b The address of the bond contract.
    /// @return r The tranche ratio of the senior most tranche.
    function getSeniorTrancheRatio(IBondController b) internal view returns (uint256 r) {
        (, r) = b.tranches(0);
    }

    /// @notice Helper function to estimate the amount of tranches minted when a given amount of collateral
    ///         is deposited into the bond.
    /// @dev This function is used off-chain services (using callStatic) to preview tranches minted.
    ///      This function assumes that the no fees are withheld for tranching.
    /// @param b The address of the bond contract.
    /// @return The tranche data, an array of tranche amounts.
    function previewDeposit(IBondController b, uint256 collateralAmount) internal view returns (TokenAmount[] memory) {
        if (b.isMature()) {
            revert UnacceptableDeposit();
        }

        BondTranches memory bt = getTranches(b);
        TokenAmount[] memory tranchesOut = new TokenAmount[](2);

        uint256 totalDebt = b.totalDebt();
        uint256 collateralBalance = IERC20Upgradeable(b.collateralToken()).balanceOf(address(b));

        uint256 seniorAmt = collateralAmount.mulDiv(bt.trancheRatios[0], TRANCHE_RATIO_GRANULARITY);
        if (collateralBalance > 0) {
            seniorAmt = seniorAmt.mulDiv(totalDebt, collateralBalance);
        }
        tranchesOut[0] = TokenAmount({ token: bt.tranches[0], amount: seniorAmt });

        uint256 juniorAmt = collateralAmount.mulDiv(bt.trancheRatios[1], TRANCHE_RATIO_GRANULARITY);
        if (collateralBalance > 0) {
            juniorAmt = juniorAmt.mulDiv(totalDebt, collateralBalance);
        }
        tranchesOut[1] = TokenAmount({ token: bt.tranches[1], amount: juniorAmt });

        return tranchesOut;
    }

    /// @notice Helper function which approves underlying tokens and mints tranche tokens by depositing into the provided bond contract.
    /// @return The array of tranche tokens minted.
    function approveAndDeposit(
        IBondController b,
        IERC20Upgradeable underlying_,
        uint256 underlyingAmt
    ) internal returns (ITranche[2] memory) {
        BondTranches memory bt = getTranches(b);
        underlying_.checkAndApproveMax(address(b), underlyingAmt);
        b.deposit(underlyingAmt);
        return bt.tranches;
    }
}
BondTranchesHelpers.sol 62 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { ITranche } from "../_interfaces/buttonwood/ITranche.sol";

import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";

// @dev We assume that all bonds in the system just have 2 tranches, i.e) one senior and one junior.
struct BondTranches {
    ITranche[2] tranches;
    uint256[2] trancheRatios;
}

/**
 *  @title BondTranchesHelpers
 *
 *  @notice Library with helper functions for the bond's retrieved tranche data.
 *
 */
library BondTranchesHelpers {
    using MathUpgradeable for uint256;

    /// @notice For a given bond's tranche data and user address, computes the maximum number of each of the bond's tranches
    ///         the user is able to redeem before the bond's maturity. These tranche amounts necessarily match the bond's tranche ratios.
    /// @param bt The bond's tranche data.
    /// @param u The address to check balance for.
    /// @return An array of tranche token balances.
    function computeRedeemableTrancheAmounts(
        BondTranches memory bt,
        address u
    ) internal view returns (uint256[] memory) {
        uint256[] memory trancheBalsAvailable = new uint256[](2);
        trancheBalsAvailable[0] = bt.tranches[0].balanceOf(u);
        trancheBalsAvailable[1] = bt.tranches[1].balanceOf(u);
        return computeRedeemableTrancheAmounts(bt, trancheBalsAvailable);
    }

    /// @notice For a given bond's tranche data and tranche balances available, computes the maximum number of each of the bond's tranches
    ///         the user is able to redeem before the bond's maturity.
    ///         The returned tranche amounts necessarily match the bond's tranche ratios.
    /// @param bt The bond's tranche data.
    /// @param trancheBalsAvailable The tranche balance of each bond tranche available to be used for redemption.
    /// @return An array of tranche token balances.
    function computeRedeemableTrancheAmounts(
        BondTranches memory bt,
        uint256[] memory trancheBalsAvailable
    ) internal pure returns (uint256[] memory) {
        uint256[] memory trancheAmtsReq = new uint256[](2);

        // We compute the amount of seniors required using all the juniors
        trancheAmtsReq[1] = trancheBalsAvailable[1] - (trancheBalsAvailable[1] % bt.trancheRatios[1]);
        trancheAmtsReq[0] = (trancheAmtsReq[1] * bt.trancheRatios[0]) / bt.trancheRatios[1];

        // If enough seniors aren't available, we compute the amount of juniors required using all the seniors
        if (trancheAmtsReq[0] > trancheBalsAvailable[0]) {
            trancheAmtsReq[0] = trancheBalsAvailable[0] - (trancheBalsAvailable[0] % bt.trancheRatios[0]);
            trancheAmtsReq[1] = (trancheAmtsReq[0] * bt.trancheRatios[1]) / bt.trancheRatios[0];
        }

        return trancheAmtsReq;
    }
}
ERC20Helpers.sol 23 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

/**
 *  @title ERC20Helpers
 *
 *  @notice Library with helper functions for ERC20 Tokens.
 *
 */
library ERC20Helpers {
    using SafeERC20Upgradeable for IERC20Upgradeable;

    /// @notice Checks if the spender has sufficient allowance. If not, approves the maximum possible amount.
    function checkAndApproveMax(IERC20Upgradeable token, address spender, uint256 amount) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (allowance < amount) {
            token.safeApprove(spender, type(uint256).max);
        }
    }
}
PerpHelpers.sol 105 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IBondController } from "../_interfaces/buttonwood/IBondController.sol";

import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { BondHelpers } from "./BondHelpers.sol";

/**
 *  @title PerpHelpers
 *
 *  @notice Library with helper functions for the Perpetual tranche contract.
 *
 */
library PerpHelpers {
    using MathUpgradeable for uint256;
    using BondHelpers for IBondController;

    // Replicating value used here:
    // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
    uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000;

    /// @dev Input data required to estimate the amount of underlying required to mint perps.
    struct MintEstimationParams {
        /// @notice perpTVL The current TVL of perp.
        uint256 perpTVL;
        /// @notice perpSupply The total supply of perp tokens.
        uint256 perpSupply;
        /// @notice depositBondCollateralBalance The total collateral balance of perp's deposit bond.
        uint256 depositBondCollateralBalance;
        /// @notice depositBondTotalDebt The total debt of perp's deposit bond.
        uint256 depositBondTotalDebt;
        /// @notice depositTrancheSupply The total supply of perp's deposit tranche.
        uint256 depositTrancheSupply;
        /// @notice depositTrancheTR The tranche ratio of perp's deposit tranche.
        uint256 depositTrancheTR;
    }

    /// @notice This function estimates the amount of underlying tokens that need to be tranched
    ///         in order to mint the given amount of perp tokens.
    /// @dev If this function errs, it is guaranteed to err by overestimating, i.e) when you tranche the estimated amount
    ///      of underlying tokens, then and use the senior tranches to mint perps,
    ///      you might end up minting slightly more than `perpAmtToMint`.
    /// @param p The estimation input parameters.
    /// @param perpAmtToMint The required number of perp tokens to mint.
    /// @return underylingAmtToTranche The number of underlying tokens to tranche.
    /// @return seniorAmtToDeposit The number of minted seniors to then deposit into perp.
    function estimateUnderlyingAmtToTranche(
        MintEstimationParams memory p,
        uint256 perpAmtToMint
    ) internal pure returns (uint256, uint256) {
        // We assume that:
        //  - Perp's deposit tranche is the most senior tranche in the deposit bond.
        //  - The deposit bond is NOT mature.
        //  - No fees are withheld while tranching

        // Math explanation:
        //
        // Given [Y] underlying tokens,
        // We can create S seniors,
        // S = Y * seniorRatio / bondCDR
        //   = Y * (depositTrancheTR/TRANCHE_RATIO_GRANULARITY) / (depositBondCollateralBalance/depositBondTotalDebt)
        //   = (Y * depositTrancheTR * depositBondTotalDebt) / (TRANCHE_RATIO_GRANULARITY * depositBondCollateralBalance)
        //
        // Given [S] senior tranche tokens,
        // We can mint X perps,
        // X = S * price(senior) / price(perp)
        // X = S * (seniorClaim / seniorSupply) / (perpTVL / perpSupply)
        // X = (S * seniorClaim * perpSupply) / (seniorSupply * perpTVL)
        //
        // Thus given X (perpAmtToMint), we calculate S (seniorAmtToDeposit) and Y (underlyingAmtToTranche)
        //
        // S = (X * perpTVL * seniorSupply) / (perpSupply * seniorClaim)
        //   = X * (perpTVL / perpSupply) * (seniorSupply / seniorClaim)
        //
        // Y = (S * depositBondCollateralBalance * TRANCHE_RATIO_GRANULARITY) / (depositBondTotalDebt * depositTrancheTR)
        //   = S * (depositBondCollateralBalance / depositBondTotalDebt) * (TRANCHE_RATIO_GRANULARITY / depositTrancheTR)
        //

        uint256 seniorAmtToDeposit = (p.perpSupply > 0)
            ? perpAmtToMint.mulDiv(p.perpTVL, p.perpSupply, MathUpgradeable.Rounding.Up)
            : perpAmtToMint;

        uint256 depositTrancheClaim = MathUpgradeable.min(p.depositTrancheSupply, p.depositBondCollateralBalance);
        seniorAmtToDeposit = (p.depositTrancheSupply > 0)
            ? seniorAmtToDeposit.mulDiv(p.depositTrancheSupply, depositTrancheClaim, MathUpgradeable.Rounding.Up)
            : seniorAmtToDeposit;

        uint256 underlyingAmtToTranche = (p.depositBondTotalDebt > 0)
            ? seniorAmtToDeposit.mulDiv(
                p.depositBondCollateralBalance,
                p.depositBondTotalDebt,
                MathUpgradeable.Rounding.Up
            )
            : seniorAmtToDeposit;

        underlyingAmtToTranche = underlyingAmtToTranche.mulDiv(
            TRANCHE_RATIO_GRANULARITY,
            p.depositTrancheTR,
            MathUpgradeable.Rounding.Up
        );

        return (underlyingAmtToTranche, seniorAmtToDeposit);
    }
}
TrancheHelpers.sol 61 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { IBondController } from "../_interfaces/buttonwood/IBondController.sol";
import { ITranche } from "../_interfaces/buttonwood/ITranche.sol";
import { UnacceptableTrancheLength } from "../_interfaces/ProtocolErrors.sol";

import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { BondHelpers } from "./BondHelpers.sol";

/**
 *  @title TrancheHelpers
 *
 *  @notice Library with helper functions for tranche tokens.
 *
 */
library TrancheHelpers {
    using BondHelpers for IBondController;

    /// @notice Given a tranche, calculates the claimable collateral balance backing the tranche supply.
    /// @param tranche Address of the tranche token.
    /// @param collateralToken Address of the tranche's underlying collateral token.
    /// @return The collateral balance and the tranche token supply.
    function getTrancheCollateralization(
        ITranche tranche,
        IERC20Upgradeable collateralToken
    ) internal view returns (uint256, uint256) {
        IBondController bond = IBondController(tranche.bond());

        uint256 trancheSupply = tranche.totalSupply();
        uint256 trancheClaim = 0;

        // When the tranche's parent bond is mature
        if (bond.isMature()) {
            trancheClaim = collateralToken.balanceOf(address(tranche));
            return (trancheClaim, trancheSupply);
        }

        // NOTE: This implementation assumes the bond has only two tranches.
        if (bond.trancheCount() != 2) {
            revert UnacceptableTrancheLength();
        }

        uint256 bondCollateralBalance = collateralToken.balanceOf(address(bond));

        // For junior tranche
        if (bond.trancheAt(1) == tranche) {
            uint256 seniorSupply = bond.totalDebt() - trancheSupply;
            uint256 seniorClaim = MathUpgradeable.min(seniorSupply, bondCollateralBalance);
            trancheClaim = bondCollateralBalance - seniorClaim;
        }
        // For senior tranche
        else {
            // require(bond.trancheAt(0) == tranche);
            trancheClaim = MathUpgradeable.min(trancheSupply, bondCollateralBalance);
        }

        return (trancheClaim, trancheSupply);
    }
}
TrancheManager.sol 72 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IERC20Upgradeable, IBondController, ITranche } from "../_interfaces/IPerpetualTranche.sol";

import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { BondTranches, BondTranchesHelpers } from "./BondTranchesHelpers.sol";
import { TrancheHelpers } from "./TrancheHelpers.sol";
import { BondHelpers } from "./BondHelpers.sol";
import { ERC20Helpers } from "./ERC20Helpers.sol";

/**
 *  @title TrancheManager
 *
 *  @notice Linked external library with helper functions for tranche management.
 *
 *  @dev Proxies which use external libraries are by default NOT upgrade safe.
 *       We guarantee that this linked external library will never trigger selfdestruct,
 *       and this one is.
 *
 */
library TrancheManager {
    // data handling
    using BondHelpers for IBondController;
    using TrancheHelpers for ITranche;
    using BondTranchesHelpers for BondTranches;

    // ERC20 operations
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using ERC20Helpers for IERC20Upgradeable;

    // math
    using MathUpgradeable for uint256;

    //--------------------------------------------------------------------------
    // Helper methods

    /// @notice Low level method that redeems the given mature tranche for the underlying asset.
    ///         It interacts with the button-wood bond contract.
    function execMatureTrancheRedemption(IBondController bond, ITranche tranche, uint256 amount) external {
        if (!bond.isMature()) {
            bond.mature();
        }
        bond.redeemMature(address(tranche), amount);
    }

    /// @notice Low level method that redeems the given tranche for the underlying asset, before maturity.
    ///         If the contract holds sibling tranches with proportional balances, those will also get redeemed.
    ///         It interacts with the button-wood bond contract.
    function execImmatureTrancheRedemption(IBondController bond, BondTranches memory bt) external {
        uint256[] memory trancheAmts = bt.computeRedeemableTrancheAmounts(address(this));

        // NOTE: It is guaranteed that if one tranche amount is zero, all amounts are zeros.
        if (trancheAmts[0] > 0) {
            bond.redeem(trancheAmts);
        }
    }

    /// @notice Computes the value of the given amount of tranche tokens, based on it's current CDR.
    ///         Value is denominated in the underlying collateral.
    function computeTrancheValue(
        address tranche,
        address collateralToken,
        uint256 trancheAmt
    ) external view returns (uint256) {
        (uint256 trancheClaim, uint256 trancheSupply) = ITranche(tranche).getTrancheCollateralization(
            IERC20Upgradeable(collateralToken)
        );
        return trancheClaim.mulDiv(trancheAmt, trancheSupply, MathUpgradeable.Rounding.Up);
    }
}
RolloverVault.sol 1085 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import { IERC20Upgradeable, IPerpetualTranche, IBondController, ITranche, IFeePolicy } from "./_interfaces/IPerpetualTranche.sol";
import { IVault } from "./_interfaces/IVault.sol";
import { IRolloverVault } from "./_interfaces/IRolloverVault.sol";
import { IERC20Burnable } from "./_interfaces/IERC20Burnable.sol";
import { TokenAmount, RolloverData, SystemTVL } from "./_interfaces/CommonTypes.sol";
import { UnauthorizedCall, UnauthorizedTransferOut, UnexpectedDecimals, UnexpectedAsset, OutOfBounds, UnacceptableSwap, InsufficientDeployment, DeployedCountOverLimit, InsufficientLiquidity, LastRebalanceTooRecent } from "./_interfaces/ProtocolErrors.sol";

import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import { SignedMathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol";
import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import { BondTranches, BondTranchesHelpers } from "./_utils/BondTranchesHelpers.sol";
import { TrancheHelpers } from "./_utils/TrancheHelpers.sol";
import { BondHelpers } from "./_utils/BondHelpers.sol";
import { PerpHelpers } from "./_utils/PerpHelpers.sol";
import { ERC20Helpers } from "./_utils/ERC20Helpers.sol";
import { TrancheManager } from "./_utils/TrancheManager.sol";

/*
 *  @title RolloverVault
 *
 *  @notice A vault which performs rollovers on PerpetualTranche (or perp). After rolling over,
 *          it holds the junior tranches to maturity, effectively becoming a perpetual junior tranche.
 *
 *          The vault takes in AMPL or any other rebasing collateral as the "underlying" asset.
 *          It also generates a yield (from entry/exit fees, flash swap liquidity, and rebalancing incentives).
 *
 *          Vault strategy:
 *              1) deploy: The vault deposits the underlying asset into perp's current deposit bond
 *                 to get tranche tokens in return, it then swaps these fresh tranche tokens for
 *                 older tranche tokens (ones mature or approaching maturity) from perp.
 *              2) recover: The vault redeems the tranches it holds for the underlying asset.
 *                 NOTE: It performs both mature and immature redemption. Read more: https://bit.ly/3tuN6OC
 *
 *          The vault provides perp<>underlying swap liquidity and charges a fee.
 *          The swap fees are an additional source of yield for vault note holders.
 *
 *          The vault has a "rebalance" operation (which can be executed at most once a day).
 *          This is intended to balance demand for holding perp tokens with
 *          the demand for holding vault notes, such that the vault is always sufficiently capitalized.
 *
 *
 * @dev When new tranches are added into the system, always double check if they are not malicious
 *      by only accepting one whitelisted by perp (ones part of perp's deposit bond or ones part of the perp reserve).
 *
 *      We use `_syncAsset` and `_syncDeployedAsset` to keep track of tokens entering and leaving the system.
 *      When ever a tranche token enters or leaves the system, we immediately invoke `_syncDeployedAsset` to update book-keeping.
 *      We call `_syncAsset` at the very end of every external function which changes the vault's underlying or perp balance.
 *
 *      The perp tokens aren't considered part of the vault's asset set. However
 *      during the normal operations, the vault can hold transient perp tokens but they
 *      are immediately broken down into tranches. The vault receives perps only during rebalancing and swapping.
 *      At the end of those operations, the vault redeems perp tokens for the senior tranches backing them.
 *
 *
 */
contract RolloverVault is
    ERC20BurnableUpgradeable,
    OwnableUpgradeable,
    PausableUpgradeable,
    ReentrancyGuardUpgradeable,
    IRolloverVault
{
    // data handling
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using BondHelpers for IBondController;
    using TrancheHelpers for ITranche;
    using BondTranchesHelpers for BondTranches;

    // ERC20 operations
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeERC20Upgradeable for ITranche;
    using ERC20Helpers for IERC20Upgradeable;

    // math
    using MathUpgradeable for uint256;
    using SignedMathUpgradeable for int256;
    using SafeCastUpgradeable for int256;

    /// Allow linking TrancheManager
    /// @custom:oz-upgrades-unsafe-allow external-library-linking

    //-------------------------------------------------------------------------
    // Events

    /// @notice Emits the vault asset's token balance that's recorded after a change.
    /// @param token Address of token.
    /// @param balance The recorded ERC-20 balance of the token.
    event AssetSynced(IERC20Upgradeable token, uint256 balance);

    //-------------------------------------------------------------------------
    // Constants

    /// @dev Internal percentages are fixed point numbers with {PERC_DECIMALS} places.
    uint8 public constant PERC_DECIMALS = 8;
    uint256 public constant ONE = (10 ** PERC_DECIMALS); // 1.0 or 100%

    /// @dev Initial exchange rate between the underlying asset and notes.
    uint256 private constant INITIAL_RATE = 10 ** 6;

    /// @dev The maximum number of deployed assets that can be held in this vault at any given time.
    uint8 public constant MAX_DEPLOYED_COUNT = 47;

    /// @dev Immature redemption may result in some dust tranches when balances are not perfectly divisible by the tranche ratio.
    ///      Based on current the implementation of `computeRedeemableTrancheAmounts`,
    ///      the dust balances which remain after immature redemption will be *at most* {TRANCHE_RATIO_GRANULARITY} or 1000.
    ///      We exclude the vault's dust tranche balances from TVL computation, note redemption and
    ///      during recovery (through recurrent immature redemption).
    ///      https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
    uint256 public constant TRANCHE_DUST_AMT = 10000000;

    //--------------------------------------------------------------------------
    // ASSETS
    //
    // The vault's assets are represented by a master list of ERC-20 tokens
    //      => { [underlying] U _deployed }
    //
    //
    /// @notice The ERC20 token that can be deposited into this vault.
    IERC20Upgradeable public underlying;

    /// @dev The set of the intermediate ERC-20 tokens when the underlying asset has been put to use.
    ///      In the case of this vault, they represent the tranche tokens held before maturity.
    EnumerableSetUpgradeable.AddressSet private _deployed;

    //-------------------------------------------------------------------------
    // Parameters

    /// @notice Minimum amount of underlying assets that must be deployed, for a deploy operation to succeed.
    /// @dev The deployment transaction reverts, if the vaults does not have sufficient underlying tokens
    ///      to cover the minimum deployment amount.
    uint256 public minDeploymentAmt;

    /// @notice The perpetual token on which rollovers are performed.
    IPerpetualTranche public perp;

    //--------------------------------------------------------------------------
    // v2.0.0 STORAGE ADDITION

    /// @notice External contract that orchestrates fees across the spot protocol.
    IFeePolicy public feePolicy;

    /// @notice Reference to the address that has the ability to pause/unpause operations.
    /// @dev The keeper is meant for time-sensitive operations, and may be different from the owner address.
    /// @return The address of the keeper.
    address public keeper;

    //--------------------------------------------------------------------------
    // The reserved liquidity is the subset of the vault's underlying tokens that it
    // does not deploy for rolling over (or used for swaps) and simply holds.
    // The existence of sufficient reserved liquidity ensures that
    // a) The vault's TVL never goes too low and guards against the "share" manipulation attack.
    // b) Not all of the vault's liquidity is locked up in tranches.

    /// @notice The absolute amount of underlying tokens, reserved.
    /// @custom:oz-upgrades-renamed-from minUnderlyingBal
    uint256 public reservedUnderlyingBal;

    /// @notice The amount of underlying tokens as percentage of the vault's TVL, reserved.
    /// @custom:oz-upgrades-renamed-from reservedSubscriptionPerc
    uint256 public reservedUnderlyingPerc;

    //--------------------------------------------------------------------------
    // v3.0.0 STORAGE ADDITION

    /// @notice Recorded timestamp of the last successful rebalance.
    uint256 public lastRebalanceTimestampSec;

    //--------------------------------------------------------------------------
    // Modifiers

    /// @dev Throws if called by any account other than the keeper.
    modifier onlyKeeper() {
        if (msg.sender != keeper) {
            revert UnauthorizedCall();
        }
        _;
    }

    //--------------------------------------------------------------------------
    // Construction & Initialization

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

    /// @notice Contract state initialization.
    /// @param name ERC-20 Name of the vault token.
    /// @param symbol ERC-20 Symbol of the vault token.
    /// @param perp_ ERC-20 address of the perpetual tranche rolled over.
    /// @param feePolicy_ Address of the fee policy contract.
    function init(
        string memory name,
        string memory symbol,
        IPerpetualTranche perp_,
        IFeePolicy feePolicy_
    ) external initializer {
        // initialize dependencies
        __ERC20_init(name, symbol);
        __ERC20Burnable_init();
        __Ownable_init();
        __Pausable_init();
        __ReentrancyGuard_init();

        // setup underlying collateral
        underlying = perp_.underlying();

        // set reference to perp
        perp = perp_;

        // set the reference to the fee policy
        updateFeePolicy(feePolicy_);

        // set keeper reference
        updateKeeper(owner());

        // setting initial parameter values
        minDeploymentAmt = 0;
        reservedUnderlyingBal = 0;
        reservedUnderlyingPerc = 0;
        lastRebalanceTimestampSec = block.timestamp;

        // sync underlying
        _syncAsset(underlying);
    }

    //--------------------------------------------------------------------------
    // Owner only methods

    /// @notice Update the reference to the fee policy contract.
    /// @param feePolicy_ New strategy address.
    function updateFeePolicy(IFeePolicy feePolicy_) public onlyOwner {
        if (feePolicy_.decimals() != PERC_DECIMALS) {
            revert UnexpectedDecimals();
        }
        feePolicy = feePolicy_;
    }

    /// @notice Transfers a non-vault token out of the contract, which may have been added accidentally.
    /// @param token The token address.
    /// @param to The destination address.
    /// @param amount The amount of tokens to be transferred.
    function transferERC20(IERC20Upgradeable token, address to, uint256 amount) external onlyOwner nonReentrant {
        if (isVaultAsset(token)) {
            revert UnauthorizedTransferOut();
        }
        token.safeTransfer(to, amount);
    }

    /// @notice Updates the reference to the keeper.
    /// @param keeper_ The address of the new keeper.
    function updateKeeper(address keeper_) public onlyOwner {
        keeper = keeper_;
    }

    //--------------------------------------------------------------------------
    // Keeper only methods

    /// @notice Pauses deposits, withdrawals and vault operations.
    /// @dev NOTE: ERC-20 functions, like transfers will always remain operational.
    function pause() external onlyKeeper {
        _pause();
    }

    /// @notice Unpauses deposits, withdrawals and vault operations.
    /// @dev NOTE: ERC-20 functions, like transfers will always remain operational.
    function unpause() external onlyKeeper {
        _unpause();
    }

    /// @notice Pauses the rebalance operation.
    function pauseRebalance() external onlyKeeper {
        lastRebalanceTimestampSec = type(uint64).max;
    }

    /// @notice Unpauses the rebalance operation.
    function unpauseRebalance() external onlyKeeper {
        lastRebalanceTimestampSec = block.timestamp;
    }

    /// @notice Updates the vault's minimum liquidity requirements.
    /// @param minDeploymentAmt_ The new minimum deployment amount, denominated in underlying tokens.
    /// @param reservedUnderlyingBal_ The new reserved underlying balance.
    /// @param reservedUnderlyingPerc_ The new reserved subscription percentage.
    function updateLiquidityLimits(
        uint256 minDeploymentAmt_,
        uint256 reservedUnderlyingBal_,
        uint256 reservedUnderlyingPerc_
    ) external onlyKeeper {
        minDeploymentAmt = minDeploymentAmt_;
        reservedUnderlyingBal = reservedUnderlyingBal_;
        reservedUnderlyingPerc = reservedUnderlyingPerc_;
    }

    //--------------------------------------------------------------------------
    // External & Public write methods

    /// @inheritdoc IRolloverVault
    function rebalance() external override nonReentrant whenNotPaused {
        if (block.timestamp <= lastRebalanceTimestampSec + feePolicy.rebalanceFreqSec()) {
            revert LastRebalanceTooRecent();
        }
        _rebalance(perp, underlying);
        lastRebalanceTimestampSec = block.timestamp;
    }

    /// @inheritdoc IVault
    /// @dev Simply batches the `recover` and `deploy` functions. Reverts if there are no funds to deploy.
    function recoverAndRedeploy() external override {
        recover();
        deploy();
    }

    /// @inheritdoc IVault
    /// @dev Its safer to call `recover` before `deploy` so the full available balance can be deployed.
    ///      The vault holds the reserved balance of underlying tokens and deploys the rest.
    ///      Reverts if no funds are rolled over or enforced deployment threshold is not reached.
    function deploy() public override nonReentrant whenNotPaused {
        IERC20Upgradeable underlying_ = underlying;
        IPerpetualTranche perp_ = perp;

        // We calculate the usable underlying balance.
        uint256 underlyingBal = underlying_.balanceOf(address(this));
        uint256 reservedBal = _totalReservedBalance(getTVL());
        uint256 usableBal = (underlyingBal > reservedBal) ? underlyingBal - reservedBal : 0;

        // We ensure that at-least `minDeploymentAmt` amount of underlying tokens are deployed
        // (i.e used productively for rollovers).
        if (usableBal <= minDeploymentAmt) {
            revert InsufficientDeployment();
        }

        // We tranche all the underlying held by the vault to create seniors and juniors
        _tranche(perp_.getDepositBond(), underlying_, usableBal);

        // Newly minted seniors are rolled into perp
        if (!_rollover(perp_, underlying_)) {
            revert InsufficientDeployment();
        }

        // sync underlying
        _syncAsset(underlying);
    }

    /// @inheritdoc IVault
    function recover() public override nonReentrant whenNotPaused {
        // Redeem deployed tranches
        uint8 deployedCount_ = uint8(_deployed.length());
        for (uint8 i = 0; i < deployedCount_; ++i) {
            ITranche tranche = ITranche(_deployed.at(i));
            uint256 trancheBalance = tranche.balanceOf(address(this));

            // if the vault has no tranche balance,
            // we continue to the next one.
            if (trancheBalance <= 0) {
                continue;
            }

            // get the parent bond
            IBondController bond = IBondController(tranche.bond());
            BondTranches memory bt = bond.getTranches();

            // if bond has matured, redeem the tranche token
            if (bond.secondsToMaturity() <= 0) {
                // execute redemption
                TrancheManager.execMatureTrancheRedemption(bond, tranche, trancheBalance);
            }
            // if not redeem using proportional balances
            // redeems this tranche and it's siblings if the vault holds balances.
            // NOTE: For gas optimization, we perform this operation only once
            // i.e) when we encounter the most-senior tranche.
            // We also skip if the tranche balance is too low as immature redemption will be a no-op.
            else if (tranche == bt.tranches[0] && trancheBalance > TRANCHE_DUST_AMT) {
                // execute redemption
                TrancheManager.execImmatureTrancheRedemption(bond, bt);
            }
        }

        // sync deployed tranches
        // NOTE: We traverse the deployed set in the reverse order
        //       as deletions involve swapping the deleted element to the
        //       end of the set and removing the last element.
        for (uint8 i = deployedCount_; i > 0; i--) {
            _syncDeployedAsset(IERC20Upgradeable(_deployed.at(i - 1)));
        }

        // sync underlying
        _syncAsset(underlying);
    }

    /// @inheritdoc IVault
    function recover(IERC20Upgradeable token) public override nonReentrant whenNotPaused {
        if (_deployed.contains(address(token))) {
            _redeemTranche(ITranche(address(token)));
            _syncAsset(underlying);
            return;
        }

        IPerpetualTranche perp_ = perp;
        if (address(token) == address(perp_)) {
            _meldPerps(perp_);
            _syncAsset(perp_);
            _syncAsset(underlying);
            return;
        }

        revert UnexpectedAsset();
    }

    /// @inheritdoc IRolloverVault
    /// @dev This operation pushes the system back into balance, we thus charge no fees.
    function mint2(
        uint256 underlyingAmtIn
    ) external override nonReentrant whenNotPaused returns (uint256 perpAmt, uint256 vaultNoteAmt) {
        IPerpetualTranche perp_ = perp;
        IERC20Upgradeable underlying_ = underlying;

        // Compute perp vault asset split.
        SystemTVL memory s = _querySystemTVL(perp_);
        uint256 underlyingAmtIntoPerp = underlyingAmtIn.mulDiv(ONE, ONE + feePolicy.targetSystemRatio());
        uint256 underlyingAmtIntoVault = underlyingAmtIn - underlyingAmtIntoPerp;

        // Compute perp amount and vault note amount to mint
        perpAmt = underlyingAmtIntoPerp.mulDiv(perp_.totalSupply(), s.perpTVL);
        vaultNoteAmt = computeMintAmt(underlyingAmtIntoVault, 0);

        // Transfer underlying tokens from user
        underlying_.safeTransferFrom(msg.sender, address(this), underlyingAmtIn);

        // Mint perps to user
        _trancheAndMintPerps(perp_, underlying_, s.perpTVL, perp_.getDepositTrancheRatio(), perpAmt);
        IERC20Upgradeable(address(perp_)).safeTransfer(msg.sender, perpAmt);

        // Mint vault notes to user
        _mint(msg.sender, vaultNoteAmt);

        // Sync underlying
        _syncAsset(underlying_);
    }

    /// @inheritdoc IRolloverVault
    /// @dev This operation maintains the system's balance, we thus charge no fees.
    function redeem2(
        uint256 perpAmtAvailable,
        uint256 vaultNoteAmtAvailable
    )
        external
        override
        nonReentrant
        whenNotPaused
        returns (uint256 perpAmt, uint256 vaultNoteAmt, TokenAmount[] memory returnedTokens)
    {
        IPerpetualTranche perp_ = perp;

        // Compute perp vault split
        {
            uint256 perpSupply = perp_.totalSupply();
            uint256 vaultNoteSupply = totalSupply();
            perpAmt = perpAmtAvailable;
            vaultNoteAmt = vaultNoteSupply.mulDiv(perpAmtAvailable, perpSupply);
            if (vaultNoteAmt > vaultNoteAmtAvailable) {
                vaultNoteAmt = vaultNoteAmtAvailable;
                perpAmt = perpSupply.mulDiv(vaultNoteAmtAvailable, vaultNoteSupply);
            }
        }

        // Redeem vault notes
        TokenAmount[] memory vaultTokens = computeRedemptionAmts(vaultNoteAmt, 0);
        _burn(msg.sender, vaultNoteAmt);

        // Transfer perps from user and redeem
        IERC20Upgradeable(address(perp_)).safeTransferFrom(msg.sender, address(this), perpAmt);
        TokenAmount[] memory perpTokens = perp.redeem(perpAmt);

        // Compute final list of tokens to return to the user
        // assert(underlying == perpTokens[0].token && underlying == vaultTokens[0].token);
        returnedTokens = new TokenAmount[](perpTokens.length + vaultTokens.length - 1);
        returnedTokens[0] = TokenAmount({
            token: perpTokens[0].token,
            amount: (perpTokens[0].amount + vaultTokens[0].amount)
        });
        returnedTokens[0].token.safeTransfer(msg.sender, returnedTokens[0].amount);

        // perp tokens
        for (uint8 i = 1; i < uint8(perpTokens.length); i++) {
            returnedTokens[i] = perpTokens[i];
            perpTokens[i].token.safeTransfer(msg.sender, returnedTokens[i].amount);
        }

        // vault tokens
        for (uint8 i = 1; i < uint8(vaultTokens.length); i++) {
            returnedTokens[i - 1 + perpTokens.length] = vaultTokens[i];
            vaultTokens[i].token.safeTransfer(msg.sender, vaultTokens[i].amount);

            // sync balances
            _syncDeployedAsset(vaultTokens[i].token);
        }

        // sync underlying
        _syncAsset(returnedTokens[0].token);
    }

    /// @inheritdoc IVault
    function deposit(uint256 underlyingAmtIn) external override nonReentrant whenNotPaused returns (uint256) {
        // Compute the mint fees
        SystemTVL memory s = _querySystemTVL(perp);
        uint256 feePerc = feePolicy.computeFeePerc(
            feePolicy.computeDeviationRatio(s),
            feePolicy.computeDeviationRatio(SystemTVL({ perpTVL: s.perpTVL, vaultTVL: s.vaultTVL + underlyingAmtIn }))
        );

        // Calculates the fee adjusted amount of vault notes minted when depositing `underlyingAmtIn` of underlying tokens.
        // NOTE: This operation should precede any token transfers.
        uint256 vaultNoteAmt = computeMintAmt(underlyingAmtIn, feePerc);
        if (underlyingAmtIn <= 0 || vaultNoteAmt <= 0) {
            return 0;
        }

        // transfer user assets in
        underlying.safeTransferFrom(msg.sender, address(this), underlyingAmtIn);

        // mint vault notes
        _mint(msg.sender, vaultNoteAmt);

        // sync underlying
        _syncAsset(underlying);
        return vaultNoteAmt;
    }

    /// @inheritdoc IVault
    function redeem(uint256 vaultNoteAmt) public override nonReentrant whenNotPaused returns (TokenAmount[] memory) {
        if (vaultNoteAmt <= 0) {
            return new TokenAmount[](0);
        }

        // Compute the redemption fees
        SystemTVL memory s = _querySystemTVL(perp);
        uint256 vaultNoteSupply = totalSupply();
        uint256 feePerc = feePolicy.computeFeePerc(
            feePolicy.computeDeviationRatio(s),
            feePolicy.computeDeviationRatio(
                SystemTVL({
                    perpTVL: s.perpTVL,
                    vaultTVL: s.vaultTVL.mulDiv(vaultNoteSupply - vaultNoteAmt, vaultNoteSupply)
                })
            )
        );

        // Calculates the fee adjusted share of vault tokens to be redeemed
        // NOTE: This operation should precede any token transfers.
        TokenAmount[] memory redemptions = computeRedemptionAmts(vaultNoteAmt, feePerc);

        // burn vault notes
        _burn(msg.sender, vaultNoteAmt);

        // transfer assets out
        uint8 redemptionsCount = uint8(redemptions.length);
        for (uint8 i = 0; i < redemptionsCount; ++i) {
            if (redemptions[i].amount == 0) {
                continue;
            }

            // Transfer token share out
            redemptions[i].token.safeTransfer(msg.sender, redemptions[i].amount);

            // sync balances, wkt i=0 is the underlying and remaining are tranches
            if (i == 0) {
                _syncAsset(redemptions[i].token);
            } else {
                _syncDeployedAsset(redemptions[i].token);
            }
        }
        return redemptions;
    }

    /// @inheritdoc IRolloverVault
    /// @dev Callers should call `recover` before executing `swapUnderlyingForPerps` to maximize vault liquidity.
    function swapUnderlyingForPerps(uint256 underlyingAmtIn) external nonReentrant whenNotPaused returns (uint256) {
        // Calculates the fee adjusted perp amount to transfer to the user.
        // NOTE: This operation should precede any token transfers.
        IPerpetualTranche perp_ = perp;
        IERC20Upgradeable underlying_ = underlying;
        uint256 underlyingBalPre = underlying_.balanceOf(address(this));
        (uint256 perpAmtOut, , SystemTVL memory s) = computeUnderlyingToPerpSwapAmt(underlyingAmtIn);

        // Revert if insufficient tokens are swapped in or out
        if (perpAmtOut <= 0 || underlyingAmtIn <= 0) {
            revert UnacceptableSwap();
        }

        // transfer underlying in
        underlying_.safeTransferFrom(msg.sender, address(this), underlyingAmtIn);

        // tranche and mint perps as needed
        _trancheAndMintPerps(perp_, underlying_, s.perpTVL, perp_.getDepositTrancheRatio(), perpAmtOut);

        // transfer remaining perps out to the user
        IERC20Upgradeable(address(perp_)).safeTransfer(msg.sender, perpAmtOut);

        // NOTE: In case this operation mints slightly more perps than that are required for the swap,
        // The vault continues to hold the perp dust until the subsequent `swapPerpsForUnderlying` or manual `recover(perp)`.

        // We ensure that the vault's underlying token liquidity
        // remains above the reserved level after a swap that reduces liquidity.
        uint256 underlyingBalPost = underlying_.balanceOf(address(this));
        if ((underlyingBalPost < underlyingBalPre) && (underlyingBalPost <= _totalReservedBalance(s.vaultTVL))) {
            revert InsufficientLiquidity();
        }

        // sync underlying
        _syncAsset(underlying_);

        return perpAmtOut;
    }

    /// @inheritdoc IRolloverVault
    function swapPerpsForUnderlying(uint256 perpAmtIn) external nonReentrant whenNotPaused returns (uint256) {
        // Calculates the fee adjusted underlying amount to transfer to the user.
        IPerpetualTranche perp_ = perp;
        IERC20Upgradeable underlying_ = underlying;
        uint256 underlyingBalPre = underlying_.balanceOf(address(this));
        (uint256 underlyingAmtOut, , ) = computePerpToUnderlyingSwapAmt(perpAmtIn);

        // Revert if insufficient tokens are swapped in or out
        if (underlyingAmtOut <= 0 || perpAmtIn <= 0) {
            revert UnacceptableSwap();
        }

        // transfer perps in
        IERC20Upgradeable(perp_).safeTransferFrom(msg.sender, address(this), perpAmtIn);

        // Meld incoming perps
        _meldPerps(perp_);

        // transfer underlying out
        underlying_.safeTransfer(msg.sender, underlyingAmtOut);

        // We ensure that this swap strictly increases the vault's liquidity.
        uint256 underlyingBalPost = underlying_.balanceOf(address(this));
        if (underlyingBalPost < underlyingBalPre) {
            revert InsufficientLiquidity();
        }

        // sync underlying
        _syncAsset(underlying_);

        return underlyingAmtOut;
    }

    //--------------------------------------------------------------------------
    // External & Public compute methods

    /// @inheritdoc IRolloverVault
    function deviationRatio() external override nonReentrant returns (uint256) {
        return feePolicy.computeDeviationRatio(_querySystemTVL(perp));
    }

    /// @inheritdoc IRolloverVault
    function computeUnderlyingToPerpSwapAmt(
        uint256 underlyingAmtIn
    ) public returns (uint256, uint256, SystemTVL memory) {
        IPerpetualTranche perp_ = perp;
        // Compute equal value perps to swap out to the user
        SystemTVL memory s = _querySystemTVL(perp_);
        uint256 perpAmtOut = underlyingAmtIn.mulDiv(perp_.totalSupply(), s.perpTVL);

        //-----------------------------------------------------------------------------
        // When user swaps underlying for vault's perps -> perps are minted by the vault
        // We thus compute fees based on the post-mint system tvl.
        uint256 feePerc = feePolicy.computeFeePerc(
            feePolicy.computeDeviationRatio(s),
            feePolicy.computeDeviationRatio(SystemTVL({ perpTVL: s.perpTVL + underlyingAmtIn, vaultTVL: s.vaultTVL }))
        );
        //-----------------------------------------------------------------------------

        // We deduct fees by transferring out fewer perp tokens
        perpAmtOut = perpAmtOut.mulDiv(ONE - feePerc, ONE);

        return (perpAmtOut, 0, s);
    }

    /// @inheritdoc IRolloverVault
    function computePerpToUnderlyingSwapAmt(uint256 perpAmtIn) public returns (uint256, uint256, SystemTVL memory) {
        IPerpetualTranche perp_ = perp;
        // Compute equal value underlying tokens to swap out
        SystemTVL memory s = _querySystemTVL(perp_);
        uint256 underlyingAmtOut = perpAmtIn.mulDiv(s.perpTVL, perp_.totalSupply());

        //-----------------------------------------------------------------------------
        // When user swaps perps for vault's underlying -> perps are redeemed by the vault
        // We thus compute fees based on the post-burn system tvl.
        uint256 feePerc = feePolicy.computeFeePerc(
            feePolicy.computeDeviationRatio(s),
            feePolicy.computeDeviationRatio(SystemTVL({ perpTVL: s.perpTVL - underlyingAmtOut, vaultTVL: s.vaultTVL }))
        );
        //-----------------------------------------------------------------------------

        // We deduct fees by transferring out fewer underlying tokens
        underlyingAmtOut = underlyingAmtOut.mulDiv(ONE - feePerc, ONE);

        return (underlyingAmtOut, 0, s);
    }

    //--------------------------------------------------------------------------
    // External & Public read methods

    /// @notice Computes the amount of vault notes minted when given amount of underlying asset tokens
    ///         are deposited into the system.
    /// @param underlyingAmtIn The amount underlying tokens to be deposited into the vault.
    /// @param feePerc The percentage of minted vault notes paid as fees.
    /// @return vaultNoteAmtMint The amount of vault notes to be minted.
    function computeMintAmt(uint256 underlyingAmtIn, uint256 feePerc) public view returns (uint256 vaultNoteAmtMint) {
        uint256 vaultNoteSupply = totalSupply();

        //-----------------------------------------------------------------------------
        // Compute mint amt
        vaultNoteAmtMint = (vaultNoteSupply > 0)
            ? vaultNoteSupply.mulDiv(underlyingAmtIn, getTVL())
            : (underlyingAmtIn * INITIAL_RATE);

        // The mint fees are settled by simply minting fewer vault notes.
        vaultNoteAmtMint = vaultNoteAmtMint.mulDiv(ONE - feePerc, ONE);
    }

    /// @notice Computes the amount of asset tokens returned for redeeming vault notes.
    /// @param vaultNoteAmtRedeemed The amount of vault notes to be redeemed.
    /// @param feePerc The percentage of redeemed vault notes paid as fees.
    /// @return returnedTokens The list of asset tokens and amounts returned.
    function computeRedemptionAmts(
        uint256 vaultNoteAmtRedeemed,
        uint256 feePerc
    ) public view returns (TokenAmount[] memory returnedTokens) {
        uint256 vaultNoteSupply = totalSupply();

        //-----------------------------------------------------------------------------
        uint8 assetCount_ = 1 + uint8(_deployed.length());

        // aggregating vault assets to be redeemed
        returnedTokens = new TokenAmount[](assetCount_);

        // underlying share to be redeemed
        IERC20Upgradeable underlying_ = underlying;
        returnedTokens[0] = TokenAmount({
            token: underlying_,
            amount: underlying_.balanceOf(address(this)).mulDiv(vaultNoteAmtRedeemed, vaultNoteSupply)
        });
        returnedTokens[0].amount = returnedTokens[0].amount.mulDiv(ONE - feePerc, ONE);

        for (uint8 i = 1; i < assetCount_; ++i) {
            // tranche token share to be redeemed
            IERC20Upgradeable token = IERC20Upgradeable(_deployed.at(i - 1));
            returnedTokens[i] = TokenAmount({
                token: token,
                amount: token.balanceOf(address(this)).mulDiv(vaultNoteAmtRedeemed, vaultNoteSupply)
            });

            // deduct redemption fee
            returnedTokens[i].amount = returnedTokens[i].amount.mulDiv(ONE - feePerc, ONE);

            // in case the redemption amount is just dust, we skip
            if (returnedTokens[i].amount < TRANCHE_DUST_AMT) {
                returnedTokens[i].amount = 0;
            }
        }
    }

    /// @inheritdoc IVault
    /// @dev The total value is denominated in the underlying asset.
    function getTVL() public view override returns (uint256) {
        // The underlying balance
        uint256 totalValue = underlying.balanceOf(address(this));

        // The deployed asset value denominated in the underlying
        uint8 deployedCount_ = uint8(_deployed.length());
        for (uint8 i = 0; i < deployedCount_; ++i) {
            ITranche tranche = ITranche(_deployed.at(i));
            uint256 balance = tranche.balanceOf(address(this));
            if (balance > TRANCHE_DUST_AMT) {
                totalValue += TrancheManager.computeTrancheValue(address(tranche), address(underlying), balance);
            }
        }

        return totalValue;
    }

    /// @inheritdoc IVault
    /// @dev The asset value is denominated in the underlying asset.
    function getVaultAssetValue(IERC20Upgradeable token) external view override returns (uint256) {
        uint256 balance = token.balanceOf(address(this));

        // Underlying asset
        if (token == underlying) {
            return balance;
        }
        // Deployed asset
        else if (_deployed.contains(address(token))) {
            ITranche tranche = ITranche(address(token));
            return
                (balance > TRANCHE_DUST_AMT)
                    ? TrancheManager.computeTrancheValue(address(tranche), address(underlying), balance)
                    : 0;
        }

        // Not a vault asset, so returning zero
        return 0;
    }

    /// @inheritdoc IVault
    function assetCount() external view override returns (uint256) {
        return _deployed.length() + 1;
    }

    /// @inheritdoc IVault
    function assetAt(uint256 i) external view override returns (IERC20Upgradeable) {
        if (i == 0) {
            return underlying;
        } else if (i <= _deployed.length()) {
            return IERC20Upgradeable(_deployed.at(i - 1));
        }
        revert OutOfBounds();
    }

    /// @inheritdoc IVault
    function vaultAssetBalance(IERC20Upgradeable token) external view override returns (uint256) {
        return isVaultAsset(token) ? token.balanceOf(address(this)) : 0;
    }

    /// @inheritdoc IVault
    function isVaultAsset(IERC20Upgradeable token) public view override returns (bool) {
        return token == underlying || _deployed.contains(address(token));
    }

    //--------------------------------------------------------------------------
    // Private write methods

    /// @dev Executes a system-level rebalance. This operation transfers value between
    ///      the perp reserve and the vault such that the system moves toward balance.
    ///      Performs some book-keeping to keep track of the vault and perp's assets.
    function _rebalance(IPerpetualTranche perp_, IERC20Upgradeable underlying_) private {
        // Claim mint/burn fees collected by perp.
        perp_.claimFees(address(this));
        _meldPerps(perp_);

        SystemTVL memory s = _querySystemTVL(perp_);
        int256 underlyingAmtIntoPerp = feePolicy.computeRebalanceAmount(s);

        // When value is flowing from perp to the vault.
        // We rebalance from perp to the vault.
        if (underlyingAmtIntoPerp < 0) {
            perp_.rebalanceToVault(underlyingAmtIntoPerp.abs());
            _meldPerps(perp_); // Meld residual perps, if any.
        }
        // When value is flowing from the vault to perp.
        // We rebalance from the vault to perp.
        else if (underlyingAmtIntoPerp > 0) {
            // We transfer value by minting the perp tokens (after making required deposit)
            // and then simply burning the newly minted perp tokens.
            uint256 perpAmtToTransfer = (underlyingAmtIntoPerp.toUint256()).mulDiv(perp_.totalSupply(), s.perpTVL);
            _trancheAndMintPerps(perp_, underlying_, s.perpTVL, perp_.getDepositTrancheRatio(), perpAmtToTransfer);
            IERC20Burnable(address(perp_)).burn(perpAmtToTransfer);
        }

        // We pay the protocol fee on every rebalance.
        uint256 protocolSharePerc = feePolicy.protocolSharePerc();
        if (protocolSharePerc > 0) {
            address collector = feePolicy.protocolFeeCollector();
            perp.payProtocolFee(collector, protocolSharePerc);
            _mint(collector, protocolSharePerc.mulDiv(totalSupply(), ONE - protocolSharePerc));
        }

        // Sync token balances.
        _syncAsset(perp_);
        _syncAsset(underlying_);
    }

    /// @dev Redeems tranche tokens held by the vault, for underlying.
    ///      In the case of immature redemption, this method will recover other sibling tranches as well.
    ///      Performs some book-keeping to keep track of the vault's assets.
    function _redeemTranche(ITranche tranche) private {
        uint256 trancheBalance = tranche.balanceOf(address(this));

        // if the vault has no tranche balance,
        // we update our internal book-keeping and return.
        if (trancheBalance <= 0) {
            _syncDeployedAsset(tranche);
            return;
        }

        // get the parent bond
        IBondController bond = IBondController(tranche.bond());

        // if bond has matured, redeem the tranche token
        if (bond.secondsToMaturity() <= 0) {
            // execute redemption
            TrancheManager.execMatureTrancheRedemption(bond, tranche, trancheBalance);

            // sync deployed asset
            _syncDeployedAsset(tranche);
        }
        // if not redeem using proportional balances
        // redeems this tranche and it's siblings if the vault holds balances.
        // We skip if the tranche balance is too low as immature redemption will be a no-op.
        else if (trancheBalance > TRANCHE_DUST_AMT) {
            // execute redemption
            BondTranches memory bt = bond.getTranches();
            TrancheManager.execImmatureTrancheRedemption(bond, bt);

            // sync deployed asset, i.e) current tranche and its sibling.
            _syncDeployedAsset(bt.tranches[0]);
            _syncDeployedAsset(bt.tranches[1]);
        } else {
            _syncDeployedAsset(tranche);
        }
    }

    /// @dev Redeems perp tokens held by the vault for tranches and
    ///      melds them with existing tranches to redeem more underlying tokens.
    ///      Performs some book-keeping to keep track of the vault's assets.
    function _meldPerps(IPerpetualTranche perp_) private {
        uint256 perpBalance = perp_.balanceOf(address(this));
        if (perpBalance <= 0) {
            return;
        }

        TokenAmount[] memory tranchesRedeemed = perp_.redeem(perpBalance);

        // sync and meld perp's tranches
        uint8 tranchesRedeemedCount = uint8(tranchesRedeemed.length);
        for (uint8 i = 1; i < tranchesRedeemedCount; ++i) {
            ITranche tranche = ITranche(address(tranchesRedeemed[i].token));

            // if possible, meld redeemed tranche with
            // existing tranches to redeem underlying.
            _redeemTranche(tranche);
        }
    }

    /// @dev Tranches the vault's underlying to mint perps.
    ///      Performs some book-keeping to keep track of the vault's assets.
    function _trancheAndMintPerps(
        IPerpetualTranche perp_,
        IERC20Upgradeable underlying_,
        uint256 perpTVL,
        uint256 depositTrancheTR,
        uint256 perpAmtToMint
    ) private {
        // Tranche as needed
        IBondController depositBond = perp_.getDepositBond();
        ITranche trancheIntoPerp = perp_.getDepositTranche();
        (uint256 underylingAmtToTranche, uint256 seniorAmtToDeposit) = PerpHelpers.estimateUnderlyingAmtToTranche(
            PerpHelpers.MintEstimationParams({
                perpTVL: perpTVL,
                perpSupply: perp_.totalSupply(),
                depositBondCollateralBalance: underlying_.balanceOf(address(depositBond)),
                depositBondTotalDebt: depositBond.totalDebt(),
                depositTrancheSupply: trancheIntoPerp.totalSupply(),
                depositTrancheTR: depositTrancheTR
            }),
            perpAmtToMint
        );
        _tranche(depositBond, underlying_, underylingAmtToTranche);

        // Mint perps
        IERC20Upgradeable(trancheIntoPerp).checkAndApproveMax(address(perp_), seniorAmtToDeposit);
        perp_.deposit(trancheIntoPerp, seniorAmtToDeposit);

        // sync holdings
        _syncDeployedAsset(trancheIntoPerp);
    }

    /// @dev Rolls over freshly tranched tokens from the given bond for older tranches (close to maturity) from perp.
    ///      Redeems intermediate tranches for underlying if possible.
    ///      Performs some book-keeping to keep track of the vault's assets.
    /// @return Flag indicating if any tokens were rolled over.
    function _rollover(IPerpetualTranche perp_, IERC20Upgradeable underlying_) private returns (bool) {
        // NOTE: The first element of the list is the mature tranche,
        //       there after the list is NOT ordered by maturity.
        IERC20Upgradeable[] memory rolloverTokens = perp_.getReserveTokensUpForRollover();

        // Batch rollover
        bool rollover = false;

        // We query perp's current deposit tranche
        ITranche trancheIntoPerp = perp_.getDepositTranche();

        // Compute available tranche in to rollover
        uint256 trancheInAmtAvailable = trancheIntoPerp.balanceOf(address(this));

        // Approve once for all rollovers
        IERC20Upgradeable(trancheIntoPerp).checkAndApproveMax(address(perp_), trancheInAmtAvailable);

        // We pair the senior tranche token held by the vault (from the deposit bond)
        // with each of the perp's tokens available for rollovers and execute a rollover.
        // We continue to rollover till either the vault's senior tranche balance is exhausted or
        // there are no more tokens in perp available to be rolled-over.
        uint8 rolloverTokensCount = uint8(rolloverTokens.length);
        for (uint8 i = 0; (i < rolloverTokensCount && trancheInAmtAvailable > 0); ++i) {
            // tokenOutOfPerp is the reserve token coming out of perp into the vault
            IERC20Upgradeable tokenOutOfPerp = rolloverTokens[i];

            // Perform rollover
            RolloverData memory r = perp_.rollover(trancheIntoPerp, tokenOutOfPerp, trancheInAmtAvailable);

            // no rollover occurred, skip updating balances
            if (r.tokenOutAmt <= 0) {
                continue;
            }

            // skip insertion into the deployed list the case of the mature tranche, ie underlying
            if (rolloverTokens[i] != underlying_) {
                // Clean up after rollover, merge seniors from perp
                // with vault held juniors to recover more underlying.
                _redeemTranche(ITranche(address(tokenOutOfPerp)));
            }

            // Calculate trancheIn available amount
            trancheInAmtAvailable -= r.trancheInAmt;

            // keep track if "at least" one rolled over operation occurred
            rollover = true;
        }

        // Final cleanup, if there remain excess seniors we recover back to underlying.
        _redeemTranche(trancheIntoPerp);

        return (rollover);
    }

    /// @dev Given a bond, deposits the provided amount into the bond
    ///      and receives tranche tokens in return.
    ///      Performs some book-keeping to keep track of the vault's assets.
    function _tranche(IBondController bond, IERC20Upgradeable underlying_, uint256 underlyingAmt) private {
        // Tranche
        ITranche[2] memory t = bond.approveAndDeposit(underlying_, underlyingAmt);

        // sync holdings
        _syncDeployedAsset(t[0]);
        _syncDeployedAsset(t[1]);
    }

    /// @dev Syncs balance and updates the deployed list based on the vault's token balance.
    function _syncDeployedAsset(IERC20Upgradeable token) private {
        uint256 balance = _syncAsset(token);

        bool inVault = _deployed.contains(address(token));
        if (balance > 0 && !inVault) {
            // Inserts new token into the deployed assets list.
            _deployed.add(address(token));
            if (_deployed.length() > MAX_DEPLOYED_COUNT) {
                revert DeployedCountOverLimit();
            }
        } else if (balance <= 0 && inVault) {
            // Removes token into the deployed assets list.
            _deployed.remove(address(token));
        }
    }

    /// @dev Logs the token balance held by the vault.
    function _syncAsset(IERC20Upgradeable token) private returns (uint256 balance) {
        balance = token.balanceOf(address(this));
        emit AssetSynced(token, balance);
    }

    /// @dev Queries the current TVL of the perp and vault systems.
    function _querySystemTVL(IPerpetualTranche perp_) private returns (SystemTVL memory) {
        return SystemTVL({ perpTVL: perp_.getTVL(), vaultTVL: getTVL() });
    }

    //--------------------------------------------------------------------------
    // Private view methods

    /// @dev Computes the balance of underlying tokens to NOT be used for any operation.
    function _totalReservedBalance(uint256 vaultTVL) private view returns (uint256) {
        return MathUpgradeable.max(reservedUnderlyingBal, vaultTVL.mulDiv(reservedUnderlyingPerc, ONE));
    }
}

Read Contract

MAX_DEPLOYED_COUNT 0x39697825 → uint8
ONE 0xc2ee3a08 → uint256
PERC_DECIMALS 0x75d5179f → uint8
TRANCHE_DUST_AMT 0xcc03c2e3 → uint256
allowance 0xdd62ed3e → uint256
assetAt 0xaa9239f5 → address
assetCount 0xeafe7a74 → uint256
balanceOf 0x70a08231 → uint256
computeMintAmt 0x33bce7f7 → uint256
computeRedemptionAmts 0x7c0ca111 → tuple[]
decimals 0x313ce567 → uint8
feePolicy 0x82589038 → address
getTVL 0x97b3fcaa → uint256
getVaultAssetValue 0x2ad537e3 → uint256
isVaultAsset 0x62b232a4 → bool
keeper 0xaced1661 → address
lastRebalanceTimestampSec 0x3f280af8 → uint256
minDeploymentAmt 0x3f45e08f → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
perp 0xbfa4c00c → address
reservedUnderlyingBal 0x93334d2e → uint256
reservedUnderlyingPerc 0xc215bc4d → uint256
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
underlying 0x6f307dc3 → address
vaultAssetBalance 0x7375c5d6 → uint256

Write Contract 32 functions

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

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
burn 0x42966c68
uint256 amount
burnFrom 0x79cc6790
address account
uint256 amount
computePerpToUnderlyingSwapAmt 0xe1a2209f
uint256 perpAmtIn
returns: uint256, uint256, tuple
computeUnderlyingToPerpSwapAmt 0x6df71ab3
uint256 underlyingAmtIn
returns: uint256, uint256, tuple
decreaseAllowance 0xa457c2d7
address spender
uint256 subtractedValue
returns: bool
deploy 0x775c300c
No parameters
deposit 0xb6b55f25
uint256 underlyingAmtIn
returns: uint256
deviationRatio 0x3434dc4e
No parameters
returns: uint256
increaseAllowance 0x39509351
address spender
uint256 addedValue
returns: bool
init 0x0e07f854
string name
string symbol
address perp_
address feePolicy_
mint2 0x3aa18088
uint256 underlyingAmtIn
returns: uint256, uint256
pause 0x8456cb59
No parameters
pauseRebalance 0x306cf684
No parameters
rebalance 0x7d7c2a1c
No parameters
recover 0x0cd865ec
address token
recover 0xce746024
No parameters
recoverAndRedeploy 0x72617687
No parameters
redeem 0xdb006a75
uint256 vaultNoteAmt
returns: tuple[]
redeem2 0xa2b29206
uint256 perpAmtAvailable
uint256 vaultNoteAmtAvailable
returns: uint256, uint256, tuple[]
renounceOwnership 0x715018a6
No parameters
swapPerpsForUnderlying 0x1cd66a95
uint256 perpAmtIn
returns: uint256
swapUnderlyingForPerps 0xe36894f9
uint256 underlyingAmtIn
returns: uint256
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferERC20 0x9db5dbe4
address token
address to
uint256 amount
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
unpauseRebalance 0x08e9ff23
No parameters
updateFeePolicy 0x69f6f92c
address feePolicy_
updateKeeper 0x97790217
address keeper_
updateLiquidityLimits 0xa8f4cd51
uint256 minDeploymentAmt_
uint256 reservedUnderlyingBal_
uint256 reservedUnderlyingPerc_

Recent Transactions

This address has 1 on-chain transactions, but only 1.3% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →