Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xABFEC10802e69a5d63EC954bF16a9BDAfb4590b9
Balance 15.3636 ETH
Nonce 1
Code Size 24207 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

24207 bytes
0x608060405260043610610257575f3560e01c8063a45744a31161013f578063cb35a0cf116100b3578063dd86fea111610078578063dd86fea1146105e3578063e37a375f14610600578063e6f65b361461062d578063edb0271c14610640578063f2fde38b14610653578063f434405f14610666575f5ffd5b8063cb35a0cf1461055c578063cdab1ced14610589578063ce1bd03f1461059c578063d3c7c2c7146105af578063d4acc760146105d0575f5ffd5b8063af6f1e1711610104578063af6f1e17146104c2578063b29b189f146104ef578063b4c7b78d14610502578063b55c72d91461050a578063bc157ac11461051d578063c651e01914610530575f5ffd5b8063a45744a314610452578063a55890361461045a578063a689c9831461046d578063a83d226a1461049c578063ae26299b146104af575f5ffd5b806367bbae00116101d65780637d6b67421161019b5780637d6b6742146103a9578063836644f0146103c857806385d71e20146103db5780638746ac5f146103ee5780639157cfe9146104015780639c37f8a014610430575f5ffd5b806367bbae001461033c5780637bc52b601461034f5780637be933cc146103625780637d379a481461038e5780637d5cee1e146103a1575f5ffd5b80634cafa2981161021c5780634cafa298146102dd578063549660c4146102f0578063575ecf0b146103035780636063a82c1461031657806367b8142a14610329575f5ffd5b806308e6b41c146102625780631738036a1461026c578063246fb69f1461028b578063251e24dd1461029e5780633ac846ff146102ca575f5ffd5b3661025e57005b5f5ffd5b61026a610685565b005b348015610277575f5ffd5b5061026a610286366004614e69565b61079e565b61026a610299366004614ea7565b6108ce565b3480156102a9575f5ffd5b506005546040516001600160a01b0390911681526020015b60405180910390f35b61026a6102d8366004614f02565b61095d565b61026a6102eb366004614f54565b6109e1565b61026a6102fe366004614f54565b610b00565b61026a610311366004614f02565b610b8a565b61026a610324366004614ea7565b610cd8565b61026a610337366004614f6f565b610d6f565b61026a61034a366004615001565b610ee1565b61026a61035d366004614f02565b610f07565b34801561036d575f5ffd5b5061038161037c366004615033565b610f4a565b6040516102c19190615189565b61026a61039c3660046151ba565b610fe5565b61026a61101a565b3480156103b4575f5ffd5b5061026a6103c33660046151d1565b61111e565b61026a6103d6366004615231565b6112c0565b61026a6103e9366004614f02565b61131d565b61026a6103fc366004614f54565b611368565b34801561040c575f5ffd5b5061042061041b36600461525b565b611392565b6040516102c19493929190615276565b34801561043b575f5ffd5b5061044461142c565b6040516102c19291906152cd565b61026a611555565b61026a610468366004614f54565b6115e0565b348015610478575f5ffd5b5061048c610487366004615327565b611658565b6040516102c19493929190615403565b61026a6104aa3660046154a2565b611733565b61026a6104bd366004614f54565b611a6c565b3480156104cd575f5ffd5b506104e16104dc36600461525b565b611aa8565b6040516102c19291906154c5565b61026a6104fd3660046151ba565b611b38565b61026a611b52565b61026a610518366004614f54565b611c01565b61026a61052b3660046154e9565b611c4e565b34801561053b575f5ffd5b5061054f61054a366004615327565b611f1b565b6040516102c1919061550d565b348015610567575f5ffd5b5061057b61057636600461551f565b611fa9565b6040516102c1929190615542565b61026a610597366004614f54565b61201c565b61026a6105aa366004615231565b6121f9565b3480156105ba575f5ffd5b506105c361225f565b6040516102c19190615566565b61026a6105de366004615231565b6122bf565b3480156105ee575f5ffd5b506004546040519081526020016102c1565b34801561060b575f5ffd5b5061061f61061a366004615327565b612323565b6040516102c1929190615578565b61026a61063b366004615001565b612425565b61026a61064e366004614f54565b61243f565b61026a610661366004614f54565b612469565b348015610671575f5ffd5b5061026a6106803660046151d1565b612493565b61068d6124fa565b5f6106ab6013825b335f908152910160205260409020600601612522565b90505b8015610789575f6106de6106c36001846155b2565b60135f5b335f9081529101602052604090206006019061252b565b6001600160a01b0381165f908152600e60205260409020549091506001600160f81b0316421061077657604051631d9018a360e31b81526004808201526001600160a01b038216602482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063ec80c518906044015f6040518083038186803b15801561075f575f5ffd5b505af4158015610771573d5f5f3e3d5ffd5b505050505b5080610781816155c5565b9150506106ae565b506107935f612536565b61079c60015f55565b565b6107a66124fa565b6107b4635e0d3280426155da565b836001600160f81b031611806107de57506107d262278d00426155b2565b836001600160f81b0316105b156107fc57604051632288301b60e11b815260040160405180910390fd5b80158061080a575061016881115b156108285760405163202a4de560e11b815260040160405180910390fd5b335f908152601060205260409020546001600160f81b031680156108a157335f908152600d602052604090205461086262278d00826155ed565b610875906001600160f81b0384166155da565b42101561089557604051637bb40a5f60e11b815260040160405180910390fd5b61089f8182612a43565b505b335f908152600d602052604090208290556108bf6002858585612e17565b506108c960015f55565b505050565b6108d66124fa565b6108df81612f0d565b6108e7612f43565b335f818152600f602090815260409182902080546001600160f81b0319166001600160f81b038616908117909155825190815242918101919091526001917f6c5bb0bd3664810e73504687bb17c6299453ddcf39b5a7c30aca44516ec6f7fe91015b60405180910390a361095a60015f55565b50565b6109656124fa565b61096d612f7e565b604051632b26015560e21b815273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063ac980554906109a890600490869086908301615604565b5f6040518083038186803b1580156109be575f5ffd5b505af41580156109d0573d5f5f3e3d5ffd5b505050506109dd60015f55565b5050565b6109e96124fa565b6109f1612f7e565b306001600160a01b0316816001600160a01b0316633f44bdeb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a37573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a5b9190615658565b6001600160a01b031614610a825760405163439cc0cd60e01b815260040160405180910390fd5b60405163c3ea8d2b60e01b8152600480820152600360248201526001600160a01b038216604482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063c3ea8d2b906064015b5f6040518083038186803b158015610ae1575f5ffd5b505af4158015610af3573d5f5f3e3d5ffd5b5050505061095a60015f55565b610b086124fa565b6001600160a01b0381165f908152600f60205260409020546001600160f81b0316610b3281612fa9565b610b3f62278d0082615673565b6001600160a01b0383165f908152600f6020526040812080546001600160f81b0319166001600160f81b039390931692909217909155610b80908390612fe5565b5061095a60015f55565b610b926124fa565b610b9a613aeb565b6005546001600160a01b03165f5b82811015610ccd5760095f858584818110610bc557610bc561558a565b9050602002016020810190610bda9190614f54565b6001600160a01b0316815260208101919091526040015f908120805460ff19169055600a81868685818110610c1157610c1161558a565b9050602002016020810190610c269190614f54565b6001600160a01b0316815260208101919091526040015f205490508015610cc457600a5f868685818110610c5c57610c5c61558a565b9050602002016020810190610c719190614f54565b6001600160a01b03166001600160a01b031681526020019081526020015f205f9055610cc4858584818110610ca857610ca861558a565b9050602002016020810190610cbd9190614f54565b8285613b2f565b50600101610ba8565b50506109dd60015f55565b610ce06124fa565b610ce981612f0d565b610d0d600e5f5b335f908152910160205260409020546001600160f81b0316613c0d565b335f818152600e6020908152604080832080546001600160f81b0319166001600160f81b038716908117909155815190815242928101929092527f6c5bb0bd3664810e73504687bb17c6299453ddcf39b5a7c30aca44516ec6f7fe9101610949565b610d77612f7e565b5f5b82811015610edb5760085f858584818110610d9657610d9661558a565b9050602002016020810190610dab9190614f54565b6001600160a01b0316815260208101919091526040015f205460ff16610ed3576003848483818110610ddf57610ddf61558a565b9050602002016020810190610df49190614f54565b8154600180820184555f9384526020842090910180546001600160a01b0319166001600160a01b03939093169290921790915590600890868685818110610e3d57610e3d61558a565b9050602002016020810190610e529190614f54565b6001600160a01b0316815260208101919091526040015f20805460ff19169115159190911790558115610ed357600160095f868685818110610e9657610e9661558a565b9050602002016020810190610eab9190614f54565b6001600160a01b0316815260208101919091526040015f20805460ff19169115159190911790555b600101610d79565b50505050565b610ee96124fa565b610ef4600e5f610cf0565b610efe8282613c36565b6109dd60015f55565b610f0f6124fa565b6040516368826d0d60e11b815273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063d104da1a906109a890600490869086908301615604565b610f52614dbc565b604051633d8bdd5960e01b81526004808201526001600160a01b0384166024820152821515604482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd90633d8bdd59906064015f60405180830381865af4158015610fb5573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610fdc9190810190615917565b90505b92915050565b610fed612f7e565b670de0b6b3a7640000811061101557604051632adb500d60e11b815260040160405180910390fd5b600455565b6110226124fa565b335f908152601060205260409020546001600160f81b031661104381612fa9565b335f908152600d60205260408120549062278d0061106a6001600160f81b038516426155b2565b61107491906159aa565b61107f9060016155da565b90508181106110ab5750335f90815260106020526040902080546001600160f81b0319169055806110ef565b6110b862278d00826155ed565b6110c29084615673565b335f90815260106020526040902080546001600160f81b0319166001600160f81b03929092169190911790555b6110f981836155b2565b335f908152600d60205260409020556111128183612a43565b50505061079c60015f55565b6111266124fa565b61112f84612f0d565b335f908152600e60205260409020546001600160f81b031680156112a057806001600160f81b031642101561117757604051637bb40a5f60e11b815260040160405180910390fd5b604051631d9018a360e31b815260048082015233602482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063ec80c518906044015f6040518083038186803b1580156111c5575f5ffd5b505af41580156111d7573d5f5f3e3d5ffd5b505f92506111fc9150600e9050825b335f908152910160205260409020600101612522565b11156112a057335f908152600e6020526040902054600160f81b900460ff1615611268578361124157335f908152600e6020526040902080546001600160f81b031690555b6040514281525f90339082905f516020615e3a5f395f51905f529060200160405180910390a45b60405142815233905f907f6bafde6f2640b095fd9e0b9c7d8667b6f9559effda5f6420433f65f3e8ff431d9060200160405180910390a35b6112aa8383613c36565b6112b65f86865f612e17565b50610edb60015f55565b6112c86124fa565b6112d0612f7e565b604051637d90aff560e01b81526004808201526001600160a01b03831660248201526044810182905273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd90637d90aff5906064016109a8565b6113256124fa565b61132d613aeb565b6040516305b5ce0b60e51b815273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063b6b9c160906109a890600490869086908301615604565b611370612f7e565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6040516325b3893360e21b815260048181015260036024820152811515604482015260609081908190819073e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd906396ce24cc906064015f60405180830381865af41580156113f6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261141d91908101906159c9565b93509350935093509193509193565b606080600380548060200260200160405190810160405280929190818152602001828054801561148357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611465575b5050505050915081516001600160401b038111156114a3576114a3615692565b6040519080825280602002602001820160405280156114cc578160200160208202803683370190505b5090505f5b82518110156115505760046005015f8483815181106114f2576114f261558a565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f9054906101000a900460ff168282815181106115385761153861558a565b911515602092830291909101909101526001016114d1565b509091565b61155d6124fa565b5f61156a60136001610695565b90505b80156115d5575f61158b6115826001846155b2565b601360016106c7565b6001600160a01b0381165f908152600f60205260409020549091506001600160f81b03164281116115c0576115c08282613c68565b505080806115cd906155c5565b91505061156d565b506107936001612536565b6115e86124fa565b6001600160a01b0381165f908152600e6020526040902054611612906001600160f81b0316612fa9565b604051631d9018a360e31b81526004808201526001600160a01b038216602482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063ec80c51890604401610acb565b611660614ddb565b6116a06040518060e00160405280606081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b60608073e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd63732c181260048989896040518563ffffffff1660e01b81526004016116e19493929190615a7e565b5f60405180830381865af41580156116fb573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526117229190810190615bdd565b935093509350935093509350935093565b61173b6124fa565b61176e600e83600381106117515761175161558a565b335f908152910160205260409020546001600160f81b0316613ced565b600e82600381106117815761178161558a565b335f90815291016020526040902054600160f81b900460ff16151581151514610efe576040805160808101825260045481526005546001600160a01b039081166020830152600654811692820192909252600754909116606082015281600e84600381106117f1576117f161558a565b335f9081529101602052604081208054921515600160f81b026001600160f81b0390931692909217909155611832600e85600381106111e6576111e661558a565b90505f5b81811015611a2c575f61186d82600e88600381106118565761185661558a565b335f9081529101602052604090206001019061252b565b90505f5f5f61188860048a86338b602001518c5f0151613d16565b9250925092505f604051806060016040528083865f01516118a991906155b2565b81526020018560200151815260200185604001518152509050881561191a576118d28184613dc4565b604081015190915015611915576119133360158c600381106118f6576118f661558a565b6001600160a01b0389165f90815291016020526040902090613e4f565b505b6119bd565b5f81602001511161192b575f61194b565b60208101518151604083015161194191906155ed565b61194b91906159aa565b60408201819052815182906119619083906155b2565b905250604084015160208201805161197a9083906155b2565b9052506040810151156119bd576119bb3360158c6003811061199e5761199e61558a565b6001600160a01b0389165f90815291016020526040902090613e63565b505b6119d06004858386898d60200151613e77565b816040018181516119e191906155b2565b905250604080850151908201516119fd916004918d338a613f81565b8115611a1b57611a1b82868a604001518b60600151885f01516140c8565b505060019093019250611836915050565b50821515336001600160a01b0316855f516020615e3a5f395f51905f5242604051611a5991815260200190565b60405180910390a450506109dd60015f55565b611a746124fa565b6001600160a01b0381165f908152600f60205260409020546001600160f81b0316611a9e81612fa9565b610b808282613c68565b60405163c18624cb60e01b8152600481810152600360248201528115156044820152606090819073e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd9063c18624cb906064015f60405180830381865af4158015611b08573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611b2f9190810190615d08565b91509150915091565b611b406124fa565b611b4981612536565b61095a60015f55565b611b5a6124fa565b335f908152601060205260409020546001600160f81b0316611b7b81612fa9565b335f908152600d6020526040902054611b956001826155b2565b335f908152600d602052604090205560018111611bb2575f611bbf565b611bbf62278d0083615673565b335f90815260106020526040902080546001600160f81b0319166001600160f81b0392909216919091179055611bf6600182612a43565b505061079c60015f55565b6007546001600160a01b03163314611c2c576040516308f51d4b60e31b815260040160405180910390fd5b600780546001600160a01b0319166001600160a01b0392909216919091179055565b611c566124fa565b5f600e8460038110611c6a57611c6a61558a565b335f908152910160205260409020546001600160f81b03169050611c8d81613ced565b6001600160a01b0383165f9081526008602052604090205460ff16611cc557604051633dd1b30560e01b815260040160405180910390fd5b600184148015611cde5750806001600160f81b03164210155b15611ced57611ced3382613c68565b6001600160a01b03831615611d1657611d116001600160a01b038416333085614126565b611d1a565b3491505b8115611edd57600e8460038110611d3357611d3361558a565b335f90815291016020526040902054600160f81b900460ff1615611e2557600554600480546001600160a01b03909216915f9182918291611d7b918a908a9033908990613d16565b9250925092505f611db8604051806060016040528084875f0151611d9f91906155b2565b8152602001866020015181526020018981525084613dc4565b9050836040015181604001818151611dd091906155da565b905250611de460048583868d338e8c61418d565b611dee90836155b2565b91508115611e1b576006546007548551611e1b9285928c926001600160a01b0392831692909116906140c8565b5050505050611edd565b5f600e8560038110611e3957611e3961558a565b335f9081529101602090815260408083206001600160a01b03881684526003019091528120549150819003611e9857611e9684600e8760038110611e7f57611e7f61558a565b335f90815291016020526040902060010190613e4f565b505b611ea283826155da565b600e8660038110611eb557611eb561558a565b335f9081529101602090815260408083206001600160a01b0389168452600301909152902055505b5f336001600160a01b0316855f516020615e1a5f395f51905f52868642604051611f0993929190615d6b565b60405180910390a4506108c960015f55565b611f23614ddb565b604051639b7283bf60e01b815273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd90639b7283bf90611f60906004908890889088908401615a7e565b5f60405180830381865af4158015611f7a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611fa19190810190615d8c565b949350505050565b606080611fde60158560038110611fc257611fc261558a565b6001600160a01b0386165f9081529101602052604090206141bf565b61201060188660028110611ff457611ff461558a565b6001600160a01b0387165f9081529101602052604090206141bf565b915091505b9250929050565b6120246124fa565b61202c612f7e565b306001600160a01b0316816001600160a01b0316633f44bdeb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612072573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120969190615658565b6001600160a01b0316146120bd5760405163439cc0cd60e01b815260040160405180910390fd5b600580546001600160a01b038381166001600160a01b03198316179092556003549116905f5b818110156121ed575f600382815481106120ff576120ff61558a565b5f9182526020808320909101546001600160a01b0316808352600990915260409091205490915060ff1615801561214c57506001600160a01b0381165f908152600b602052604090205415155b156121e4576040516370a0823160e01b81526001600160a01b0382811660048301525f91908616906370a0823190602401602060405180830381865afa158015612198573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121bc9190615dbd565b905080156121e2576121cf8282876141d2565b905080156121e2576121e2828288613b2f565b505b506001016120e3565b50505061095a60015f55565b6122016124fa565b61220c600e5f610cf0565b604051634632e0c360e11b81526004808201526001600160a01b0383166024820152604481018290525f606482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd90638c65c186906084016109a8565b606060038054806020026020016040519081016040528092919081815260200182805480156122b557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612297575b5050505050905090565b6122c76124fa565b6122cf612f43565b604051634632e0c360e11b81526004808201526001600160a01b0383166024820152604481018290526001606482015273e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd90638c65c186906084016109a8565b6123636040518060e00160405280606081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b6040516398577d6760e01b815260609073e44d2757e1ed012edc5dd7f16fa7f5b2c1b8fadd906398577d67906123a3906004908990899089908401615a7e565b5f60405180830381865af41580156123bd573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526123e49190810190615dd4565b612419601387600281106123fa576123fa61558a565b6001600160a01b0388165f9081529101602052604090206006016141bf565b91509150935093915050565b61242d6124fa565b612435612f43565b610efe828261438d565b612447613aeb565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b612471612f7e565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b61249b6124fa565b6124a484612f0d565b335f908152600f60205260409020546001600160f81b0316156124da5760405163119b4fd360e11b815260040160405180910390fd5b6124e4828261438d565b6124f1600185855f612e17565b610edb60015f55565b60025f540361251c57604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b5f610fdf825490565b5f610fdc83836143c1565b5f6125636013836002811061254d5761254d61558a565b335f908152910160205260409020600201612522565b905080156128d1576040805160808101825260045481526005546001600160a01b03908116602083015260065481169282019290925260075490911660608201525b81156128cf575f6125e46125ba6001856155b2565b601386600281106125cd576125cd61558a565b335f9081529101602052604090206002019061252b565b905061260760405180606001604052805f81526020015f81526020015f81525090565b5f5f61261d6004858760200151885f01516143e7565b9185526001600160a01b0386165f908152600b60209081526040909120549086015292509050601387600281106126565761265661558a565b335f9081529101602090815260408083206001600160a01b03881684526005019091528082205490850152835161268e9083906155b2565b90505f84602001518286604001516126a691906155ed565b6126b091906159aa565b90505f604051806060016040528083856126ca91906155b2565b8152602001876040015188602001516126e391906155b2565b81526020015f815250905085604001518160400151146128205780604001515f03612771576127368760138c6002811061271f5761271f61558a565b335f90815291016020526040902060020190613e63565b5061276b3360188c6002811061274e5761274e61558a565b6001600160a01b038b165f90815291016020526040902090613e63565b506127e1565b85604001515f036127e1576127aa8760138c600281106127935761279361558a565b335f90815291016020526040902060020190613e4f565b506127df3360188c600281106127c2576127c261558a565b6001600160a01b038b165f90815291016020526040902090613e4f565b505b604081015160138b600281106127f9576127f961558a565b335f9081529101602090815260408083206001600160a01b038c1684526005019091529020555b61283360048783888b8d60200151613e77565b61283d90836155b2565b9150811561285057612850338884614452565b831561286e5761286e84888a604001518b606001518a5f01516140c8565b604080518381524260208201526001600160a01b0389169133918d917f3e356ee9071ea983e847cc7da7b8b224b8f44262f7c9ce77262ea0e854a5442c910160405180910390a45050505050505081806128c7906155c5565b9250506125a5565b505b6128fa601383600281106128e7576128e761558a565b335f908152910160205260409020612522565b90505b80156109dd575f6129396129126001846155b2565b601385600281106129255761292561558a565b335f9081529101602052604090209061252b565b90505f6013846002811061294f5761294f61558a565b335f9081529101602090815260408083206001600160a01b038616845260040190915290205490506013846002811061298a5761298a61558a565b335f9081529101602090815260408083206001600160a01b03861684526004019091528120556129db82601386600281106129c7576129c761558a565b335f90815291016020526040902090613e63565b506129e7338383614452565b604080518281524260208201526001600160a01b03841691339187917f3e356ee9071ea983e847cc7da7b8b224b8f44262f7c9ce77262ea0e854a5442c910160405180910390a450508080612a3b906155c5565b9150506128fd565b335f90815260106020526040902054600160f81b900460ff168015612cae576040805160808101825260045481526005546001600160a01b03908116602083015260065481169282019290925260075490911660608201525f612aa8600e60026111e6565b90505b8015612ca7575f612ac9612ac06001846155b2565b600e6002611856565b90505f5f5f612ae56004600286338a602001518b5f0151613d16565b925092509250612b1260405180608001604052805f81526020015f81526020015f81526020015f81525090565b8351612b1f9083906155b2565b815260408401518990612b33908c906155ed565b612b3d91906159aa565b602080830182905285015182519091612b5691906155ed565b612b6091906159aa565b604080830191825280516060810190915290518251612bcd9260049288928291612b89916155b2565b815260200185602001518960200151612ba291906155b2565b815260200185602001518960400151612bbb91906155b2565b815250866002338b8e6020015161418d565b81604001818151612bde91906155b2565b905250604081015115612c1e5760328160400151612bfc91906159aa565b606082018190526040820151612c1e9133918891612c19916155b2565b614452565b5f816060015183612c2f91906155da565b1115612c5557612c55828260600151878a604001518b606001513360028b5f01516144a7565b6002336001600160a01b031660025f516020615e1a5f395f51905f5288856040015142604051612c8793929190615d6b565b60405180910390a450505050508080612c9f906155c5565b915050612aab565b5050612e04565b6006546007546001600160a01b0391821691165f612cce600e60026111e6565b90505b8015612e00575f612ce6612ac06001846155b2565b335f9081526010602090815260408083206001600160a01b038516845260030190915281205491925087612d1a8a846155ed565b612d2491906159aa565b90508015612db457612d3681836155b2565b335f9081526010602090815260408083206001600160a01b0388168452600301909152902055818103612d8057335f908152601060205260409020612d7e9060010184613e63565b505b5f612d8c6032836159aa565b9050612d9d3385612c1984866155b2565b8015612db257612db281858989336002614575565b505b6002336001600160a01b031660025f516020615e1a5f395f51905f52868542604051612de293929190615d6b565b60405180910390a45050508080612df8906155c5565b915050612cd1565b5050505b8183036108c9576108c9600233836145e0565b82600e8560038110612e2b57612e2b61558a565b335f81815291909201602090815260409182902080546001600160f81b0319166001600160f81b0395861617905581519387168452830184905242908301529085907f0f9bfdcb4b183e1e1c8740a5b970788e46c5091bd09deffda96ecc52952817779060600160405180910390a38115610edb5781600e8560038110612eb457612eb461558a565b335f81815291909201602090815260409182902080546001600160f81b0316600160f81b95151595909502949094179093555142815260019287915f516020615e3a5f395f51905f52910160405180910390a450505050565b612f1a62278d00426155b2565b816001600160f81b0316101561095a57604051632288301b60e11b815260040160405180910390fd5b335f908152600f60205260409020546001600160f81b0316612f6481613ced565b806001600160f81b0316421061095a5761095a3382613c68565b6001546001600160a01b0316331461079c576040516308f51d4b60e31b815260040160405180910390fd5b6001600160f81b0381161580612fc75750806001600160f81b031642105b1561095a57604051637bb40a5f60e11b815260040160405180910390fd5b6001600160a01b0382165f90815260126020526040812061300590612522565b6001600160401b0381111561301c5761301c615692565b60405190808252806020026020018201604052801561306057816020015b604080518082019091525f808252602082015281526020019060019003908161303a5790505b50905080515f0361307057505050565b5f805b825181101561317a576001600160a01b0385165f90815260126020526040902061309d908261252b565b8382815181106130af576130af61558a565b60209081029190910101516001600160a01b0390911690525f60116001015f876001600160a01b03166001600160a01b031681526020019081526020015f206002015f8584815181106131045761310461558a565b60200260200101515f01516001600160a01b03166001600160a01b031681526020019081526020015f20549050808361313d91906155da565b9250613151670d99a8cec7e20000826155ed565b8483815181106131635761316361558a565b602090810291909101810151015250600101613073565b5060018311156132a1575f61319782670de0b6b3a76400006155b2565b90505f6131a482806155ed565b905060025b858110156131d857670de0b6b3a76400006131c484846155ed565b6131ce91906159aa565b91506001016131a9565b506131f2816ec097ce7bc90715b34b9f10000000006155b2565b905061320683670de0b6b3a76400006155ed565b91505f5b845181101561326f5782828683815181106132275761322761558a565b60200260200101516020015161323d91906155ed565b61324791906159aa565b8582815181106132595761325961558a565b602090810291909101810151015260010161320a565b508181613284670de0b6b3a7640000866155ed565b61328e91906155ed565b61329891906159aa565b925050506132b6565b6132b3670de0b6b3a7640000826155ed565b90505b6001600160a01b0384165f908152600f6020526040902054600160f81b900460ff16156137b7576040805160808101825260045481526005546001600160a01b03908116602083015260065481169282019290925260075490911660608201525f61333d600e60016001600160a01b0389165f908152910160205260409020600101612522565b90505b80156137b0576133826040518060c001604052805f6001600160a01b031681526020015f81526020015f81526020015f81526020015f81526020015f81525090565b6133b06133906001846155b2565b6001600160a01b0389165f908152600f602052604090206001019061252b565b6001600160a01b03168152604080516060810182525f80825260208201819052918101919091526133f160046001845f01518b8860200151895f0151613d16565b60408086019190915260208501919091528101519091506ec097ce7bc90715b34b9f1000000000906134249087906155ed565b61342e91906159aa565b6060830181905260808301525f5b8651811015613625575f6ec097ce7bc90715b34b9f10000000008883815181106134685761346861558a565b602002602001015160200151846040015161348391906155ed565b61348d91906159aa565b9050801561361c5780846080018181516134a791906155b2565b9052505f60136001015f8a85815181106134c3576134c361558a565b60200260200101515f01516001600160a01b03166001600160a01b031681526020019081526020015f206005015f865f01516001600160a01b03166001600160a01b031681526020019081526020015f20549050805f036135bd5784516135779060136001015f8c878151811061353c5761353c61558a565b60200260200101515f01516001600160a01b03166001600160a01b031681526020019081526020015f20600201613e4f90919063ffffffff16565b506135bb89848151811061358d5761358d61558a565b6020908102919091010151516018600188516001600160a01b03165f90815291016020526040902090613e4f565b505b6135c782826155da565b60136001015f8b86815181106135df576135df61558a565b602090810291909101810151516001600160a01b0390811683528282019390935260409182015f9081208a51909416815260059093019052902055505b5060010161343c565b5060208101516040830151825161363c91906155b2565b836080015161364b91906155ed565b61365591906159aa565b8260a00181815250506136dc60048260405180606001604052808660a001518760400151875f015161368791906155b2565b61369191906155b2565b8152602001866080015186602001516136aa91906155b2565b8152602001866060015186604001516136c391906155b2565b815250856020015160018d885f01518b6020015161418d565b8260a0018181516136ed91906155b2565b90525060a082015160408301515f91613705916155da565b11156137325761373282604001518360a00151845f0151876040015188606001518d6001885f01516144a7565b6002886001600160a01b031660015f516020615e1a5f395f51905f52855f015185602001518760400151875f015161376a91906155b2565b886060015161377991906155ed565b61378391906159aa565b4260405161379393929190615d6b565b60405180910390a4505080806137a8906155c5565b915050613340565b5050610edb565b6006546007546001600160a01b0391821691165f6137f1600e60016001600160a01b038a165f908152910160205260409020600101612522565b90505b8015613ae2575f6138296138096001846155b2565b6001600160a01b038a165f908152600f602052604090206001019061252b565b6001600160a01b038981165f908152600f60209081526040808320938516835260039093019052908120549192506ec097ce7bc90715b34b9f100000000061387188846155ed565b61387b91906159aa565b90508015613a9657805f5b8951811015613a1e575f6ec097ce7bc90715b34b9f10000000008b83815181106138b2576138b261558a565b602002602001015160200151866138c991906155ed565b6138d391906159aa565b90508015613a15576138e581846155b2565b92505f60136001015f8d85815181106139005761390061558a565b60200260200101515f01516001600160a01b03166001600160a01b031681526020019081526020015f206004015f886001600160a01b03166001600160a01b031681526020019081526020015f20549050805f0361399f5761399d8760136001015f8f87815181106139745761397461558a565b602090810291909101810151516001600160a01b031682528101919091526040015f2090613e4f565b505b6139a982826155da565b60136001015f8e86815181106139c1576139c161558a565b60200260200101515f01516001600160a01b03166001600160a01b031681526020019081526020015f206004015f896001600160a01b03166001600160a01b031681526020019081526020015f2081905550505b50600101613886565b50613a2982846155b2565b6001600160a01b038c81165f908152600f602090815260408083209389168352600390930190522055828203613a7f576001600160a01b038b165f908152600f60205260409020613a7d9060010185613e63565b505b8015613a9457613a94818589898f6001614575565b505b60028a6001600160a01b031660015f516020615e1a5f395f51905f52868542604051613ac493929190615d6b565b60405180910390a45050508080613ada906155c5565b9150506137f4565b50505050505050565b6002546001600160a01b03163314801590613b1157506001546001600160a01b03163314155b1561079c576040516308f51d4b60e31b815260040160405180910390fd5b6001600160a01b038316613ba0576040516311f9fbc960e21b81525f6004820152602481018390526001600160a01b038216906347e7ef249084906044015f604051808303818588803b158015613b84575f5ffd5b505af1158015613b96573d5f5f3e3d5ffd5b5050505050505050565b613bb46001600160a01b0384168284614691565b6040516311f9fbc960e21b81526001600160a01b038481166004830152602482018490528216906347e7ef24906044015f604051808303815f87803b158015613bfb575f5ffd5b505af1158015613ae2573d5f5f3e3d5ffd5b806001600160f81b0316421061095a57604051635c4c8a6760e01b815260040160405180910390fd5b670de0b6b3a7640000613c4a83835f614720565b146109dd5760405163d255f59f60e01b815260040160405180910390fd5b5f62278d00613c806001600160f81b038416426155b2565b613c8a91906159aa565b613c959060016155da565b9050613ca462278d00826155ed565b613cae9083615673565b6001600160a01b0384165f908152600f6020526040902080546001600160f81b0319166001600160f81b03929092169190911790556108c98382612fe5565b806001600160f81b03165f0361095a57604051635861b41d60e01b815260040160405180910390fd5b613d3760405180606001604052805f81526020015f81526020015f81525090565b5f5f613d45898887876143e7565b9185526001600160a01b0389165f90815260078c0160209081526040909120549086015292509050600a89018860038110613d8257613d8261558a565b6001600160a01b039788165f90815291016020908152604080832099909816825260039098019097529585902054948201949094529692955092935090915050565b613de560405180606001604052805f81526020015f81526020015f81525090565b5f8211613df6578260400151613e16565b825160208401516040850151613e0c91906155ed565b613e1691906159aa565b6040808301919091528301518351613e2e91906155da565b815260408101516020840151613e4491906155da565b602082015292915050565b5f610fdc836001600160a01b0384166149c8565b5f610fdc836001600160a01b038416614a14565b5f8560200151856020015114613ea9576020808601516001600160a01b0385165f90815260078a019092526040909120555b84518414613ece5784516001600160a01b0384165f9081526008890160205260409020555b6001600160a01b0383165f90815260058801602052604090205460ff1615613f1b578551855114613f165784516001600160a01b0384165f9081526006890160205260409020555b613f77565b855185511015613f5357845186515f91613f34916155b2565b9050613f418482856141d2565b613f4b90826155b2565b915050613f77565b855185511115613f775785518551613f77918591613f7191906155b2565b84613b2f565b9695505050505050565b8484146140c057835f0361400557613fc88187600a018560038110613fa857613fa861558a565b6001600160a01b0386165f90815291016020526040902060010190613e63565b50613fff82876011018560038110613fe257613fe261558a565b6001600160a01b0385165f90815291016020526040902090613e63565b5061407e565b845f0361407e576140458187600a0185600381106140255761402561558a565b6001600160a01b0386165f90815291016020526040902060010190613e4f565b5061407c8287601101856003811061405f5761405f61558a565b6001600160a01b0385165f90815291016020526040902090613e4f565b505b8386600a0184600381106140945761409461558a565b6001600160a01b038581165f908152919092016020908152604080832093861683526003909301905220555b505050505050565b6140d485858585614af7565b6040805186815260208101839052428183015290516001600160a01b038616917f49e4a434fe2ae4f4ebd368ac3780530e5a983ef95bf3ee98bda7b3ede305d47c919081900360600190a25050505050565b6040516001600160a01b038481166024830152838116604483015260648201839052610edb9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614bcf565b5f6141a48989604001518960400151888888613f81565b6141b2898989898787613e77565b9998505050505050505050565b60605f6141cb83614c3f565b9392505050565b5f806001600160a01b0385161561424e576040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015614225573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142499190615dbd565b614250565b475b60405163f3fef3a360e01b81526001600160a01b038781166004830152602482018790529192509084169063f3fef3a3906044016020604051808303815f875af11580156142a0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142c49190615dbd565b91505f6001600160a01b03861615614341576040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa158015614318573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061433c9190615dbd565b614343565b475b9050818110156143665760405163439cc0cd60e01b815260040160405180910390fd5b8261437183836155b2565b10156143845761438182826155b2565b92505b50509392505050565b670de0b6b3a76400006143a283836001614720565b11156109dd57604051632adb500d60e11b815260040160405180910390fd5b5f825f0182815481106143d6576143d661558a565b905f5260205f200154905092915050565b6001600160a01b0383165f90815260088501602052604081205481811561444857614413878787614c98565b92508183111561444857670de0b6b3a76400008461443184866155b2565b61443b91906155ed565b61444591906159aa565b90505b9450945094915050565b6001600160a01b038216614493576040516001600160a01b0384169082156108fc029083905f818181858888f19350505050158015610edb573d5f5f3e3d5ffd5b6108c96001600160a01b0383168483614d46565b6144bc6144b4888a6155da565b878787614af7565b871561450e576040805189815260208101839052428183015290516001600160a01b038816917f49e4a434fe2ae4f4ebd368ac3780530e5a983ef95bf3ee98bda7b3ede305d47c919081900360600190a25b8615613b9657856001600160a01b0316836001600160a01b0316837f4fbeb4730651aa647c63a8181251ca11672027de33ab396efd26ee538043ba3a8a42604051614563929190918252602082015260400190565b60405180910390a45050505050505050565b61458186868686614af7565b846001600160a01b0316826001600160a01b0316827f4fbeb4730651aa647c63a8181251ca11672027de33ab396efd26ee538043ba3a89426040516145d0929190918252602082015260400190565b60405180910390a4505050505050565b801561464857600e83600381106145f9576145f961558a565b6001600160a01b0384165f81815292909101602052604080832080546001600160f81b031690555185905f516020615e3a5f395f51905f529061463f9042815260200190565b60405180910390a45b816001600160a01b0316837f6bafde6f2640b095fd9e0b9c7d8667b6f9559effda5f6420433f65f3e8ff431d4260405161468491815260200190565b60405180910390a3505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526146e28482614d77565b610edb576040516001600160a01b0384811660248301525f604483015261471691869182169063095ea7b39060640161415b565b610edb8482614bcf565b5f80614738601184600281106128e7576128e761558a565b90505b8015614809575f6147636147506001846155b2565b601186600281106129255761292561558a565b9050601184600281106147785761477861558a565b335f9081529101602090815260408083206001600160a01b038516845260029081019092528220919091556147bc908290601190879081106129c7576129c761558a565b506147f433601386600281106147d4576147d461558a565b6001600160a01b0385165f90815291016020526040902060060190613e63565b50508080614801906155c5565b91505061473b565b505f5b838110156149c0575f8585838181106148275761482761558a565b9050604002016020013511156149b85761488985858381811061484c5761484c61558a565b6148629260206040909202019081019150614f54565b601185600281106148755761487561558a565b335f90815291016020526040902090613e4f565b6148a657604051639f9950f760e01b815260040160405180910390fd5b8484828181106148b8576148b861558a565b90506040020160200135826148cd91906155da565b91508484828181106148e1576148e161558a565b905060400201602001356004600d0184600281106149015761490161558a565b335f908152910160205260408120600201908787858181106149255761492561558a565b61493b9260206040909202019081019150614f54565b6001600160a01b0316815260208101919091526040015f20556149b6336013856002811061496b5761496b61558a565b015f88888681811061497f5761497f61558a565b6149959260206040909202019081019150614f54565b6001600160a01b0316815260208101919091526040015f2060060190613e4f565b505b60010161480c565b509392505050565b5f818152600183016020526040812054614a0d57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610fdf565b505f610fdf565b5f8181526001830160205260408120548015614aee575f614a366001836155b2565b85549091505f90614a49906001906155b2565b9050808214614aa8575f865f018281548110614a6757614a6761558a565b905f5260205f200154905080875f018481548110614a8757614a8761558a565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614ab957614ab9615e05565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610fdf565b5f915050610fdf565b5f6064614b058660556155ed565b614b0f91906159aa565b90506001600160a01b038416614b95576040516001600160a01b0384169082156108fc029083905f818181858888f19350505050158015614b52573d5f5f3e3d5ffd5b506001600160a01b0382166108fc614b6a83886155b2565b6040518115909202915f818181858888f19350505050158015614b8f573d5f5f3e3d5ffd5b50614bc8565b614ba96001600160a01b0385168483614d46565b614bc882614bb783886155b2565b6001600160a01b0387169190614d46565b5050505050565b5f5f60205f8451602086015f885af180614bee576040513d5f823e3d81fd5b50505f513d91508115614c05578060011415614c12565b6001600160a01b0384163b155b15610edb57604051635274afe760e01b81526001600160a01b038516600482015260240160405180910390fd5b6060815f01805480602002602001604051908101604052809291908181526020018280548015614c8c57602002820191905f5260205f20905b815481526020019060010190808311614c78575b50505050509050919050565b6001600160a01b0382165f90815260058401602052604081205460ff16614d26576040516370a0823160e01b81526001600160a01b0384811660048301528316906370a0823190602401602060405180830381865afa158015614cfd573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614d219190615dbd565b611fa1565b50506001600160a01b03165f908152600691909101602052604090205490565b6040516001600160a01b038381166024830152604482018390526108c991859182169063a9059cbb9060640161415b565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015613f7757508115614da85780600114613f77565b50505050506001600160a01b03163b151590565b6040518060400160405280614dcf614e02565b81526020015f81525090565b6040518060600160405280614dee614e02565b815260200160608152602001606081525090565b6040518060e001604052805f6001600160f81b031681526020015f1515815260200160608152602001606081526020016060815260200160608152602001606081525090565b6001600160f81b038116811461095a575f5ffd5b801515811461095a575f5ffd5b5f5f5f60608486031215614e7b575f5ffd5b8335614e8681614e48565b92506020840135614e9681614e5c565b929592945050506040919091013590565b5f60208284031215614eb7575f5ffd5b81356141cb81614e48565b5f5f83601f840112614ed2575f5ffd5b5081356001600160401b03811115614ee8575f5ffd5b6020830191508360208260051b8501011115612015575f5ffd5b5f5f60208385031215614f13575f5ffd5b82356001600160401b03811115614f28575f5ffd5b614f3485828601614ec2565b90969095509350505050565b6001600160a01b038116811461095a575f5ffd5b5f60208284031215614f64575f5ffd5b81356141cb81614f40565b5f5f5f60408486031215614f81575f5ffd5b83356001600160401b03811115614f96575f5ffd5b614fa286828701614ec2565b9094509250506020840135614fb681614e5c565b809150509250925092565b5f5f83601f840112614fd1575f5ffd5b5081356001600160401b03811115614fe7575f5ffd5b6020830191508360208260061b8501011115612015575f5ffd5b5f5f60208385031215615012575f5ffd5b82356001600160401b03811115615027575f5ffd5b614f3485828601614fc1565b5f5f60408385031215615044575f5ffd5b823561504f81614f40565b9150602083013561505f81614e5c565b809150509250929050565b5f8151808452602084019350602083015f5b828110156150a35781516001600160a01b031686526020958601959091019060010161507c565b5093949350505050565b5f8151808452602084019350602083015f5b828110156150a35781518652602095860195909101906001016150bf565b80516001600160f81b031682526020808201515f916150ff9085018215159052565b50604082015160e0604085015261511960e085018261506a565b90506060830151848203606086015261513282826150ad565b9150506080830151848203608086015261514c82826150ad565b91505060a083015184820360a086015261516682826150ad565b91505060c083015184820360c086015261518082826150ad565b95945050505050565b602081525f8251604060208401526151a460608401826150dd565b9050602084015160408401528091505092915050565b5f602082840312156151ca575f5ffd5b5035919050565b5f5f5f5f606085870312156151e4575f5ffd5b84356151ef81614e48565b935060208501356151ff81614e5c565b925060408501356001600160401b03811115615219575f5ffd5b61522587828801614fc1565b95989497509550505050565b5f5f60408385031215615242575f5ffd5b823561524d81614f40565b946020939093013593505050565b5f6020828403121561526b575f5ffd5b81356141cb81614e5c565b608081525f615288608083018761506a565b828103602084015261529a81876150ad565b905082810360408401526152ae81866150ad565b905082810360608401526152c281856150ad565b979650505050505050565b604081525f6152df604083018561506a565b82810360208401528084518083526020830191506020860192505f5b8181101561531b57835115158352602093840193909201916001016152fb565b50909695505050505050565b5f5f5f60608486031215615339575f5ffd5b83359250602084013561534b81614f40565b91506040840135614fb681614e5c565b5f81516060845261536f60608501826150dd565b905060208301518482036020860152615388828261506a565b9150506040830151848203604086015261518082826150ad565b5f815160e084526153b660e085018261506a565b9050602083015184820360208601526153cf82826150ad565b915050604083015184820360408601526153e9828261506a565b9150506060830151848203606086015261513282826150ad565b608081525f615415608083018761535b565b828103602084015261542781876153a2565b9050828103604084015261543b818661506a565b9050828103606084015280845180835260208301915060208160051b840101602087015f5b8381101561549257601f1986840301855261547c83835161535b565b6020958601959093509190910190600101615460565b50909a9950505050505050505050565b5f5f604083850312156154b3575f5ffd5b82359150602083013561505f81614e5c565b604081525f6154d7604083018561506a565b828103602084015261518081856150ad565b5f5f5f606084860312156154fb575f5ffd5b833592506020840135614e9681614f40565b602081525f610fdc602083018461535b565b5f5f60408385031215615530575f5ffd5b82359150602083013561505f81614f40565b604081525f615554604083018561506a565b8281036020840152615180818561506a565b602081525f610fdc602083018461506a565b604081525f61555460408301856153a2565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115610fdf57610fdf61559e565b5f816155d3576155d361559e565b505f190190565b80820180821115610fdf57610fdf61559e565b8082028115828204841417610fdf57610fdf61559e565b83815260406020820181905281018290525f8360608301825b8581101561564d57823561563081614f40565b6001600160a01b031682526020928301929091019060010161561d565b509695505050505050565b5f60208284031215615668575f5ffd5b81516141cb81614f40565b6001600160f81b038181168382160190811115610fdf57610fdf61559e565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b03811182821017156156c8576156c8615692565b60405290565b604051601f8201601f191681016001600160401b03811182821017156156f6576156f6615692565b604052919050565b805161570981614e48565b919050565b805161570981614e5c565b5f6001600160401b0382111561573157615731615692565b5060051b60200190565b5f82601f83011261574a575f5ffd5b815161575d61575882615719565b6156ce565b8082825260208201915060208360051b86010192508583111561577e575f5ffd5b602085015b838110156157a457805161579681614f40565b835260209283019201615783565b5095945050505050565b5f82601f8301126157bd575f5ffd5b81516157cb61575882615719565b8082825260208201915060208360051b8601019250858311156157ec575f5ffd5b602085015b838110156157a45780518352602092830192016157f1565b5f60e08284031215615819575f5ffd5b6158216156a6565b905061582c826156fe565b815261583a6020830161570e565b602082015260408201516001600160401b03811115615857575f5ffd5b6158638482850161573b565b60408301525060608201516001600160401b03811115615881575f5ffd5b61588d848285016157ae565b60608301525060808201516001600160401b038111156158ab575f5ffd5b6158b7848285016157ae565b60808301525060a08201516001600160401b038111156158d5575f5ffd5b6158e1848285016157ae565b60a08301525060c08201516001600160401b038111156158ff575f5ffd5b61590b848285016157ae565b60c08301525092915050565b5f60208284031215615927575f5ffd5b81516001600160401b0381111561593c575f5ffd5b82016040818503121561594d575f5ffd5b604080519081016001600160401b038111828210171561596f5761596f615692565b60405281516001600160401b03811115615987575f5ffd5b61599386828501615809565b825250602091820151918101919091529392505050565b5f826159c457634e487b7160e01b5f52601260045260245ffd5b500490565b5f5f5f5f608085870312156159dc575f5ffd5b84516001600160401b038111156159f1575f5ffd5b6159fd8782880161573b565b94505060208501516001600160401b03811115615a18575f5ffd5b615a24878288016157ae565b93505060408501516001600160401b03811115615a3f575f5ffd5b615a4b878288016157ae565b92505060608501516001600160401b03811115615a66575f5ffd5b615a72878288016157ae565b91505092959194509250565b93845260208401929092526001600160a01b031660408301521515606082015260800190565b5f60608284031215615ab4575f5ffd5b604051606081016001600160401b0381118282101715615ad657615ad6615692565b806040525080915082516001600160401b03811115615af3575f5ffd5b615aff85828601615809565b82525060208301516001600160401b03811115615b1a575f5ffd5b615b268582860161573b565b60208301525060408301516001600160401b03811115615b44575f5ffd5b615b50858286016157ae565b6040830152505092915050565b5f60e08284031215615b6d575f5ffd5b615b756156a6565b905081516001600160401b03811115615b8c575f5ffd5b615b988482850161573b565b82525060208201516001600160401b03811115615bb3575f5ffd5b615bbf848285016157ae565b60208301525060408201516001600160401b03811115615857575f5ffd5b5f5f5f5f60808587031215615bf0575f5ffd5b84516001600160401b03811115615c05575f5ffd5b615c1187828801615aa4565b94505060208501516001600160401b03811115615c2c575f5ffd5b615c3887828801615b5d565b93505060408501516001600160401b03811115615c53575f5ffd5b615c5f8782880161573b565b92505060608501516001600160401b03811115615c7a575f5ffd5b8501601f81018713615c8a575f5ffd5b8051615c9861575882615719565b8082825260208201915060208360051b850101925089831115615cb9575f5ffd5b602084015b83811015615cf95780516001600160401b03811115615cdb575f5ffd5b615cea8c602083890101615aa4565b84525060209283019201615cbe565b50969995985093965050505050565b5f5f60408385031215615d19575f5ffd5b82516001600160401b03811115615d2e575f5ffd5b615d3a8582860161573b565b92505060208301516001600160401b03811115615d55575f5ffd5b615d61858286016157ae565b9150509250929050565b6001600160a01b039390931683526020830191909152604082015260600190565b5f60208284031215615d9c575f5ffd5b81516001600160401b03811115615db1575f5ffd5b611fa184828501615aa4565b5f60208284031215615dcd575f5ffd5b5051919050565b5f60208284031215615de4575f5ffd5b81516001600160401b03811115615df9575f5ffd5b611fa184828501615b5d565b634e487b7160e01b5f52603160045260245ffdfe1e7618862adc6575f305e8d45ede8e1496fe8fe7324f5f0c3a0e4074976a3b038868aac92e09a85f565e976983952937f3a78e317511d85ddc5ecfa4c09abce0a2646970667358221220671a636017c69f4b91b9da0aef8442a432dee842ec4d79353124cbd8a79ec41f64736f6c634300081d0033

Verified Source Code Partial Match

Compiler: v0.8.29+commit.ab55807c EVM: cancun Optimization: Yes (200 runs)
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
ReentrancyGuard.sol 87 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * 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 ReentrancyGuard {
    // 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;

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

    constructor() {
        _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 {
        // 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 {
        // 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) {
        return _status == ENTERED;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
EnumerableSet.sol 375 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ```solidity
 * 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 EnumerableSet {
    // 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 is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 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 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[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._positions[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) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

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

        assembly ("memory-safe") {
            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 in 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;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}
TokenGuard.sol 2616 lines
// SPDX-License-Identifier: None
pragma solidity ^0.8.29;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;

struct BeneficiaryPercent {
    address beneficiary; // Beneficiary's address
    uint256 percent; // Benefit ratio, 1e18 = 100%
}

struct Asset {
    uint248 releaseTime; // Next release time
    bool isFinanced;
    EnumerableSet.AddressSet tokenSet;
    mapping(address => uint256) balanceMap;
}

struct BeneficiaryMap {
    EnumerableSet.AddressSet beneficiarySet; // Beneficiary's address set
    mapping(address => uint256) percentMap; // Benefit ratio map (The sum must be 100%), 1e18 = 100%
}

struct BenefitStatus {
    EnumerableSet.AddressSet claimableTokenSet;
    EnumerableSet.AddressSet claimableFinancedTokenSet;
    mapping(address => uint256) claimableBalanceMap;
    mapping(address => uint256) claimableFinancedBalanceMap;
    EnumerableSet.AddressSet effectiveSettlors;
}

struct BasicAsset {
    uint248 releaseTime;
    bool isFinanced;
    address[] tokens;
    uint256[] balances;
    uint256[] sumActualBalances;
    uint256[] sumRecordBalances;
    uint256[] sumInterestBases;
}

struct LegacyOrTrust {
    BasicAsset basicAsset;
    address[] beneficiaries;
    uint256[] percentages;
}

struct Pension {
    BasicAsset basicAsset;
    uint256 releaseMonths;
}

struct BenefitAsset {
    address[] claimableTokens;
    uint256[] claimableBalances;
    address[] claimableFinancedTokens;
    uint256[] claimableFinancedBalances;
    uint256[] sumActualBalances;
    uint256[] sumRecordBalances;
    uint256[] sumInterestBases;
}

struct FinParam {
    uint256 interestFeeRate;
    address financeContractAddress;
    address receiver1;
    address receiver2;
}

struct Vars {
    uint256 sumActual;
    uint256 sumRecord;
    uint256 balance;
}

struct TmpVars1 {
    address token;
    uint256 sumInterestBase;
    uint256 interestFee;
    uint256 record;
    uint256 feeRecord;
    uint256 withdrawFee;
}

struct TmpVars2 {
    uint256 sumActualNew;
    uint256 record;
    uint256 amount;
    uint256 withdrawFee;
}

struct Store {
    uint256 interestFeeRate; // Service fee rate based on financial profit, 1e18 = 100%
    address financeContractAddress;
    address receiver1;
    address receiver2;
    mapping(address => bool) isTokenSupported;
    mapping(address => bool) isTokenFinancingPaused;
    mapping(address => uint256) pausingFinancedActualMap;
    mapping(address => uint256) sumFinancedRecordMap;
    mapping(address => uint256) sumInterestBaseMap;
    mapping(address => uint256) pensionReleaseMonths; // Months of pension payment remaining (1 ~ MONTHS_RELEASE_MAX)
    mapping(address => Asset)[3] assets;
    mapping(address => BeneficiaryMap)[2] beneficiaryMaps;
    mapping(address => BenefitStatus)[2] benefitStatuses;
    mapping(address => EnumerableSet.AddressSet)[3] financedTokenSettlors;
    mapping(address => EnumerableSet.AddressSet)[2] financedTokenBeneficiaries;
}

enum OperationType {
    Deposit,
    Withdraw,
    Execute
}

error TotalPercentNot100(); // The benefit ratio does not add up to 100%
error RateExceeds100Percent();
error AlreadyExists(); // Trust already exists and cannot be created repeatedly until the assets are cleared
error NotExists(); // This asset does not exist or has been terminated
error OnlyOwnerCanCall();
error OnlyCallBeforeRelease(); // It can only be called before the release time, but the current time has reached the release time, or this asset has not been created
error OnlyCallAfterRelease(); // It can only be called after the release time, but the current time has not reached the release time, or this asset has not been created
error InvalidReleaseTime(); // Must be no earlier than the current time - 30 days, and pension must also be no later than the current time + 50 years
error InvalidReleaseMonths(); // 1 ~ MONTHS_RELEASE_MAX
error BeneficiaryDuplicated(); // Beneficiary's address duplicated
error TokenNotSupported();
error VerificationFailed(); // The balance decreases after the withdrawal of financial assets, or financeContract.tokenGuardContract is not address of this

event AssetCreated(
    uint256 indexed assetMode,
    address indexed settlor,
    uint248 releaseTime,
    uint256 releaseMonths,
    uint256 timestamp
);
event AssetOperation(
    uint256 indexed assetMode,
    address indexed settlor,
    OperationType indexed operationType,
    address token,
    uint256 amount,
    uint256 timestamp
); // Asset operation record (Deposit, Withdraw, Execute)
event Claimed(
    uint256 indexed assetMode,
    address indexed beneficiary,
    address indexed token,
    uint256 amount,
    uint256 timestamp
); // Pension does not emit the event
event AssetTerminated(
    uint256 indexed assetMode,
    address indexed settlor,
    uint256 timestamp
);
event FinanceChanged(
    uint256 indexed assetMode,
    address indexed settlor,
    bool indexed isFinanced,
    uint256 timestamp
); // Financial status update (This event will be emitted if financial is enabled when the asset is created, and will be emitted if financial is disabled when the asset is terminated)
event AssetReleaseTimeUpdated(
    uint256 indexed assetMode,
    address indexed settlor,
    uint248 releaseTime,
    uint256 timestamp
); // This event will not be emitted when the asset is created or terminated
event FeePaid(
    uint256 indexed assetMode,
    address indexed settlor,
    address indexed token,
    uint256 amount,
    uint256 timestamp
); // Management fee on withdrawals paid
event InterestFeePaid(
    address indexed token,
    uint256 amount,
    uint256 financeContractBalance, // The balance of the financial contract before the interest fee charged
    uint256 timestamp
); // Service fee rate based on financial profit paid

uint256 constant PERCENTAGE_BASE = 1e18; // 100%
uint256 constant PERCENTAGE_SQUARE = 1e36; // PERCENTAGE_BASE squared
uint256 constant FEE_DIVISOR = 50; // Management fee on withdrawals, 2%
uint256 constant REMAIN_AFTER_FEE = 98e16; // 98%
uint248 constant SECONDS_PER_MONTH = 2592000; // The number of seconds in 30 days
uint256 constant SECONDS_LOCK_MAX = 1577923200; // Maximum locking seconds of pension (50 years, 60 * 60 * 24 * (365 * 50 + 13 leap days))
uint256 constant MONTHS_RELEASE_MAX = 360; // Maximum months of pension payment
uint256 constant LEGACY = 0;
uint256 constant TRUST = 1;
uint256 constant PENSION = 2;

interface IFinance {
    function balanceOf(address token) external view returns (uint256 balance);

    function deposit(address token, uint256 amount) external payable;

    function withdraw(address token, uint256 amount)
        external
        returns (uint256 withdrawAmount);

    function tokenGuardContract() external view returns (address);
}

library TokenGuardLib {
    function _getFinancedBalance(
        Store storage store,
        address token,
        address _financeContractAddress
    ) internal view returns (uint256 balance) {
        return
            store.isTokenFinancingPaused[token]
                ? store.pausingFinancedActualMap[token]
                : IFinance(_financeContractAddress).balanceOf(token);
    }

    function _getFinancedBalanceWithFlag(
        Store storage store,
        address token,
        address _financeContractAddress,
        bool notQueryFinancedBalance
    ) internal view returns (uint256 balance) {
        return
            store.isTokenFinancingPaused[token]
                ? store.pausingFinancedActualMap[token]
                : notQueryFinancedBalance
                ? 0
                : IFinance(_financeContractAddress).balanceOf(token);
    }

    function _getSumFinancedActualMapAll(
        Store storage store,
        address[] storage supportedTokens,
        bool notQueryFinancedBalance
    )
        external
        view
        returns (address[] memory tokens, uint256[] memory balances)
    {
        tokens = supportedTokens;
        balances = new uint256[](tokens.length);
        address _financeContractAddress = store.financeContractAddress;
        for (uint256 i = 0; i < tokens.length; i++)
            balances[i] = _getFinancedBalanceWithFlag(
                store,
                tokens[i],
                _financeContractAddress,
                notQueryFinancedBalance
            );
    }

    function _getFinancedTransform(
        Store storage store,
        address[] storage supportedTokens,
        bool notQueryFinancedBalance
    )
        external
        view
        returns (
            address[] memory tokens,
            uint256[] memory sumActualBalances,
            uint256[] memory sumRecordBalances,
            uint256[] memory sumInterestBases
        )
    {
        tokens = supportedTokens;
        sumActualBalances = new uint256[](tokens.length);
        sumRecordBalances = new uint256[](tokens.length);
        sumInterestBases = new uint256[](tokens.length);
        address _financeContractAddress = store.financeContractAddress;
        for (uint256 i = 0; i < tokens.length; i++) {
            sumActualBalances[i] = _getFinancedBalanceWithFlag(
                store,
                tokens[i],
                _financeContractAddress,
                notQueryFinancedBalance
            );
            sumRecordBalances[i] = store.sumFinancedRecordMap[tokens[i]];
            sumInterestBases[i] = store.sumInterestBaseMap[tokens[i]];
        }
    }

    function _getAsset(
        Store storage store,
        uint256 assetMode,
        address settlor,
        bool notQueryFinancedBalance
    ) internal view returns (BasicAsset memory basicAsset) {
        basicAsset.releaseTime = store.assets[assetMode][settlor].releaseTime;
        basicAsset.isFinanced = store.assets[assetMode][settlor].isFinanced;
        basicAsset.tokens = store.assets[assetMode][settlor].tokenSet.values();
        if (basicAsset.tokens.length > 0) {
            basicAsset.balances = new uint256[](basicAsset.tokens.length);
            if (basicAsset.isFinanced) {
                address _financeContractAddress = store.financeContractAddress;
                basicAsset.sumActualBalances = new uint256[](
                    basicAsset.tokens.length
                );
                basicAsset.sumRecordBalances = new uint256[](
                    basicAsset.tokens.length
                );
                basicAsset.sumInterestBases = new uint256[](
                    basicAsset.tokens.length
                );
                for (uint256 i = 0; i < basicAsset.tokens.length; i++) {
                    basicAsset.balances[i] = store
                    .assets[assetMode][settlor].balanceMap[
                            basicAsset.tokens[i]
                        ];
                    basicAsset.sumActualBalances[
                        i
                    ] = _getFinancedBalanceWithFlag(
                        store,
                        basicAsset.tokens[i],
                        _financeContractAddress,
                        notQueryFinancedBalance
                    );
                    basicAsset.sumRecordBalances[i] = store
                        .sumFinancedRecordMap[basicAsset.tokens[i]];
                    basicAsset.sumInterestBases[i] = store.sumInterestBaseMap[
                        basicAsset.tokens[i]
                    ];
                }
            } else
                for (uint256 i = 0; i < basicAsset.tokens.length; i++)
                    basicAsset.balances[i] = store
                    .assets[assetMode][settlor].balanceMap[
                            basicAsset.tokens[i]
                        ];
        }
    }

    function _getLegacyOrTrust(
        Store storage store,
        uint256 assetMode,
        address settlor,
        bool notQueryFinancedBalance
    ) internal view returns (LegacyOrTrust memory legacyOrTrust) {
        legacyOrTrust.basicAsset = _getAsset(
            store,
            assetMode,
            settlor,
            notQueryFinancedBalance
        );
        legacyOrTrust.beneficiaries = store
            .beneficiaryMaps[assetMode][settlor]
            .beneficiarySet
            .values();
        if (legacyOrTrust.beneficiaries.length > 0) {
            legacyOrTrust.percentages = new uint256[](
                legacyOrTrust.beneficiaries.length
            );
            for (uint256 i = 0; i < legacyOrTrust.beneficiaries.length; i++) {
                legacyOrTrust.percentages[i] = store
                .beneficiaryMaps[assetMode][settlor].percentMap[
                        legacyOrTrust.beneficiaries[i]
                    ];
            }
        }
    }

    function _getLegacyOrTrustExternal(
        Store storage store,
        uint256 assetMode,
        address settlor,
        bool notQueryFinancedBalance
    ) external view returns (LegacyOrTrust memory legacyOrTrust) {
        return
            _getLegacyOrTrust(
                store,
                assetMode,
                settlor,
                notQueryFinancedBalance
            );
    }

    function _getPension(
        Store storage store,
        address settlor,
        bool notQueryFinancedBalance
    ) external view returns (Pension memory pension) {
        pension.basicAsset = _getAsset(
            store,
            PENSION,
            settlor,
            notQueryFinancedBalance
        );
        pension.releaseMonths = store.pensionReleaseMonths[settlor];
    }

    function _getBenefitStatus(
        Store storage store,
        uint256 assetMode,
        address beneficiary,
        bool notQueryFinancedBalance
    ) public view returns (BenefitAsset memory benefitAsset) {
        benefitAsset.claimableTokens = store
            .benefitStatuses[assetMode][beneficiary]
            .claimableTokenSet
            .values();
        if (benefitAsset.claimableTokens.length > 0) {
            benefitAsset.claimableBalances = new uint256[](
                benefitAsset.claimableTokens.length
            );
            for (uint256 i = 0; i < benefitAsset.claimableTokens.length; i++) {
                benefitAsset.claimableBalances[i] = store
                .benefitStatuses[assetMode][beneficiary].claimableBalanceMap[
                        benefitAsset.claimableTokens[i]
                    ];
            }
        }
        benefitAsset.claimableFinancedTokens = store
            .benefitStatuses[assetMode][beneficiary]
            .claimableFinancedTokenSet
            .values();
        if (benefitAsset.claimableFinancedTokens.length > 0) {
            address _financeContractAddress = store.financeContractAddress;
            benefitAsset.claimableFinancedBalances = new uint256[](
                benefitAsset.claimableFinancedTokens.length
            );
            benefitAsset.sumActualBalances = new uint256[](
                benefitAsset.claimableFinancedTokens.length
            );
            benefitAsset.sumRecordBalances = new uint256[](
                benefitAsset.claimableFinancedTokens.length
            );
            benefitAsset.sumInterestBases = new uint256[](
                benefitAsset.claimableFinancedTokens.length
            );
            for (
                uint256 i = 0;
                i < benefitAsset.claimableFinancedTokens.length;
                i++
            ) {
                benefitAsset.claimableFinancedBalances[i] = store
                .benefitStatuses[assetMode][beneficiary]
                    .claimableFinancedBalanceMap[
                        benefitAsset.claimableFinancedTokens[i]
                    ];
                benefitAsset.sumActualBalances[i] = _getFinancedBalanceWithFlag(
                    store,
                    benefitAsset.claimableFinancedTokens[i],
                    _financeContractAddress,
                    notQueryFinancedBalance
                );
                benefitAsset.sumRecordBalances[i] = store.sumFinancedRecordMap[
                    benefitAsset.claimableFinancedTokens[i]
                ];
                benefitAsset.sumInterestBases[i] = store.sumInterestBaseMap[
                    benefitAsset.claimableFinancedTokens[i]
                ];
            }
        }
    }

    function _getAssetAndBenefit(
        Store storage store,
        uint256 assetMode,
        address settlorAndBeneficiary,
        bool notQueryFinancedBalance
    )
        external
        view
        returns (
            LegacyOrTrust memory legacyOrTrust,
            BenefitAsset memory benefitAsset,
            address[] memory executableSettlors,
            LegacyOrTrust[] memory executableAssets
        )
    {
        legacyOrTrust = _getLegacyOrTrust(
            store,
            assetMode,
            settlorAndBeneficiary,
            notQueryFinancedBalance
        );
        benefitAsset = _getBenefitStatus(
            store,
            assetMode,
            settlorAndBeneficiary,
            notQueryFinancedBalance
        );
        address[] memory effectiveSettlors = store
            .benefitStatuses[assetMode][settlorAndBeneficiary]
            .effectiveSettlors
            .values();
        uint256[] memory indexes = new uint256[](effectiveSettlors.length);
        uint256 executableNum;
        for (uint256 i = 0; i < effectiveSettlors.length; i++) {
            if (
                block.timestamp >=
                store.assets[assetMode][effectiveSettlors[i]].releaseTime
            ) {
                indexes[executableNum] = i;
                executableNum++;
            }
        }
        if (executableNum > 0) {
            executableSettlors = new address[](executableNum);
            executableAssets = new LegacyOrTrust[](executableNum);
            for (uint256 i = 0; i < executableNum; i++) {
                executableSettlors[i] = effectiveSettlors[indexes[i]];
                executableAssets[i] = _getLegacyOrTrust(
                    store,
                    assetMode,
                    effectiveSettlors[indexes[i]],
                    notQueryFinancedBalance
                );
            }
        }
    }

    function _transfer(
        address receiver,
        address token,
        uint256 amount
    ) internal {
        if (token == address(0)) payable(receiver).transfer(amount);
        else IERC20(token).safeTransfer(receiver, amount);
    }

    function _payFee(
        uint256 fee,
        address token,
        address _receiver1,
        address _receiver2
    ) internal {
        uint256 fee1 = (fee * 85) / 100;
        if (token == address(0)) {
            payable(_receiver1).transfer(fee1);
            payable(_receiver2).transfer(fee - fee1);
        } else {
            IERC20(token).safeTransfer(_receiver1, fee1);
            IERC20(token).safeTransfer(_receiver2, fee - fee1);
        }
    }

    function _payInterestFee(
        uint256 fee,
        address token,
        address _receiver1,
        address _receiver2,
        uint256 financeContractBalance
    ) internal {
        _payFee(fee, token, _receiver1, _receiver2);
        emit InterestFeePaid(
            token,
            fee,
            financeContractBalance,
            block.timestamp
        );
    }

    function _payWithdrawFee(
        uint256 fee,
        address token,
        address _receiver1,
        address _receiver2,
        address settlor,
        uint256 assetMode
    ) internal {
        _payFee(fee, token, _receiver1, _receiver2);
        emit FeePaid(assetMode, settlor, token, fee, block.timestamp);
    }

    function _payInterestFeeAndWithdrawFee(
        uint256 interestFee,
        uint256 withdrawFee,
        address token,
        address _receiver1,
        address _receiver2,
        address settlor,
        uint256 assetMode,
        uint256 financeContractBalance
    ) internal {
        _payFee(interestFee + withdrawFee, token, _receiver1, _receiver2);
        if (interestFee > 0)
            emit InterestFeePaid(
                token,
                interestFee,
                financeContractBalance,
                block.timestamp
            );
        if (withdrawFee > 0)
            emit FeePaid(
                assetMode,
                settlor,
                token,
                withdrawFee,
                block.timestamp
            );
    }

    function _calculateInterest(
        Store storage store,
        address token,
        address _financeContractAddress,
        uint256 _interestFeeRate
    )
        internal
        view
        returns (
            uint256 sumFinancedActual,
            uint256 sumInterestBase,
            uint256 fee
        )
    {
        sumInterestBase = store.sumInterestBaseMap[token];
        if (sumInterestBase > 0) {
            sumFinancedActual = _getFinancedBalance(
                store,
                token,
                _financeContractAddress
            );
            if (sumFinancedActual > sumInterestBase)
                fee =
                    ((sumFinancedActual - sumInterestBase) * _interestFeeRate) /
                    PERCENTAGE_BASE;
        }
    }

    function _financeVars(
        Store storage store,
        uint256 assetMode,
        address token,
        address settlor,
        address _financeContractAddress,
        uint256 _interestFeeRate
    )
        internal
        view
        returns (
            Vars memory vars,
            uint256 sumInterestBase,
            uint256 fee
        )
    {
        (vars.sumActual, sumInterestBase, fee) = _calculateInterest(
            store,
            token,
            _financeContractAddress,
            _interestFeeRate
        );
        vars.sumRecord = store.sumFinancedRecordMap[token];
        vars.balance = store.assets[assetMode][settlor].balanceMap[token];
    }

    function _depositToFinanceContract(
        address token,
        uint256 amount,
        address _financeContractAddress
    ) internal {
        if (token == address(0))
            IFinance(_financeContractAddress).deposit{value: amount}(
                address(0),
                amount
            );
        else {
            IERC20(token).forceApprove(_financeContractAddress, amount);
            IFinance(_financeContractAddress).deposit(token, amount);
        }
    }

    function _withdrawFromFinanceContract(
        address token,
        uint256 amount,
        address _financeContractAddress
    ) internal returns (uint256 withdrawAmount) {
        uint256 oldBalance = token == address(0)
            ? address(this).balance
            : IERC20(token).balanceOf(address(this));
        withdrawAmount = IFinance(_financeContractAddress).withdraw(
            token,
            amount
        );
        uint256 newBalance = token == address(0)
            ? address(this).balance
            : IERC20(token).balanceOf(address(this));
        if (newBalance < oldBalance) revert VerificationFailed();
        if (newBalance - oldBalance < withdrawAmount)
            withdrawAmount = newBalance - oldBalance;
    }

    function _processFinanceAsset(
        Store storage store,
        uint256 oldBalance,
        uint256 newBalance,
        uint256 assetMode,
        address settlor,
        address token
    ) internal {
        if (newBalance != oldBalance) {
            if (newBalance == 0) {
                store.assets[assetMode][settlor].tokenSet.remove(token);
                store.financedTokenSettlors[assetMode][token].remove(settlor);
            } else if (oldBalance == 0) {
                store.assets[assetMode][settlor].tokenSet.add(token);
                store.financedTokenSettlors[assetMode][token].add(settlor);
            }
            store.assets[assetMode][settlor].balanceMap[token] = newBalance;
        }
    }

    function _processFinanceSum(
        Store storage store,
        Vars memory varsOld,
        Vars memory varsNew,
        uint256 sumInterestBase,
        address token,
        address _financeContractAddress
    ) internal returns (uint256 gap) {
        if (varsNew.sumRecord != varsOld.sumRecord)
            store.sumFinancedRecordMap[token] = varsNew.sumRecord;
        if (varsNew.sumActual != sumInterestBase)
            store.sumInterestBaseMap[token] = varsNew.sumActual;
        if (store.isTokenFinancingPaused[token]) {
            if (varsNew.sumActual != varsOld.sumActual)
                store.pausingFinancedActualMap[token] = varsNew.sumActual;
        } else if (varsNew.sumActual < varsOld.sumActual) {
            uint256 amount = varsOld.sumActual - varsNew.sumActual;
            gap =
                amount -
                _withdrawFromFinanceContract(
                    token,
                    amount,
                    _financeContractAddress
                );
        } else if (varsNew.sumActual > varsOld.sumActual)
            _depositToFinanceContract(
                token,
                varsNew.sumActual - varsOld.sumActual,
                _financeContractAddress
            );
    }

    function _processFinance(
        Store storage store,
        Vars memory varsOld,
        Vars memory varsNew,
        uint256 sumInterestBase,
        uint256 assetMode,
        address settlor,
        address token,
        address _financeContractAddress
    ) internal returns (uint256 gap) {
        _processFinanceAsset(
            store,
            varsOld.balance,
            varsNew.balance,
            assetMode,
            settlor,
            token
        );
        return
            _processFinanceSum(
                store,
                varsOld,
                varsNew,
                sumInterestBase,
                token,
                _financeContractAddress
            );
    }

    function _collectInterestFee(Store storage store, address[] calldata tokens)
        external
    {
        uint256 _interestFeeRate = store.interestFeeRate;
        address _financeContractAddress = store.financeContractAddress;
        address _receiver1 = store.receiver1;
        address _receiver2 = store.receiver2;
        for (uint256 i = 0; i < tokens.length; i++) {
            (
                uint256 sumFinancedActual,
                uint256 sumInterestBase,
                uint256 fee
            ) = _calculateInterest(
                    store,
                    tokens[i],
                    _financeContractAddress,
                    _interestFeeRate
                );
            if (fee > 0) {
                if (store.isTokenFinancingPaused[tokens[i]])
                    store.pausingFinancedActualMap[tokens[i]] =
                        sumFinancedActual -
                        fee;
                else
                    fee = _withdrawFromFinanceContract(
                        tokens[i],
                        fee,
                        _financeContractAddress
                    );
                if (fee > 0) {
                    store.sumInterestBaseMap[tokens[i]] =
                        sumFinancedActual -
                        fee;
                    _payInterestFee(
                        fee,
                        tokens[i],
                        _receiver1,
                        _receiver2,
                        sumFinancedActual
                    );
                }
            } else if (sumFinancedActual < sumInterestBase)
                store.sumInterestBaseMap[tokens[i]] = sumFinancedActual;
        }
    }

    function _cleanFinancedToken(Store storage store, address[] calldata tokens)
        external
    {
        address _financeContractAddress = store.financeContractAddress;
        for (uint256 i = 0; i < tokens.length; i++)
            if (
                _getFinancedBalance(
                    store,
                    tokens[i],
                    _financeContractAddress
                ) == 0
            ) {
                delete store.sumFinancedRecordMap[tokens[i]];
                delete store.sumInterestBaseMap[tokens[i]];
                for (uint256 assetMode = 0; assetMode < 3; assetMode++) {
                    for (
                        uint256 j = store
                        .financedTokenSettlors[assetMode][tokens[i]].length();
                        j > 0;
                        j--
                    ) {
                        address settlor = store
                        .financedTokenSettlors[assetMode][tokens[i]].at(j - 1);
                        delete store.assets[assetMode][settlor].balanceMap[
                            tokens[i]
                        ];
                        store.assets[assetMode][settlor].tokenSet.remove(
                            tokens[i]
                        );
                        store
                        .financedTokenSettlors[assetMode][tokens[i]].remove(
                                settlor
                            );
                    }
                    if (assetMode < 2)
                        for (
                            uint256 j = store
                            .financedTokenBeneficiaries[assetMode][tokens[i]]
                                .length();
                            j > 0;
                            j--
                        ) {
                            address beneficiary = store
                            .financedTokenBeneficiaries[assetMode][tokens[i]]
                                .at(j - 1);
                            delete store
                            .benefitStatuses[assetMode][beneficiary]
                                .claimableFinancedBalanceMap[tokens[i]];
                            store
                                .benefitStatuses[assetMode][beneficiary]
                                .claimableFinancedTokenSet
                                .remove(tokens[i]);
                            store
                            .financedTokenBeneficiaries[assetMode][tokens[i]]
                                .remove(beneficiary);
                        }
                }
            }
    }

    function _withdraw(
        Store storage store,
        address token,
        uint256 amount,
        uint256 assetMode
    ) external {
        if (!store.isTokenSupported[token]) revert TokenNotSupported();
        if (store.assets[assetMode][msg.sender].isFinanced) {
            address _financeContractAddress = store.financeContractAddress;
            (
                Vars memory varsOld,
                uint256 sumInterestBase,
                uint256 interestFee
            ) = _financeVars(
                    store,
                    assetMode,
                    token,
                    msg.sender,
                    _financeContractAddress,
                    store.interestFeeRate
                );
            uint256 sumActualNew = varsOld.sumActual - interestFee;
            uint256 record = sumInterestBase > 0
                ? (amount * varsOld.sumRecord) / sumActualNew
                : amount;
            if (record > varsOld.balance) record = varsOld.balance;
            amount = record > 0
                ? (record * sumActualNew) / varsOld.sumRecord
                : 0;
            amount -= _processFinance(
                store,
                varsOld,
                Vars(
                    sumActualNew - amount,
                    varsOld.sumRecord - record,
                    varsOld.balance - record
                ),
                sumInterestBase,
                assetMode,
                msg.sender,
                token,
                _financeContractAddress
            );
            uint256 withdrawFee;
            if (amount > 0) {
                withdrawFee = amount / FEE_DIVISOR;
                _transfer(msg.sender, token, amount - withdrawFee);
            }
            if (interestFee + withdrawFee > 0)
                _payInterestFeeAndWithdrawFee(
                    interestFee,
                    withdrawFee,
                    token,
                    store.receiver1,
                    store.receiver2,
                    msg.sender,
                    assetMode,
                    varsOld.sumActual
                );
        } else {
            uint256 balance = store.assets[assetMode][msg.sender].balanceMap[
                token
            ];
            amount = amount < balance ? amount : balance;
            if (amount > 0) {
                store.assets[assetMode][msg.sender].balanceMap[token] =
                    balance -
                    amount;
                if (amount == balance)
                    store.assets[assetMode][msg.sender].tokenSet.remove(token);
                uint256 withdrawFee = amount / FEE_DIVISOR;
                _transfer(msg.sender, token, amount - withdrawFee);
                if (withdrawFee > 0)
                    _payWithdrawFee(
                        withdrawFee,
                        token,
                        store.receiver1,
                        store.receiver2,
                        msg.sender,
                        assetMode
                    );
            }
        }
        emit AssetOperation(
            assetMode,
            msg.sender,
            OperationType.Withdraw,
            token,
            amount,
            block.timestamp
        );
    }

    function _trySetFinancedContractAddress(
        Store storage store,
        address[] storage supportedTokens,
        address newFinanceContractAddress
    ) external {
        address oldFinanceContractAddress = store.financeContractAddress;
        store.financeContractAddress = newFinanceContractAddress;
        uint256 tokenSetLength = supportedTokens.length;
        for (uint256 i = 0; i < tokenSetLength; i++) {
            address token = supportedTokens[i];
            if (
                !store.isTokenFinancingPaused[token] &&
                store.sumFinancedRecordMap[token] > 0
            ) {
                try
                    IFinance(oldFinanceContractAddress).balanceOf(token)
                returns (uint256 balance) {
                    if (balance > 0) {
                        uint256 oldBalance = token == address(0)
                            ? address(this).balance
                            : IERC20(token).balanceOf(address(this));
                        try
                            IFinance(oldFinanceContractAddress).withdraw(
                                token,
                                balance
                            )
                        returns (uint256 amount) {
                            uint256 newBalance = token == address(0)
                                ? address(this).balance
                                : IERC20(token).balanceOf(address(this));
                            if (newBalance > oldBalance && amount > 0) {
                                if (newBalance - oldBalance < amount)
                                    amount = newBalance - oldBalance;
                                _depositToFinanceContract(
                                    token,
                                    amount,
                                    newFinanceContractAddress
                                );
                            }
                        } catch {}
                    }
                } catch {}
            }
        }
    }

    function _pauseTokenFinancing(
        Store storage store,
        address[] calldata tokens
    ) external {
        address _financeContractAddress = store.financeContractAddress;
        for (uint256 i = 0; i < tokens.length; i++) {
            store.isTokenFinancingPaused[tokens[i]] = true;
            if (store.sumFinancedRecordMap[tokens[i]] > 0) {
                uint256 balance = IFinance(_financeContractAddress).balanceOf(
                    tokens[i]
                );
                if (balance > 0) {
                    balance = _withdrawFromFinanceContract(
                        tokens[i],
                        balance,
                        _financeContractAddress
                    );
                    if (balance > 0)
                        store.pausingFinancedActualMap[tokens[i]] = balance;
                }
            }
        }
    }

    function _tryPauseTokenFinancing(
        Store storage store,
        address token,
        uint256 amount
    ) external {
        store.isTokenFinancingPaused[token] = true;
        if (store.sumFinancedRecordMap[token] > 0) {
            address _financeContractAddress = store.financeContractAddress;
            try IFinance(_financeContractAddress).balanceOf(token) returns (
                uint256 balance
            ) {
                if (balance > 0) {
                    uint256 oldBalance = token == address(0)
                        ? address(this).balance
                        : IERC20(token).balanceOf(address(this));
                    try
                        IFinance(_financeContractAddress).withdraw(
                            token,
                            balance
                        )
                    returns (uint256 withdrawAmount) {
                        uint256 newBalance = token == address(0)
                            ? address(this).balance
                            : IERC20(token).balanceOf(address(this));
                        if (newBalance < oldBalance)
                            revert VerificationFailed();
                        if (newBalance - oldBalance < withdrawAmount)
                            withdrawAmount = newBalance - oldBalance;
                        if (withdrawAmount > 0)
                            store.pausingFinancedActualMap[
                                token
                            ] = withdrawAmount;
                    } catch {
                        if (amount > 0) {
                            amount = _withdrawFromFinanceContract(
                                token,
                                amount,
                                _financeContractAddress
                            );
                            if (amount > 0)
                                store.pausingFinancedActualMap[token] = amount;
                        }
                    }
                }
            } catch {
                if (amount > 0) {
                    amount = _withdrawFromFinanceContract(
                        token,
                        amount,
                        _financeContractAddress
                    );
                    if (amount > 0)
                        store.pausingFinancedActualMap[token] = amount;
                }
            }
        }
    }

    function _executeLegacy(Store storage store, address settlor) external {
        delete store.assets[LEGACY][settlor].releaseTime;
        BeneficiaryPercent[]
            memory beneficiaryPercents = new BeneficiaryPercent[](
                store.beneficiaryMaps[LEGACY][settlor].beneficiarySet.length()
            );
        for (uint256 i = 0; i < beneficiaryPercents.length; i++) {
            beneficiaryPercents[i].beneficiary = store
                .beneficiaryMaps[LEGACY][settlor]
                .beneficiarySet
                .at(i);
            beneficiaryPercents[i].percent =
                store.beneficiaryMaps[LEGACY][settlor].percentMap[
                    beneficiaryPercents[i].beneficiary
                ] *
                REMAIN_AFTER_FEE;
            store
                .benefitStatuses[LEGACY][beneficiaryPercents[i].beneficiary]
                .effectiveSettlors
                .remove(settlor);
        }
        if (store.assets[LEGACY][settlor].isFinanced) {
            FinParam memory finParam = FinParam(
                store.interestFeeRate,
                store.financeContractAddress,
                store.receiver1,
                store.receiver2
            );
            for (
                uint256 i = store.assets[LEGACY][settlor].tokenSet.length();
                i > 0;
                i--
            ) {
                TmpVars1 memory _tmp;
                _tmp.token = store.assets[LEGACY][settlor].tokenSet.at(i - 1);
                Vars memory varsOld;
                (
                    varsOld,
                    _tmp.sumInterestBase,
                    _tmp.interestFee
                ) = _financeVars(
                    store,
                    LEGACY,
                    _tmp.token,
                    settlor,
                    finParam.financeContractAddress,
                    finParam.interestFeeRate
                );
                _tmp.feeRecord = varsOld.balance;
                for (uint256 j = 0; j < beneficiaryPercents.length; j++) {
                    uint256 benefitAmount = (varsOld.balance *
                        beneficiaryPercents[j].percent) / PERCENTAGE_SQUARE;
                    if (benefitAmount > 0) {
                        _tmp.feeRecord -= benefitAmount;
                        uint256 claimableBalance = store
                        .benefitStatuses[LEGACY][
                            beneficiaryPercents[j].beneficiary
                        ].claimableFinancedBalanceMap[_tmp.token];
                        if (claimableBalance == 0) {
                            store
                                .benefitStatuses[LEGACY][
                                    beneficiaryPercents[j].beneficiary
                                ]
                                .claimableFinancedTokenSet
                                .add(_tmp.token);
                            store
                            .financedTokenBeneficiaries[LEGACY][_tmp.token].add(
                                    beneficiaryPercents[j].beneficiary
                                );
                        }
                        store
                        .benefitStatuses[LEGACY][
                            beneficiaryPercents[j].beneficiary
                        ].claimableFinancedBalanceMap[_tmp.token] =
                            claimableBalance +
                            benefitAmount;
                    }
                }
                store.assets[LEGACY][settlor].tokenSet.remove(_tmp.token);
                store.financedTokenSettlors[LEGACY][_tmp.token].remove(settlor);
                delete store.assets[LEGACY][settlor].balanceMap[_tmp.token];
                _tmp.withdrawFee =
                    (_tmp.feeRecord * (varsOld.sumActual - _tmp.interestFee)) /
                    varsOld.sumRecord;
                _tmp.withdrawFee -= _processFinanceSum(
                    store,
                    varsOld,
                    Vars(
                        varsOld.sumActual - _tmp.interestFee - _tmp.withdrawFee,
                        varsOld.sumRecord - _tmp.feeRecord,
                        0
                    ),
                    _tmp.sumInterestBase,
                    _tmp.token,
                    finParam.financeContractAddress
                );
                if (_tmp.interestFee + _tmp.withdrawFee > 0)
                    _payInterestFeeAndWithdrawFee(
                        _tmp.interestFee,
                        _tmp.withdrawFee,
                        _tmp.token,
                        finParam.receiver1,
                        finParam.receiver2,
                        settlor,
                        LEGACY,
                        varsOld.sumActual
                    );
                emit AssetOperation(
                    LEGACY,
                    settlor,
                    OperationType.Execute,
                    _tmp.token,
                    (varsOld.balance * (varsOld.sumActual - _tmp.interestFee)) /
                        varsOld.sumRecord,
                    block.timestamp
                );
            }
            delete store.assets[LEGACY][settlor].isFinanced;
            emit FinanceChanged(LEGACY, settlor, false, block.timestamp);
        } else {
            address _receiver1 = store.receiver1;
            address _receiver2 = store.receiver2;
            for (
                uint256 i = store.assets[LEGACY][settlor].tokenSet.length();
                i > 0;
                i--
            ) {
                address token = store.assets[LEGACY][settlor].tokenSet.at(
                    i - 1
                );
                uint256 balance = store.assets[LEGACY][settlor].balanceMap[
                    token
                ];
                uint256 withdrawFee = balance;
                for (uint256 j = 0; j < beneficiaryPercents.length; j++) {
                    uint256 benefitAmount = (balance *
                        beneficiaryPercents[j].percent) / PERCENTAGE_SQUARE;
                    if (benefitAmount > 0) {
                        withdrawFee -= benefitAmount;
                        uint256 claimableBalance = store
                        .benefitStatuses[LEGACY][
                            beneficiaryPercents[j].beneficiary
                        ].claimableBalanceMap[token];
                        if (claimableBalance == 0)
                            store
                                .benefitStatuses[LEGACY][
                                    beneficiaryPercents[j].beneficiary
                                ]
                                .claimableTokenSet
                                .add(token);
                        store
                        .benefitStatuses[LEGACY][
                            beneficiaryPercents[j].beneficiary
                        ].claimableBalanceMap[token] =
                            claimableBalance +
                            benefitAmount;
                    }
                }
                store.assets[LEGACY][settlor].tokenSet.remove(token);
                delete store.assets[LEGACY][settlor].balanceMap[token];
                if (withdrawFee > 0)
                    _payWithdrawFee(
                        withdrawFee,
                        token,
                        _receiver1,
                        _receiver2,
                        settlor,
                        LEGACY
                    );
                emit AssetOperation(
                    LEGACY,
                    settlor,
                    OperationType.Execute,
                    token,
                    balance,
                    block.timestamp
                );
            }
        }
        emit AssetTerminated(LEGACY, settlor, block.timestamp);
    }
}

contract TokenGuard is ReentrancyGuard {
    address private owner;
    address private riskController;
    address[] private supportedTokens = [
        address(0), // ETH
        address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599), // WBTC
        address(0xdAC17F958D2ee523a2206206994597C13D831ec7), // USDT
        address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) // USDC
    ];
    Store private store;

    constructor(
        address _receiver1,
        address _receiver2,
        address _riskController,
        address _financeContractAddress
    ) {
        store.interestFeeRate = 1e17; // Service fee rate based on financial profit, 1e18 = 100%, initialized to 10%
        store.financeContractAddress = _financeContractAddress;
        store.receiver1 = _receiver1;
        store.receiver2 = _receiver2;
        owner = msg.sender;
        riskController = _riskController;
        uint256 tokenLength = supportedTokens.length;
        for (uint256 i = 0; i < tokenLength; i++)
            store.isTokenSupported[supportedTokens[i]] = true;
        store.isTokenFinancingPaused[
            address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599)
        ] = true; // WBTC
    }

    function interestFeeRate()
        external
        view
        returns (uint256 _interestFeeRate)
    {
        return store.interestFeeRate;
    }

    function financeContractAddress()
        external
        view
        returns (address _financeContractAddress)
    {
        return store.financeContractAddress;
    }

    function getSupportedTokens()
        external
        view
        returns (address[] memory tokens)
    {
        return supportedTokens;
    }

    function getSupportedTokensAndIsFinancingPaused()
        external
        view
        returns (address[] memory tokens, bool[] memory isFinancingPaused)
    {
        tokens = supportedTokens;
        isFinancingPaused = new bool[](tokens.length);
        for (uint256 i = 0; i < tokens.length; i++)
            isFinancingPaused[i] = store.isTokenFinancingPaused[tokens[i]];
    }

    function getFinancedTokenSettlorsAndBeneficiaries(
        uint256 assetMode,
        address token
    )
        external
        view
        returns (address[] memory settlors, address[] memory beneficiaries)
    {
        return (
            store.financedTokenSettlors[assetMode][token].values(),
            store.financedTokenBeneficiaries[assetMode][token].values()
        );
    }

    function getSumFinancedActualMapAll(bool notQueryFinancedBalance)
        external
        view
        returns (address[] memory tokens, uint256[] memory balances)
    {
        return
            TokenGuardLib._getSumFinancedActualMapAll(
                store,
                supportedTokens,
                notQueryFinancedBalance
            );
    }

    function getFinancedTransform(bool notQueryFinancedBalance)
        external
        view
        returns (
            address[] memory tokens,
            uint256[] memory sumActualBalances,
            uint256[] memory sumRecordBalances,
            uint256[] memory sumInterestBases
        )
    {
        return
            TokenGuardLib._getFinancedTransform(
                store,
                support...

// [truncated — 95566 bytes total]

Read Contract

financeContractAddress 0x251e24dd → address
getAssetAndBenefit 0xa689c983 → tuple, tuple, address[], tuple[]
getBenefitStatus 0xe37a375f → tuple, address[]
getFinancedTokenSettlorsAndBeneficiaries 0xcb35a0cf → address[], address[]
getFinancedTransform 0x9157cfe9 → address[], uint256[], uint256[], uint256[]
getLegacyOrTrust 0xc651e019 → tuple
getPension 0x7be933cc → tuple
getSumFinancedActualMapAll 0xaf6f1e17 → address[], uint256[]
getSupportedTokens 0xd3c7c2c7 → address[]
getSupportedTokensAndIsFinancingPaused 0x9c37f8a0 → address[], bool[]
interestFeeRate 0xdd86fea1 → uint256

Write Contract 32 functions

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

addSupportedTokens 0x67b8142a
address[] tokens
bool pauseFinancing
changeReceiver1 0x8746ac5f
address newReceiver1
changeReceiver2 0xb55c72d9
address newReceiver2
changeRiskController 0xedb0271c
address newRiskController
claimExecuted 0xb29b189f
uint256 assetMode
claimLegacy 0x08e6b41c
No parameters
claimTrust 0xa45744a3
No parameters
cleanFinancedToken 0x3ac846ff
address[] tokens
collectInterestFee 0x7bc52b60
address[] tokens
createLegacy 0x17061508
uint248 releaseTime
bool isFinanced
tuple[] beneficiaryPercents
createPension 0x1738036a
uint248 releaseTime
bool isFinanced
uint256 releaseMonths
createTrust 0x5aa95401
uint248 releaseTime
bool isFinanced
tuple[] beneficiaryPercents
deposit 0xbc157ac1
uint256 assetMode
address token
uint256 amount
executeLegacy 0xa5589036
address settlor
executePension 0xb4c7b78d
No parameters
executeTrust 0x549660c4
address settlor
loopExecutePension 0x7d5cee1e
No parameters
loopExecuteTrust 0xae26299b
address settlor
pauseTokenFinancing 0x85d71e20
address[] tokens
resumeTokenFinancing 0x575ecf0b
address[] tokens
setFinance 0xa83d226a
uint256 assetMode
bool isFinanced
setFinancedContractAddress 0xcdab1ced
address newFinanceContractAddress
setInterestFeeRate 0x7d379a48
uint256 _interestFeeRate
setLegacyBeneficiary 0x2afead0f
tuple[] beneficiaryPercents
setLegacyReleaseTime 0x6063a82c
uint248 releaseTime
setTrustBeneficiary 0xead3fa6d
tuple[] beneficiaryPercents
setTrustReleaseTime 0x246fb69f
uint248 releaseTime
transferOwnership 0xf2fde38b
address newOwner
tryPauseTokenFinancing 0x836644f0
address token
uint256 amount
trySetFinancedContractAddress 0x4cafa298
address newFinanceContractAddress
withdrawLegacy 0xce1bd03f
address token
uint256 amount
withdrawTrust 0xd4acc760
address token
uint256 amount

Recent Transactions

No transactions found for this address