Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xe8a01D8Dac4Af19eC7a22cf87f3d141ce6e7e9Fb
Balance 0 ETH
Nonce 2
Code Size 9389 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

9389 bytes
0x608060405234801561001057600080fd5b50600436106101725760003560e01c80637856a353116100de578063a743d8c011610097578063d250496c11610071578063d250496c14610340578063e2bbb15814610360578063fc0c546a14610373578063fd0a0b151461038657600080fd5b8063a743d8c0146102fc578063c4bef1671461030f578063c5db1dde1461033757600080fd5b80637856a3531461028a5780637cbc23731461029d5780638870455f146102b0578063983d95ce146102c35780639bc62245146102d6578063a7076b36146102e957600080fd5b806325f63b541161013057806325f63b54146101fe578063375b74c31461021e5780633e0a5d0914610249578063510c82b3146102515780635fcbd2851461026457806370c489ef1461027757600080fd5b8062350e7f1461017757806305436d15146101a0578063089cdd0c146101b85780631764f236146101c157806322637940146101d657806322e07352146101f6575b600080fd5b61018a6101853660046120d7565b610390565b6040516101979190612114565b60405180910390f35b6101aa6202a30081565b604051908152602001610197565b6101aa60065481565b6101d46101cf366004612176565b6104b0565b005b6101e96101e43660046120d7565b6104c9565b60405161019791906121c6565b600d546101aa565b6101aa61020c366004612245565b60096020526000908152604090205481565b600454610231906001600160a01b031681565b6040516001600160a01b039091168152602001610197565b600b546101aa565b6101d461025f366004612245565b610607565b600254610231906001600160a01b031681565b6101d46102853660046120d7565b6107d3565b6101d4610298366004612245565b610817565b6101d46102ab366004612262565b610969565b600154610231906001600160a01b031681565b6101d46102d13660046120d7565b610974565b6101d46102e4366004612245565b6109b4565b6101aa6102f7366004612284565b610b01565b6101d461030a366004612245565b610bc4565b61032261031d366004612284565b610dbc565b60408051928352602083019190915201610197565b6101aa60005481565b6101aa61034e366004612245565b60086020526000908152604090205481565b6101d461036e366004612262565b610e94565b600354610231906001600160a01b031681565b6101aa624f1a0081565b60606000825167ffffffffffffffff8111156103ae576103ae612026565b60405190808252806020026020018201604052801561040c57816020015b6103f9604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b8152602001906001900390816103cc5790505b50905060005b83518110156104a957600a6000858381518110610431576104316122d4565b6020908102919091018101518252818101929092526040908101600020815160608101835281546001600160a01b031681526001820154938101939093526002015490820152825183908390811061048b5761048b6122d4565b602002602001018190525080806104a190612300565b915050610412565b5092915050565b6104b9816107d3565b6104c4338484610e9f565b505050565b60606000825167ffffffffffffffff8111156104e7576104e7612026565b60405190808252806020026020018201604052801561055357816020015b6105406040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816105055790505b50905060005b83518110156104a957600c6000858381518110610578576105786122d4565b6020908102919091018101518252818101929092526040908101600020815160a08101835281546001600160a01b031681526001820154938101939093526002810154918301919091526003810154606083015260040154608082015282518390839081106105e9576105e96122d4565b602002602001018190525080806105ff90612300565b915050610559565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610650573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106749190612319565b6001600160a01b0316146106a35760405162461bcd60e51b815260040161069a90612336565b60405180910390fd5b6005546001600160a01b038281169116146107005760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420437573746f6469616e204164647265737300000000000000604482015260640161069a565b426202a3006000546107129190612363565b11156107605760405162461bcd60e51b815260206004820152601960248201527f496e73756666696369656e742057616974696e672054696d6500000000000000604482015260640161069a565b6004546005546040517fd0db98814986e26fb56ed4ba7bd4cdb4bc68f5471c86e4fb372aef6c43be1703926107a69233926001600160a01b039283169290911690612376565b60405180910390a150600554600480546001600160a01b0319166001600160a01b03909216919091179055565b60005b8151811015610813576108018282815181106107f4576107f46122d4565b6020026020010151611327565b8061080b81612300565b9150506107d6565b5050565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610860573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108849190612319565b6001600160a01b0316146108aa5760405162461bcd60e51b815260040161069a90612336565b60075460ff16156108f35760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e48125b9a5d1a585b1a5e9959606a1b604482015260640161069a565b6007805460ff191660011790556004546040517fd0db98814986e26fb56ed4ba7bd4cdb4bc68f5471c86e4fb372aef6c43be17039161093f9133916001600160a01b0316908590612376565b60405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b610813338383610e9f565b60005b8151811015610813576109a2828281518110610995576109956122d4565b6020026020010151611517565b806109ac81612300565b915050610977565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa1580156109fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a219190612319565b6001600160a01b031614610a475760405162461bcd60e51b815260040161069a90612336565b6001600160a01b038116610a945760405162461bcd60e51b8152602060048201526014602482015273496e76616c6964205a65726f204164647265737360601b604482015260640161069a565b6005546040517ffdf466e06a1186bd2e3fbcbfba4810e434e9e196de25e5e29d7bcc38bea4b07891610ad39133916001600160a01b0316908590612376565b60405180910390a1600580546001600160a01b0319166001600160a01b039290921691909117905542600055565b6001600160a01b038216600090815260096020526040812054815b8351811015610bba576000600a6000868481518110610b3d57610b3d6122d4565b6020908102919091018101518252818101929092526040908101600020815160608101835281546001600160a01b0390811680835260018401549583019590955260029092015492810192909252909250871603610ba7576020810151610ba49084612363565b92505b5080610bb281612300565b915050610b1c565b5090505b92915050565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181865afa158015610c0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c319190612319565b6001600160a01b031614610c575760405162461bcd60e51b815260040161069a90612336565b6001600160a01b038116610ca45760405162461bcd60e51b8152602060048201526014602482015273496e76616c6964205a65726f204164647265737360601b604482015260640161069a565b6001546040517ff1237c4484b1216b734487b31c22619eabadf16f655c110b807119b914fff22691610ce39133916001600160a01b0316908590612376565b60405180910390a1600180546001600160a01b0319166001600160a01b03831690811790915560408051638da5cb5b60e01b8152905160009291638da5cb5b9160048083019260209291908290030181865afa158015610d47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6b9190612319565b6001600160a01b031603610db95760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964204f776e6572204164647265737360581b604482015260640161069a565b50565b60008060008060005b8551811015610e88576000600c6000888481518110610de657610de66122d4565b6020908102919091018101518252818101929092526040908101600020815160a08101835281546001600160a01b039081168083526001840154958301959095526002830154938201939093526003820154606082015260049091015460808201529250891603610e75576040810151610e609084612363565b9250806020015184610e729190612363565b93505b5080610e8081612300565b915050610dc5565b50909590945092505050565b610813338383611853565b610ea7611d66565b6001600160a01b038316600090815260096020526040902054821115610f0f5760405162461bcd60e51b815260206004820152601c60248201527f417661696c61626c652062616c616e6365206e6f7420656e6f75676800000000604482015260640161069a565b6002546040516303d1689d60e11b8152600481018490526000916001600160a01b0316906307a2d13a90602401602060405180830381865afa158015610f59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7d9190612399565b905081811015610fc45760405162461bcd60e51b815260206004820152601260248201527120b9b9b2ba1030b6b7bab73a1032b93937b960711b604482015260640161069a565b6001600160a01b03841660009081526009602052604081208054859290610fec9084906123b2565b909155505060025460405163b390c0ab60e01b815260048101859052600060248201526001600160a01b039091169063b390c0ab90604401600060405180830381600087803b15801561103e57600080fd5b505af1158015611052573d6000803e3d6000fd5b5050505061105e611d9e565b7ffb0aec119cd63b2a545926384480162cb277b4ed052310c39da3b670d2ab408e858386600260009054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f99190612399565b600d54600160009054906101000a90046001600160a01b03166001600160a01b0316636e6cc20d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561114f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111739190612399565b600260009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ea9190612399565b426040516111ff9897969594939291906123c5565b60405180910390a26040518060a00160405280856001600160a01b03168152602001828152602001848152602001600260009054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611280573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a49190612399565b815260200142815250600c60006112b9611db8565b81526020808201929092526040908101600020835181546001600160a01b0319166001600160a01b03909116178155918301516001830155820151600282015560608201516003820155608090910151600490910155506104c4600160008051602061245883398151915255565b61132f611d66565b6000818152600a6020908152604091829020825160608101845281546001600160a01b03908116825260018084015483860152600290930154828601529154845163f0dfeab760e01b8152945191944294919093169263f0dfeab792600480830193928290030181865afa1580156113ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cf9190612399565b82604001516113de9190612363565b11156114235760405162461bcd60e51b8152602060048201526014602482015273098dec6d640e8d2daca40dcdee840cadcdeeaced60631b604482015260640161069a565b80602001516000036114355750611500565b8051602080830151604080516001600160a01b03909416845291830152429082015282907f6620c2a607a07fb3d5b7449d56a4140f5bfe8bf551b92c45af5faf395e260d6b9060600160405180910390a260208082015182516001600160a01b031660009081526009909252604082208054919290916114b6908490612363565b909155505060006020828101828152848352600a909152604091829020835181546001600160a01b0319166001600160a01b03909116178155905160018201559101516002909101555b610db9600160008051602061245883398151915255565b61151f611d66565b6000818152600c60209081526040808320815160a08101835281546001600160a01b03908116825260018084015483870152600284015483860152600384015460608401526004938401546080840152548451636e6cc20d60e01b81529451929695911693636e6cc20d9381810193918290030181865afa1580156115a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115cc9190612399565b9050624f1a008111156115df5750624f1a005b428183608001516115f09190612363565b11156116355760405162461bcd60e51b8152602060048201526014602482015273098dec6d640e8d2daca40dcdee840cadcdeeaced60631b604482015260640161069a565b8160400151600003611648575050611500565b81516020830151600354611667926001600160a01b0390911691611ddd565b61166f611d9e565b7ff041d18460e8444b78f944ad4de3c6cf13f1912a1eb46f4c8de656fb627b47338360000151846020015185604001518660600151600260009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171b9190612399565b604080516001600160a01b039096168652602086019490945292840191909152606083015260808201524260a082015260c00160405180910390a2815160408084015181516001600160a01b0390931683526020830152429082015283907f2723d816727813c693c68fadd143360e52698101b43f5b167544b88dd58f7da29060600160405180910390a260408083015183516001600160a01b0316600090815260086020529182208054919290916117d59084906123b2565b9091555050600060408381018281526020808601848152878552600c90915291909220845181546001600160a01b0319166001600160a01b0390911617815590516001820155905160028201556060830151600382015560809092015160049092019190915550610db9600160008051602061245883398151915255565b61185b611d66565b60015460405163ef1e1bed60e01b81523060048201526001600160a01b039091169063ef1e1bed90602401602060405180830381865afa1580156118a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c79190612399565b8210156119165760405162461bcd60e51b815260206004820152601860248201527f4465706f73697420616d6f756e7420746f6f20736d616c6c0000000000000000604482015260640161069a565b6004546001600160a01b031661196e5760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420637573746f6469616e204164647265737300000000000000604482015260640161069a565b600354600480546040516370a0823160e01b81526001600160a01b039182169281019290925260009216906370a0823190602401602060405180830381865afa1580156119bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e39190612399565b600454600354919250611a05916001600160a01b039081169187911686611e3c565b600354600480546040516370a0823160e01b81526001600160a01b039182169281019290925260009216906370a0823190602401602060405180830381865afa158015611a56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7a9190612399565b9050611a8682826123b2565b600254604051630d9778e560e11b815260048101839052602481018690529195506000916001600160a01b0390911690631b2ef1ca906044016020604051808303816000875af1158015611ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b029190612399565b6001600160a01b038716600090815260086020526040812080549293508392909190611b2f908490612363565b90915550611b3d9050611d9e565b7f71763977f5ea6af2ea4e5ba0bcf8dfba8fad9773fe36cd941d09dc757ce8df15878784600260009054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd89190612399565b600b54600160009054906101000a90046001600160a01b03166001600160a01b031663f0dfeab76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c529190612399565b600260009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ca5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc99190612399565b42604051611cde9897969594939291906123c5565b60405180910390a26040518060600160405280876001600160a01b0316815260200182815260200142815250600a6000611d16611e7b565b81526020808201929092526040908101600020835181546001600160a01b0319166001600160a01b039091161781559183015160018301559190910151600290910155506104c49150611dc99050565b600080516020612458833981519152805460011901611d9857604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6006805460009182611daf83612300565b91905055905090565b600d805460009182611daf83612300565b600160008051602061245883398151915255565b6040516001600160a01b038381166024830152604482018390526104c491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611e8c565b6040516001600160a01b038481166024830152838116604483015260648201839052611e759186918216906323b872dd90608401611e0a565b50505050565b600b805460009182611daf83612300565b6000611ea16001600160a01b03841683611eef565b90508051600014158015611ec6575080806020019051810190611ec49190612406565b155b156104c457604051635274afe760e01b81526001600160a01b038416600482015260240161069a565b6060611efd83836000611f04565b9392505050565b606081471015611f295760405163cd78605960e01b815230600482015260240161069a565b600080856001600160a01b03168486604051611f459190612428565b60006040518083038185875af1925050503d8060008114611f82576040519150601f19603f3d011682016040523d82523d6000602084013e611f87565b606091505b5091509150611f97868383611fa1565b9695505050505050565b606082611fb657611fb182611ffd565b611efd565b8151158015611fcd57506001600160a01b0384163b155b15611ff657604051639996b31560e01b81526001600160a01b038516600482015260240161069a565b5080611efd565b80511561200d5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b600052604160045260246000fd5b600082601f83011261204d57600080fd5b8135602067ffffffffffffffff8083111561206a5761206a612026565b8260051b604051601f19603f8301168101818110848211171561208f5761208f612026565b6040529384528581018301938381019250878511156120ad57600080fd5b83870191505b848210156120cc578135835291830191908301906120b3565b979650505050505050565b6000602082840312156120e957600080fd5b813567ffffffffffffffff81111561210057600080fd5b61210c8482850161203c565b949350505050565b602080825282518282018190526000919060409081850190868401855b8281101561216957815180516001600160a01b0316855286810151878601528501518585015260609093019290850190600101612131565b5091979650505050505050565b60008060006060848603121561218b57600080fd5b8335925060208401359150604084013567ffffffffffffffff8111156121b057600080fd5b6121bc8682870161203c565b9150509250925092565b602080825282518282018190526000919060409081850190868401855b8281101561216957815180516001600160a01b0316855286810151878601528581015186860152606080820151908601526080908101519085015260a090930192908501906001016121e3565b6001600160a01b0381168114610db957600080fd5b60006020828403121561225757600080fd5b8135611efd81612230565b6000806040838503121561227557600080fd5b50508035926020909101359150565b6000806040838503121561229757600080fd5b82356122a281612230565b9150602083013567ffffffffffffffff8111156122be57600080fd5b6122ca8582860161203c565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201612312576123126122ea565b5060010190565b60006020828403121561232b57600080fd5b8151611efd81612230565b60208082526013908201527221b0b63632b91034b9903737ba1037bbb732b960691b604082015260600190565b80820180821115610bbe57610bbe6122ea565b6001600160a01b0393841681529183166020830152909116604082015260600190565b6000602082840312156123ab57600080fd5b5051919050565b81810381811115610bbe57610bbe6122ea565b6001600160a01b03989098168852602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b60006020828403121561241857600080fd5b81518015158114611efd57600080fd5b6000825160005b81811015612449576020818601810151858301520161242f565b50600092019182525091905056fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212206193b4672e07940fd1258b29059bd009d55f919ad21d0d44f2a15f5608bdab1f64736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: Yes (200 runs)
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

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

pragma solidity ^0.8.20;

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
ERC20.sol 301 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.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}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * 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.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 public decimals;

    /**
     * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
    constructor(string memory name_, string memory symbol_,uint8 decimals_) {
        _name = name_;
        _symbol = symbol_;
        decimals = decimals_;
    }

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

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

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

    /**
     * @dev See {IERC20-balanceOf}.
         */
    function balanceOf(address account) public view virtual 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 `value`.
         */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
         *
         * NOTE: If `value` 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 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        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 `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` 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.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
            // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
            _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` 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.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
            _allowances[owner][spender] = value;
            if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
IDataStorage.sol 21 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

interface IDataStorage{

    function owner() external view returns(address);

    function manager() external view returns(address);

    function depositLockTime() external view returns(uint256);

    function redeemLockTime() external view returns(uint256);

    function minDepositMap(address) external view returns(uint256);

    function updateDepositLockTime(uint256) external;

    function updateRedeemLockTime(uint256) external;

    function setVaultMinDeposit(address,uint256) external;
}
LpToken.sol 98 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "./ERC20.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./interfaces/IDataStorage.sol";
import "./USDVault.sol";

contract LpToken is ERC20,ReentrancyGuardUpgradeable{

    uint256 public constant MAX_DEVIATION = 1000;
    address public vault;
    address private immutable _asset;
    uint256 public price;     // total stake USDT / lpToken totalSupply
    uint256 public maxDeviation = 20; // max 10000
    uint256 public updateTime;
    uint256 public constant INTERVAL_TIME = 12 hours;
    uint8 public PRICE_DECIMAL;

    event UpdateIntervalTime(uint256 oldIntervalTime,uint256 currentIntervalTime);
    event UpdateDeviation(uint256 oldDeviation,uint256 currentDeviation);
    event UpdateDataStorage(address user,address oldStorage,address currentStorage);
    event UpdatePrice(address account,uint256 oldPrice,uint256 currentPrice);

    constructor(address assetToken,string memory name,string memory symbol,uint8 decimals) ERC20(name,symbol,decimals){
        _asset = assetToken;
        vault = msg.sender;
        price = 10 ** decimals;
        PRICE_DECIMAL = decimals;
    }

    function mint(uint256 assetAmount,uint256 minShareAmount) external nonReentrant onlyVault returns(uint256){
        uint256 shares = convertToShares(assetAmount);
        require(shares >= minShareAmount,"Insufficient output shares");
        _mint(address(this),shares);
        return shares;
    }

    function burn(uint256 shares,uint256 minAssetAmount) external nonReentrant onlyVault{
        uint256 assetAmount = convertToAssets(shares);
        require(assetAmount >= minAssetAmount,"Insufficient output asset");
        _burn(address(this),shares);
    }

    function updatePrice(uint256 currentPrice) external onlyManager{
        require(block.timestamp - updateTime > INTERVAL_TIME,"UpdateTime Error");
        updateTime = block.timestamp;

        uint256 max = price * (10000 + maxDeviation) / 10000;
        uint256 min = price * (10000 - maxDeviation) / 10000;
        require(currentPrice >= min && currentPrice <= max,"Price exceeded");
        emit UpdatePrice(msg.sender,price,currentPrice);
        price = currentPrice;
    }

    function forceUpdatePrice(uint256 currentPrice) external onlyOwner{
        require(block.timestamp - updateTime > INTERVAL_TIME,"UpdateTime Error");
        updateTime = block.timestamp;
        uint256 maxPrice = price * (10000 + MAX_DEVIATION) / 10000;
        uint256 minPrice = price * (10000 - MAX_DEVIATION) / 10000;
        require(currentPrice >= minPrice && currentPrice <= maxPrice,"Price exceeded");
        emit UpdatePrice(msg.sender,price,currentPrice);
        price = currentPrice;
    }

    function updateDeviation(uint256 deviation) external onlyOwner {
        require(deviation > 0 && deviation <= MAX_DEVIATION,"Deviation Error");
        emit UpdateDeviation(maxDeviation,deviation);
        maxDeviation = deviation;
    }

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return assets * (10 ** PRICE_DECIMAL) / price;
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return shares *  price / (10 ** PRICE_DECIMAL);
    }

    function asset() public view returns (address) {
        return address(_asset);
    }

    modifier onlyManager() {
        require(IDataStorage(USDVault(vault).dataStorage()).manager() == msg.sender,"Caller is not manager");
        _;
    }

    modifier onlyVault()  {
        require(vault == msg.sender,"Caller is not vault");
        _;
    }

    modifier onlyOwner() {
        require(IDataStorage(USDVault(vault).dataStorage()).owner() == msg.sender,"Caller is not owner");
        _;
    }
}
USDVault.sol 253 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

// Uncomment this line to use console.log
// import "hardhat/console.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./interfaces/IDataStorage.sol";
import "./LpToken.sol";

contract USDVault is ReentrancyGuardUpgradeable{
    using SafeERC20 for IERC20;

    uint256 public constant MAX_REDEEM_LOCK_TIME = 60 days;
    uint256 public constant UPDATE_CUSTODIAN_WAITING_TIME = 2 days;
    uint256 public updatePreCustodianTime;

    IDataStorage public dataStorage;
    LpToken public lpToken;
    address public token;      // U Token contract address
    address public custodian;
    address private preCustodian;
    uint256 public eventId;
    bool private _initializing;

    mapping(address => uint256) public balanceMap;    // user total share balance
    mapping(address => uint256) public availableShare; // user avalible share balance

    mapping(uint256 => DepositLock) private depositMap;
    uint256 private depositLockId;
    mapping(uint256 => RedeemLock) private redeemMap;
    uint256 private redeemLockId;

    struct DepositLock{
        address account;
        uint256 share;
        uint256 createTime;
    }

    struct RedeemLock{
        address account;
        uint256 assetAmount;
        uint256 share;      // lp amount
        uint256 price;       // total USD / total shares
        uint256 createTime;
    }

    event Deposit(uint256 indexed id,address user,uint256 depositAsset,uint256 depositShare,uint256 price,uint256 lockId,uint256 lockTime,uint256 totalShares,uint256 createTime);
    event Withdraw(uint256 indexed id,address user,uint256 withdrawAsset,uint256 withdrawShare,uint256 price,uint256 totalShares,uint256 createTime);
    event Redeem(uint256 indexed id,address user,uint256 redeemAsset,uint256 redeemShare,uint256 price,uint256 lockId,uint256 lockTime,uint256 totalShares,uint256 createTime);

    event UnLockDeposit(uint256 indexed lockId,address user,uint256 share,uint256 createTime);
    event UnLockRedeem(uint256 indexed lockId,address user,uint256 share,uint256 createTime);

    event UpdateCustodian(address user,address oldCustodian,address currentCustodian);
    event UpdatePreCustodian(address user,address oldCustodian,address currentCustodian);
    event UpdateDataStorage(address user,address oldStorage,address currentStorage);
    event CreateLp(address lp);

    constructor(address storageContract,address tokenContract) {
        require(storageContract != address(0) && tokenContract != address(0),"Invalid Zero Address");
        token = tokenContract;
        dataStorage = IDataStorage(storageContract);

        lpToken = new LpToken(tokenContract,string.concat('LpToken-',IERC20Metadata(tokenContract).name()),string.concat('LP-',IERC20Metadata(tokenContract).symbol()),IERC20Metadata(tokenContract).decimals());
        emit CreateLp(address(lpToken));
    }

    function deposit(uint256 amount,uint256 minShare) external{
        _deposit(msg.sender,amount,minShare);
    }

    function _deposit(address account,uint256 amount,uint256 minShare) internal nonReentrant{
        require(amount >= dataStorage.minDepositMap(address(this)),"Deposit amount too small");
        require(custodian != address(0),"Invalid custodian Address");
        uint256 beforeBalance = IERC20(token).balanceOf(custodian);
        IERC20(token).safeTransferFrom(account, custodian, amount);
        uint256 afterBalance = IERC20(token).balanceOf(custodian);
        amount = afterBalance - beforeBalance;

        uint256 shares = lpToken.mint(amount,minShare);

        balanceMap[account] += shares;

        emit Deposit(setEventId(),account,amount,shares,lpToken.price(),getDepositLockLength(),dataStorage.depositLockTime(),lpToken.totalSupply(),block.timestamp);

        depositMap[setDepositLockId()] = DepositLock(account,shares,block.timestamp);
    }

    function unLockDeposit(uint256[] memory ids) public{
        for(uint256 i; i<ids.length; i++){
            _unLockDeposit(ids[i]);
        }
    }

    function _unLockDeposit(uint256 id) internal nonReentrant{
        DepositLock memory depositLock= depositMap[id];
        require(depositLock.createTime + dataStorage.depositLockTime() <= block.timestamp,"Lock time not enough");
        if(depositLock.share == 0){
            return;
        }
        emit UnLockDeposit(id,depositLock.account,depositLock.share,block.timestamp);

        availableShare[depositLock.account] += depositLock.share;
        depositLock.share = 0;
        depositMap[id] = depositLock;
    }

    function withdraw(uint256[] memory ids) external{
        for(uint256 i; i<ids.length; i++){
            _withdraw(ids[i]);
        }
    }

    function _withdraw(uint256 id) internal nonReentrant{
        RedeemLock memory redeemLock = redeemMap[id];
        uint256 lockTime = dataStorage.redeemLockTime();
        if(lockTime > MAX_REDEEM_LOCK_TIME){
            lockTime = MAX_REDEEM_LOCK_TIME;
        }
        require(redeemLock.createTime + lockTime <= block.timestamp,"Lock time not enough");
        if(redeemLock.share == 0){
            return;
        }

        IERC20(token).safeTransfer(redeemLock.account,redeemLock.assetAmount);

        emit Withdraw(setEventId(),redeemLock.account,redeemLock.assetAmount,redeemLock.share,redeemLock.price,lpToken.totalSupply(),block.timestamp);
        emit UnLockRedeem(id,redeemLock.account,redeemLock.share,block.timestamp);

        balanceMap[redeemLock.account] -= redeemLock.share;
        redeemLock.share = 0;
        redeemLock.assetAmount = 0;
        redeemMap[id] = redeemLock;
    }

    function redeemAndUnLockDeposit(uint256 amount,uint256 minAssetAmount,uint256[] memory ids) external {
        unLockDeposit(ids);
        _redeem(msg.sender,amount,minAssetAmount);
    }

    function redeem(uint256 share,uint256 minAssetAmount) external {
        _redeem(msg.sender,share,minAssetAmount);
    }

    function _redeem(address account,uint256 share,uint256 minAssetAmount) internal nonReentrant{
        require(availableShare[account] >= share,"Available balance not enough");

        uint256 assetAmount = lpToken.convertToAssets(share);
        require(assetAmount >= minAssetAmount,"Asset amount error");

        availableShare[account] -= share;
        lpToken.burn(share,0);
        emit Redeem(setEventId(),account,assetAmount,share,lpToken.price(),getRedeemLockLength(),dataStorage.redeemLockTime(),lpToken.totalSupply(),block.timestamp);

        redeemMap[setRedeemLockId()] = RedeemLock(account,assetAmount,share,lpToken.price(),block.timestamp);
    }

    function setDepositLockId() internal returns(uint256) {
        return depositLockId++;
    }

    function setRedeemLockId() internal returns (uint256) {
        return redeemLockId++;
    }

    function getDepositLockInfo(uint256[] memory ids) public view returns(DepositLock[] memory) {
        DepositLock[] memory list = new DepositLock[](ids.length);
        for(uint256 i; i<ids.length; i++){
            list[i] = depositMap[ids[i]];
        }
        return list;
    }

    function getDepositLockLength() public view returns(uint256) {
        return depositLockId;
    }

    function getRedeemLockInfo(uint256[] memory ids) public view returns(RedeemLock[] memory) {
        RedeemLock[] memory list = new RedeemLock[](ids.length);
        for(uint256 i; i<ids.length; i++){
            list[i] = redeemMap[ids[i]];
        }
        return list;
    }

    function getRedeemLockLength() public view returns(uint256) {
        return redeemLockId;
    }

    function setEventId() internal returns(uint256){
        return eventId++;
    }

    function updateDataStorageContract(address storageContract) external onlyOwner{
        require(storageContract != address(0),"Invalid Zero Address");
        emit UpdateDataStorage(msg.sender,address(dataStorage),storageContract);
        dataStorage = IDataStorage(storageContract);
        require(dataStorage.owner() != address(0),"Invalid Owner Address");
    }

    function getWithdrawAmount(address account,uint256[] memory ids) external view returns (uint256,uint256) {
        uint256 assetAmount;
        uint256 share;
        for(uint256 i; i<ids.length; i++){
            RedeemLock memory redeemLock = redeemMap[ids[i]];
            if(account == redeemLock.account){
                share += redeemLock.share;
                assetAmount += redeemLock.assetAmount;
            }
        }
        return (assetAmount,share);
    }

    function getAvailableAmount(address account,uint256[] memory ids) external view returns(uint256){
        uint256 available = availableShare[account];
        for(uint256 i; i<ids.length; i++){
            DepositLock memory depositLock = depositMap[ids[i]];
            if(account == depositLock.account){
                available += depositLock.share;
            }
        }
        return available;
    }

    function updatePreCustodian(address account) external onlyOwner {
        require(account != address(0),"Invalid Zero Address");
        emit UpdatePreCustodian(msg.sender,preCustodian,account);
        preCustodian = account;
        updatePreCustodianTime = block.timestamp;
    }

    function initialCustodian(address account) external onlyOwner{
        require(!_initializing,"Already Initialized");
        _initializing = true;
        emit UpdateCustodian(msg.sender,custodian,account);
        custodian = account;
    }

    function updateCustodian(address account) external onlyOwner {
        require(account == preCustodian,"Invalid Custodian Address");
        require(updatePreCustodianTime + UPDATE_CUSTODIAN_WAITING_TIME <= block.timestamp,"Insufficient Waiting Time");
        emit UpdateCustodian(msg.sender,custodian,preCustodian);
        custodian = preCustodian;
    }

    modifier onlyOwner()  {
        require(dataStorage.owner() == msg.sender,"Caller is not owner");
        _;
    }
}

Read Contract

MAX_REDEEM_LOCK_TIME 0xfd0a0b15 → uint256
UPDATE_CUSTODIAN_WAITING_TIME 0x05436d15 → uint256
availableShare 0x25f63b54 → uint256
balanceMap 0xd250496c → uint256
custodian 0x375b74c3 → address
dataStorage 0x8870455f → address
eventId 0x089cdd0c → uint256
getAvailableAmount 0xa7076b36 → uint256
getDepositLockInfo 0x00350e7f → tuple[]
getDepositLockLength 0x3e0a5d09 → uint256
getRedeemLockInfo 0x22637940 → tuple[]
getRedeemLockLength 0x22e07352 → uint256
getWithdrawAmount 0xc4bef167 → uint256, uint256
lpToken 0x5fcbd285 → address
token 0xfc0c546a → address
updatePreCustodianTime 0xc5db1dde → uint256

Write Contract 9 functions

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

deposit 0xe2bbb158
uint256 amount
uint256 minShare
initialCustodian 0x7856a353
address account
redeem 0x7cbc2373
uint256 share
uint256 minAssetAmount
redeemAndUnLockDeposit 0x1764f236
uint256 amount
uint256 minAssetAmount
uint256[] ids
unLockDeposit 0x70c489ef
uint256[] ids
updateCustodian 0x510c82b3
address account
updateDataStorageContract 0xa743d8c0
address storageContract
updatePreCustodian 0x9bc62245
address account
withdraw 0x983d95ce
uint256[] ids

Recent Transactions

No transactions found for this address