Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x5F19431BC8A3eb21222771c6C867a63a119DeDA7
Balance 0 ETH
Nonce 1
Code Size 23541 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23541 bytes
0x5f3560e01c6002601f820660011b615a9701601e395f51565b638da5cb5b81186100335734615a93575f5460405260206040f35b6361acb5598118613d0857602436103417615a935760076004356020525f5260405f205460405260206040f3613d08565b63885753de81186100805734615a935760015460405260206040f35b63ffa1ad748118613d085734615a935760208060805260176040527f5032504c656e64696e674e6674732e323032343130303200000000000000000060605260408160800181518152602082015160208201528051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f3613d08565b63331c658781186101205734615a93576020615ad560403960206040f35b6305107f058118613d085761022436103417615a93576004356004018035610c60526020810135610c80526040810135610ca0526060810135610cc0526080810135610ce05260a08101358060a01c615a9357610d005260c0810135610d205260e0810135610d40526101008101358060a01c615a9357610d60526101208101358060a01c615a9357610d80526101408101358060a01c615a9357610da052610160810135610dc05261018081013581016004813511615a935780355f8160048111615a9357801561023e57905b8060071b610e00018160071b602086010180358060041c615a93578252602081013560208301526040810135604083015260608101358060a01c615a9357606083015250506001018181186101ee575b505080610de05250506101a08101358060011c615a9357611000526101c08101358060a01c615a935761102052506103e06108606103e0610c6060045afa50610288611040615308565b611040516102f557600c611060527f696e76616c6964206c6f616e00000000000000000000000000000000000000006110805261106050611060518061108001601f825f031636823750506308c379a061102052602061104052601f19601f61106051011660440161103cfd5b610d20514211610364576012611040527f6c6f616e206e6f742064656661756c74656400000000000000000000000000006110605261104050611040518061106001601f825f031636823750506308c379a061100052602061102052601f19601f61104051011660440161101cfd5b610d805160405261037661104061533d565b611040516103e357600a611060527f6e6f74206c656e646572000000000000000000000000000000000000000000006110805261106050611060518061108001601f825f031636823750506308c379a061102052602061104052601f19601f61106051011660440161103cfd5b5f6002610c60516020525f5260405f2055610d805161022052610da05161024052610dc0516102605261041461588c565b611020511561043e5761102051604052610da051606052610dc0516080525f60a05261043e6152b1565b7f4a55c2facef4f87dd498aace39997457e1fc66968956bfe3ac7f20f736e77e74610c605161104052610d605161106052610d805161108052610da0516110a052610dc0516110c05260a0611040a100613d08565b63c4a9081581186104c057602436103417615a935760026004356020525f5260405f205460405260206040f35b639544fa7c8118613d0857604436103417615a93576004358060a01c615a93576040526024358060011c615a93576060525f543318156105565760096080527f6e6f74206f776e6572000000000000000000000000000000000000000000000060a0526080506080518060a001601f825f031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b60605160086040516020525f5260405f20557f5e09e79221e29a6d762b708b6fa399e730dfc027f946b2e81a31d849b2fb1fa960405160805260605160a05260406080a100613d08565b63e5107ed781186105be5734615a93576020615af560403960206040f35b63c2a5022b8118613d0857604436103417615a93575f543318156106375760096040527f6e6f74206f776e6572000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b6020615b555f395f5160043511156106a45760176040527f757066726f6e74206665652065786365656473206d617800000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b6020615b755f395f51602435111561071157601a6040527f736574746c656d656e74206665652065786365656473206d617800000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b7f4bfb2e8697c06dccd3777bdd9b4dab854905ca53b2a56bb47882c744729729d46004546040526005546060526040600460803760806040a160043560045560243560055500613d08565b635556eda98118613d085734615a93576020615b1560403960206040f3613d08565b637719e9ea811861079c5734615a93576020615b3560403960206040f35b639274bbcd8118613d0857602436103417615a93576004358060a01c615a935760405260096040516020525f5260405f205460605260206060f3613d08565b6367db749981186107f75734615a935760035460405260206040f35b63f8c266298118613d085734615a93576020615b9560403960206040f3613d08565b6358e7783781186108355734615a935760045460405260206040f35b63f7a79a028118613d085734615a93576001543318156108aa5760166040527f6e6f74207468652070726f706f736564206f776e65720000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f5460405260015460605260406040a1335f555f60015500613d08565b6361dd0b8b8118613d085734615a935760055460405260206040f3613d08565b6350d0d5ad8118613d085734615a93576020615b5560403960206040f3613d08565b6387c8f69481186109485734615a93576020615b7560403960206040f35b63a1c7a5678118613d08576103a436103417615a935760406004610860376044358060a01c615a93576108a0526064356108c052606060846108e03760e4358060a01c615a935761094052610104358060031c615a935761096052610124356109805260a06101446109a0376101e4358060a01c615a9357610a4052610204358060011c615a9357610a60526040610224610a80376060610264610ac0376102e4356004016020813511615a9357803560208160051b018083610b2037505050610304358060a01c615a9357610f4052610364358060a01c615a9357610f60526102c060406102c061086060045afa50610a405161030052610a4b610f80613d0c565b610f8051610ab857601a610fa0527f6f66666572206e6f74207369676e6564206279206c656e646572000000000000610fc052610fa050610fa05180610fc001601f825f031636823750506308c379a0610f60526020610f8052601f19601f610fa0510116604401610f7cfd5b42610a205111610b2757600d610f80527f6f66666572206578706972656400000000000000000000000000000000000000610fa052610f8050610f805180610fa001601f825f031636823750506308c379a0610f40526020610f6052601f19601f610f80510116604401610f5cfd5b6020615ad55f395f516108a0511815610b9f576015610f80527f696e76616c6964207061796d656e7420746f6b656e0000000000000000000000610fa052610f8050610f805180610fa001601f825f031636823750506308c379a0610f40526020610f6052601f19601f610f80510116604401610f5cfd5b610860516108e0511115610c1257601c610f80527f6f726967696e6174696f6e20666565206774207072696e636970616c00000000610fa052610f8050610f805180610fa001601f825f031636823750506308c379a0610f40526020610f6052601f19601f610f80510116604401610f5cfd5b6020615b355f395f516356108f8a610fc0526109e051610fe0526040610fc06024610fdc5f855af1610c46573d5f5f3e3d5ffd5b60403d10615a9357610fc0518060a01c615a935761102052610fe0516110405261102090508051610f80526020810151610fa05250610260604061026061086060045afa506102c4356102a052610f80516102c052610fa0516102e052610b205160208160051b018061030082610b2060045afa505050610cc5613e1f565b610260604061026061086060045afa5060406103246102a037610f60516102e052610cf16111e0614104565b6111e0805160208160071b0180610fc0828560045afa505050505f6111e0525f610fc05160048111615a93578015610d7457905b8060071b610fe001805161120052602081015161122052604081015161124052606081015161126052506111e05161122051808201828110615a9357905090506111e052600101818118610d25575b50506102c060406102c061086060045afa50610d9161122061452b565b61122051611200525f611220526112005161124052610aa051611260526108605161128052610880516112a0526108a0516112c052426108c051808201828110615a9357905090506112e05242611300526008336020525f5260405f2054610df95733610dfb565b325b61132052610a405161134052610f8051611360526102c43561138052610fc05160208160071b01806113a082610fc060045afa505050610a60516115c052610f40516115e0526103e060406103e061122060045afa50610e5c611600614574565b61160051611220526002611220516020525f5260405f205415610ede576013611600527f6c6f616e20616c726561647920657869737473000000000000000000000000006116205261160050611600518061162001601f825f031636823750506308c379a06115c05260206115e052601f19601f6116005101166044016115dcfd5b6102c06103a06102c061086060045afa50610ef7614636565b6103e060406103e061122060045afa50610f126116006147a6565b611600516002611220516020525f5260405f2055611320516102a052611360516102c052611380516102e052610f46614e87565b6113405160405261132051606052611280516111e051808203828111615a93579050905061090051808201828110615a935790509050608052610f876151ed565b5f610fc05160048111615a9357801561100b57905b8060071b610fe0018051611600526020810151611620526040810151611640526060810151611660525060026116005114610fdc57611620511515610fde565b5f5b15611000576113405160405261166051606052611620516080526110006151ed565b600101818118610f9c575b5050610f40511561103857610f40516040526113605160605261138051608052600160a0526110386152b1565b7f6827a33d0a24e36314681156d8d9a7d20d6a0548c169735fe25e00c9d38ac5a96101e0611220516116005261128051611620526112a051611640526112c051611660526112e05161168052611300516116a052611320516116c052611340516116e05261136051611700526113805161172052806117405280611600015f6113a0518083528060071b5f8260048111615a9357801561111457905b8060071b60208701018160071b6113c0018051825260208101516020830152604081015160408301526060810151606083015250506001018181186110d4575b505082016020019150509050810190506115c051611760526112005161178052610aa0516117a052610f40516117c052611600a16020611220f3613d08565b631b0b0c638118613d085734615a93576020615bb560403960206040f3613d08565b63225daa0f8118613d0857602436103417615a935760066004356020525f5260405f205460405260206040f3613d08565b6396069a5f8118613d0857602436103417615a93576004358060a01c615a935760405260086040516020525f5260405f205460605260206060f3613d08565b6372e76f13811861130a57602436103417615a93576004358060a01c615a93576040525f5433181561126d5760096060527f6e6f74206f776e6572000000000000000000000000000000000000000000000060805260605060605180608001601f825f031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516112d057601a6060527f77616c6c657420697320746865207a65726f206164647265737300000000000060805260605060605180608001601f825f031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b7f6defa6e1a7dcc97f459fb552cf25427ab19cd60692b94e4f38660850a6e917e860035460605260405160805260406060a1604051600355005b63c75b6e5e8118613d08576102c436103417615a935760406004610780376044358060a01c615a93576107c0526064356107e052606060846108003760e4358060a01c615a935761086052610104358060031c615a935761088052610124356108a05260a06101446108c0376101e4358060a01c615a935761096052610204358060011c615a93576109805260406102246109a03760606102646109e037610960516040526113ba610a4061533d565b610a405161142757600a610a60527f6e6f74206c656e64657200000000000000000000000000000000000000000000610a8052610a6050610a605180610a8001601f825f031636823750506308c379a0610a20526020610a4052601f19601f610a60510116604401610a3cfd5b42610940511161149657600d610a40527f6f66666572206578706972656400000000000000000000000000000000000000610a6052610a4050610a405180610a6001601f825f031636823750506308c379a0610a00526020610a2052601f19601f610a40510116604401610a1cfd5b6102c060406102c061078060045afa5061096051610300526114b9610a40613d0c565b610a405161152657601a610a60527f6f66666572206e6f74207369676e6564206279206c656e646572000000000000610a8052610a6050610a605180610a8001601f825f031636823750506308c379a0610a20526020610a4052601f19601f610a60510116604401610a3cfd5b6102c060406102c061078060045afa50611541610a6061452b565b610a6051610a40526007610a40516020525f5260405f2054156115c3576015610a60527f6f6666657220616c7265616479207265766f6b65640000000000000000000000610a8052610a6050610a605180610a8001601f825f031636823750506308c379a0610a20526020610a4052601f19601f610a60510116604401610a3cfd5b610a40516040526102c060606102c061078060045afa506115e26145dd565b00613d08565b63bc71771d8118613d0857602436103417615a93576004358060a01c615a93576040525f543318156116705760096060527f6e6f74206f776e6572000000000000000000000000000000000000000000000060805260605060605180608001601f825f031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516116d35760106060527f5f61646472657373206973207a65726f0000000000000000000000000000000060805260605060605180608001601f825f031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b7f722cace8a9cbcb7713f3f71015c8c6b59ed317672489140eb6b3fa7f4a139d845f5460605260405160805260406060a160405160015500613d08565b63802d67998118613d085761022436103417615a93576004356004018035610c60526020810135610c80526040810135610ca0526060810135610cc0526080810135610ce05260a08101358060a01c615a9357610d005260c0810135610d205260e0810135610d40526101008101358060a01c615a9357610d60526101208101358060a01c615a9357610d80526101408101358060a01c615a9357610da052610160810135610dc05261018081013581016004813511615a935780355f8160048111615a9357801561182e57905b8060071b610e00018160071b602086010180358060041c615a93578252602081013560208301526040810135604083015260608101358060a01c615a9357606083015250506001018181186117de575b505080610de05250506101a08101358060011c615a9357611000526101c08101358060a01c615a935761102052506103e06108606103e0610c6060045afa50611878611040615308565b611040516118e557600c611060527f696e76616c6964206c6f616e00000000000000000000000000000000000000006110805261106050611060518061108001601f825f031636823750506308c379a061102052602061104052601f19601f61106051011660440161103cfd5b610d205142111561195557600e611040527f6c6f616e2064656661756c7465640000000000000000000000000000000000006110605261104050611040518061106001601f825f031636823750506308c379a061100052602061102052601f19601f61104051011660440161101cfd5b610d605160405261196761104061533d565b611040516119d457600c611060527f6e6f7420626f72726f77657200000000000000000000000000000000000000006110805261106050611060518061108001601f825f031636823750506308c379a061102052602061104052601f19601f61106051011660440161103cfd5b6103e060406103e0610c6060045afa506119ef611060615370565b6110605161104052604036611060375f611220526103e060406103e0610c6060045afa506110405161042052611a266112406153da565b61124080516020606082020180611080828560045afa5050506101a0810151611060526101c081015161122052505f6002610c60516020525f5260405f2055610ca051604052611a746154f5565b610d6051604052610cc05161104051808201828110615a93579050905061122051808201828110615a935790509050606052611aae615517565b610d8051604052610cc05161104051808201828110615a93579050905061106051808203828111615a93579050905061122051808201828110615a935790509050606052611afa6155d6565b5f6110805160048111615a93578015611b5157905b606081026110a001805161124052602081015161126052604081015161128052506112805160405261126051606052611b466155d6565b600101818118611b0f575b5050610d605161022052610da05161024052610dc05161026052611b7361588c565b6110205115611b9d5761102051604052610da051606052610dc0516080525f60a052611b9d6152b1565b7f2aa6f229bb00348f1aa98ab5ab1636a2618696536f698200d76b66a87906eafa60e0610c605161124052610d605161126052610d805161128052610d00516112a052610cc0516112c052611040516112e052806113005280611240015f61108051808352606081025f8260048111615a93578015611c4e57905b606081026020870101606082026110a0018051825260208101516020830152604081015160408301525050600101818118611c18575b50508201602001915050905081019050611240a100613d08565b6313c072ee8118613d085761058436103417615a93576004356004018035610c60526020810135610c80526040810135610ca0526060810135610cc0526080810135610ce05260a08101358060a01c615a9357610d005260c0810135610d205260e0810135610d40526101008101358060a01c615a9357610d60526101208101358060a01c615a9357610d80526101408101358060a01c615a9357610da052610160810135610dc05261018081013581016004813511615a935780355f8160048111615a93578015611d8657905b8060071b610e00018160071b602086010180358060041c615a93578252602081013560208301526040810135604083015260608101358060a01c615a935760608301525050600101818118611d36575b505080610de05250506101a08101358060011c615a9357611000526101c08101358060a01c615a9357611020525060406024611040376064358060a01c615a9357611080526084356110a052606060a46110c037610104358060a01c615a935761112052610124358060031c615a935761114052610144356111605260a061016461118037610204358060a01c615a935761122052610224358060011c615a93576112405260406102446112603760606102846112a0376102e4356004016020813511615a9357803560208160051b01808361130037505050610344358060a01c615a9357611720526103e06108606103e0610c6060045afa50611e8b611740615308565b61174051611ef857600c611760527f696e76616c6964206c6f616e00000000000000000000000000000000000000006117805261176050611760518061178001601f825f031636823750506308c379a061172052602061174052601f19601f61176051011660440161173cfd5b610d6051604052611f0a61174061533d565b61174051611f7757600c611760527f6e6f7420626f72726f77657200000000000000000000000000000000000000006117805261176050611760518061178001601f825f031636823750506308c379a061172052602061174052601f19601f61176051011660440161173cfd5b610d2051421115611fe757600e611740527f6c6f616e2064656661756c7465640000000000000000000000000000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b6102c060406102c061104060045afa50611220516103005261200a611740613d0c565b6117405161207757601a611760527f6f66666572206e6f74207369676e6564206279206c656e6465720000000000006117805261176050611760518061178001601f825f031636823750506308c379a061172052602061174052601f19601f61176051011660440161173cfd5b4261120051116120e657600d611740527f6f666665722065787069726564000000000000000000000000000000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b6020615ad55f395f5161108051181561215e576015611740527f696e76616c6964207061796d656e7420746f6b656e00000000000000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b611040516110c05111156121d157601c611740527f6f726967696e6174696f6e20666565206774207072696e636970616c000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b6020615b355f395f516356108f8a611780526111c0516117a0526040611780602461179c5f855af1612205573d5f5f3e3d5ffd5b60403d10615a9357611780518060a01c615a93576117e0526117a051611800526117e0905080516117405260208101516117605250610260604061026061104060045afa50610dc0516102a052611740516102c052611760516102e0526113005160208160051b01806103008261130060045afa505050612284613e1f565b610da0516117405118156122f757601c611780527f636f6c6c61746572616c20636f6e7472616374206d69736d61746368000000006117a0526117805061178051806117a001601f825f031636823750506308c379a061174052602061176052601f19601f61178051011660440161175cfd5b6102c06103a06102c061104060045afa50612310614636565b610ca05160405261231f6154f5565b611040518060ff1c615a9357610cc0518060ff1c615a93578082038281135f831218615a935790509050611780526103e060406103e0610c6060045afa506123686117c0615370565b6117c0516117a0526040366117c0375f611980526103e060406103e0610c6060045afa506117a0516104205261239f6119a06153da565b6119a0805160206060820201806117e0828560045afa5050506101a08101516117c0526101c08101516119805250610260604061026061104060045afa5060406103046102a037611720516102e0526123f9611bc0614104565b611bc0805160208160071b01806119a0828560045afa505050505f611bc0525f6119a05160048111615a9357801561247c57905b8060071b6119c0018051611be0526020810151611c00526040810151611c20526060810151611c405250611bc051611c0051808201828110615a935790509050611bc05260010181811861242d575b50505f6002610c60516020525f5260405f205561178051611bc0516117a051808201828110615a93579050905061198051808201828110615a9357905090508060ff1c615a93578082038281135f831218615a9357905090506110e0518060ff1c615a93578082018281125f831218615a935790509050611be052610cc0516117a051808201828110615a9357905090506117c051808203828111615a93579050905061198051808201828110615a935790509050611c0052611040516110c051808203828111615a9357905090506110e051808201828110615a935790509050611c20527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611be051136125c057610d6051604052611be051805f03600160ff1b82141582825f03141615615a935790505f8112615a93576060526125c0615517565b61122051610d8051146125fd5761122051604052611c20516060526125e3615517565b610d8051604052611c005160605261266c6155d65661266c565b611c2051611c00511161264457611c2051611c0051101561266c57610d8051604052611c2051611c0051808203828111615a93579050905060605261266c6155175661266c565b610d8051604052611c0051611c2051808203828111615a93579050905060605261266c6155d6565b6001611be0511261269457610d6051604052611be0515f8112615a93576060526126946155d6565b5f6117e05160048111615a935780156126eb57905b60608102611800018051611c40526020810151611c60526040810151611c805250611c8051604052611c60516060526126e06155d6565b6001018181186126a9575b50505f6119a05160048111615a9357801561276a57905b8060071b6119c0018051611c40526020810151611c60526040810151611c80526060810151611ca052506002611c40511461274257611c60511515612744565b5f5b1561275f57611ca051604052611c605160605261275f6155d6565b600101818118612702575b50506102c060406102c061104060045afa50612787611c6061452b565b611c6051611c40525f611c6052611c4051611c805261128051611ca05261104051611cc05261106051611ce05261108051611d0052426110a051808201828110615a935790509050611d205242611d4052610d6051611d605261122051611d805261174051611da052610dc051611dc0526119a05160208160071b0180611de0826119a060045afa505050611240516120005261102051612020526103e060406103e0611c6060045afa5061283d612040614574565b61204051611c60526002611c60516020525f5260405f2054156128bf576013612040527f6c6f616e20616c726561647920657869737473000000000000000000000000006120605261204050612040518061206001601f825f031636823750506308c379a061200052602061202052601f19601f61204051011660440161201cfd5b6103e060406103e0611c6060045afa506128da6120406147a6565b612040516002611c60516020525f5260405f20557fadf0e5d2eb7098352961e41ff94c8d5bd1e0d24910d7c8e7ae147610146fef21610240611c605161204052611cc05161206052611ce05161208052611d00516120a052611d20516120c052611d40516120e052611da05161210052611dc05161212052611d605161214052611d805161216052806121805280612040015f611de0518083528060071b5f8260048111615a935780156129ca57905b8060071b60208701018160071b611e000180518252602081015160208301526040810151604083015260608101516060830152505060010181811861298a575b50508201602001915050905081019050612000516121a052610c60516121c052610cc0516121e0526117a05161220052806122205280612040015f6117e051808352606081025f8260048111615a93578015612a5857905b60608102602087010160608202611800018051825260208101516020830152604081015160408301525050600101818118612a22575b50508201602001915050905081019050611c4051612240526112805161226052612040a16020611c60f3613d08565b63304b74ed8118613d085761052436103417615a93576004356004018035610c60526020810135610c80526040810135610ca0526060810135610cc0526080810135610ce05260a08101358060a01c615a9357610d005260c0810135610d205260e0810135610d40526101008101358060a01c615a9357610d60526101208101358060a01c615a9357610d80526101408101358060a01c615a9357610da052610160810135610dc05261018081013581016004813511615a935780355f8160048111615a93578015612ba557905b8060071b610e00018160071b602086010180358060041c615a93578252602081013560208301526040810135604083015260608101358060a01c615a935760608301525050600101818118612b55575b505080610de05250506101a08101358060011c615a9357611000526101c08101358060a01c615a9357611020525060406024611040376064358060a01c615a9357611080526084356110a052606060a46110c037610104358060a01c615a935761112052610124358060031c615a935761114052610144356111605260a061016461118037610204358060a01c615a935761122052610224358060011c615a93576112405260406102446112603760606102846112a0376102e4356004016020813511615a9357803560208160051b018083611300375050506103e06108606103e0610c6060045afa50612c9a611720615308565b61172051612d0757600c611740527f696e76616c6964206c6f616e00000000000000000000000000000000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b610d8051604052612d1961172061533d565b61172051612d8657600a611740527f6e6f74206c656e646572000000000000000000000000000000000000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b610d2051421115612df657600e611720527f6c6f616e2064656661756c7465640000000000000000000000000000000000006117405261172050611720518061174001601f825f031636823750506308c379a06116e052602061170052601f19601f6117205101166044016116fcfd5b6102c060406102c061104060045afa506112205161030052612e19611720613d0c565b61172051612e8657601a611740527f6f66666572206e6f74207369676e6564206279206c656e6465720000000000006117605261174050611740518061176001601f825f031636823750506308c379a061170052602061172052601f19601f61174051011660440161171cfd5b426112005111612ef557600d611720527f6f666665722065787069726564000000000000000000000000000000000000006117405261172050611720518061174001601f825f031636823750506308c379a06116e052602061170052601f19601f6117205101166044016116fcfd5b6020615ad55f395f51611080511815612f6d576015611720527f696e76616c6964207061796d656e7420746f6b656e00000000000000000000006117405261172050611720518061174001601f825f031636823750506308c379a06116e052602061170052601f19601f6117205101166044016116fcfd5b611040516110c0511115612fe057601c611720527f6f726967696e6174696f6e20666565206774207072696e636970616c000000006117405261172050611720518061174001601f825f031636823750506308c379a06116e052602061170052601f19601f6117205101166044016116fcfd5b610d2051426110a051808201828110615a935790509050101561306257601d611720527f6d61747572697479206265666f7265206c6f616e206d617475726974790000006117405261172050611720518061174001601f825f031636823750506308c379a06116e052602061170052601f19601f6117205101166044016116fcfd5b6020615b355f395f516356108f8a611760526111c051611780526040611760602461177c5f855af1613096573d5f5f3e3d5ffd5b60403d10615a9357611760518060a01c615a93576117c052611780516117e0526117c0905080516117205260208101516117405250610260604061026061104060045afa50610dc0516102a052611720516102c052611740516102e0526113005160208160051b01806103008261130060045afa505050613115613e1f565b610da05161172051181561318857601c611760527f636f6c6c61746572616c20636f6e7472616374206d69736d61746368000000006117805261176050611760518061178001601f825f031636823750506308c379a061172052602061174052601f19601f61176051011660440161173cfd5b6102c06103a06102c061104060045afa506131a1614636565b610ca0516040526131b06154f5565b611040518060ff1c615a9357610cc0518060ff1c615a93578082038281135f831218615a935790509050611760526103e060406103e0610c6060045afa506131f96117a0615370565b6117a051611780526040366117a0375f611960526103e060406103e0610c6060045afa5061178051610420526132306119806153da565b611980805160206060820201806117c0828560045afa5050506101a08101516117a0526101c08101516119605250610260604061026061104060045afa506060366102a037613280611ba0614104565b611ba0805160208160071b0180611980828560045afa505050505f611ba0525f6119805160048111615a9357801561330357905b8060071b6119a0018051611bc0526020810151611be0526040810151611c00526060810151611c205250611ba051611be051808201828110615a935790509050611ba0526001018181186132b4575b50505f6002610c60516020525f5260405f20556103e060406103e0610c6060045afa5061026061042061026061104060045afa506117805161068052611960516106a052613352611be06158ea565b611be051611bc052611bc0518060ff1c615a93576117805161196051808201828110615a9357905090508060ff1c615a9357611760518082038281135f831218615a935790509050808281188284130218905090505f8112615a9357611be05261176051611780518060ff1c615a93578082038281135f831218615a935790509050611960518060ff1c615a93578082038281135f831218615a935790509050611be0518060ff1c615a93578082018281125f831218615a935790509050611c0052610cc05161178051808201828110615a93579050905061196051808201828110615a9357905090506110e051808201828110615a9357905090508060ff1c615a9357611ba0516117a051808201828110615a935790509050611be051808201828110615a9357905090508060ff1c615a93578082038281135f831218615a935790509050611c2052611040516110c051808203828111615a9357905090506110e051808201828110615a935790509050611c40525f611c00511215613538576012611c60527f626f72726f7765722064656c7461203c20300000000000000000000000000000611c8052611c6050611c605180611c8001601f825f031636823750506308c379a0611c20526020611c4052601f19601f611c60510116604401611c3cfd5b61122051610d8051146135f7575f611c205112156135b5576010611c60527f6c656e6465722064656c7461203c203000000000000000000000000000000000611c8052611c6050611c605180611c8001601f825f031636823750506308c379a0611c20526020611c4052601f19601f611c60510116604401611c3cfd5b61122051604052611c40516060526135cb615517565b6001611c2051126136ec57610d8051604052611c20515f8112615a93576060526136ec6155d6566136ec565b611c2051611c40518060ff1c615a93578082038281135f831218615a935790509050611c60525f611c6051131561368d576010611c80527f6c656e6465722064656c7461203e203000000000000000000000000000000000611ca052611c8050611c805180611ca001601f825f031636823750506308c379a0611c40526020611c6052601f19601f611c80510116604401611c5cfd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611c6051136136ec57610d8051604052611c6051805f03600160ff1b82141582825f03141615615a935790505f8112615a93576060526136ec615517565b6001611c00511261371457610d6051604052611c00515f8112615a93576060526137146155d6565b5f6117c05160048111615a9357801561376b57905b606081026117e0018051611c60526020810151611c80526040810151611ca05250611ca051604052611c80516060526137606155d6565b600101818118613729575b50505f6119805160048111615a935780156137ea57905b8060071b6119a0018051611c60526020810151611c80526040810151611ca0526060810151611cc052506002611c6051146137c257611c805115156137c4565b5f5b156137df57611cc051604052611c80516060526137df6155d6565b600101818118613782575b50506102c060406102c061104060045afa50613807611c8061452b565b611c8051611c60525f611c8052611c6051611ca05261128051611cc05261104051611ce05261106051611d005261108051611d2052426110a051808201828110615a935790509050611d405242611d6052610d6051611d805261122051611da05261172051611dc052610dc051611de0526119805160208160071b0180611e008261198060045afa505050611240516120205261102051612040526103e060406103e0611c8060045afa506138bd612060614574565b61206051611c80526002611c80516020525f5260405f20541561393f576013612060527f6c6f616e20616c726561647920657869737473000000000000000000000000006120805261206050612060518061208001601f825f031636823750506308c379a061202052602061204052601f19601f61206051011660440161203cfd5b6103e060406103e0611c8060045afa5061395a6120606147a6565b612060516002611c80516020525f5260405f20557f3104dd99ab576a709e2bea4bedb076e17210d16fdbc54a86b7db45e9f3be8284610260611c805161206052611ce05161208052611d00516120a052611d20516120c052611d40516120e052611d605161210052611dc05161212052611de05161214052611d805161216052611da05161218052806121a05280612060015f611e00518083528060071b5f8260048111615a93578015613a4a57905b8060071b60208701018160071b611e2001805182526020810151602083015260408101516040830152606081015160608301525050600101818118613a0a575b50508201602001915050905081019050612020516121c052610c60516121e052610cc051612200526117805161222052806122405280612060015f6117c051808352606081025f8260048111615a93578015613ad857905b606081026020870101606082026117e0018051825260208101516020830152604081015160408301525050600101818118613aa2575b50508201602001915050905081019050611be05161226052611c605161228052611280516122a052612060a16020611c80f3613d08565b638199abaf8118613d085734615a93576009336020525f5260405f2054613b8b5760146040527f6e6f2070656e64696e67207472616e736665727300000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b6009336020525f5260405f20546040525f6009336020525f5260405f20556020615ad55f395f5163a9059cbb6060523360805260405160a052602060606044607c5f855af1613bdc573d5f5f3e3d5ffd5b60203d10615a93576060518060011c615a935760c05260c0905051613c5957601360e0527f6572726f722073656e64696e672066756e6473000000000000000000000000006101005260e05060e0518061010001601f825f031636823750506308c379a060a052602060c052601f19601f60e051011660440160bcfd5b7fb533bf65f7139533fed34316e420b8cefd0c9fb1730ceaf49fa4d0cd42341dd93360605260405160805260406060a100613d08565b63150b7a028118613d085760a436103417615a93576004358060a01c615a93576040526024358060a01c615a9357606052606435600401610400813511615a93576020813501808260803750507f150b7a02000000000000000000000000000000000000000000000000000000006104a05260206104a0f35b5f5ffd5b610300515f610760525f6002610320527f1901000000000000000000000000000000000000000000000000000000000000610340526103208051602082018361068001815181525050808301925050506020615bd5610620397fb54af15efd4078a08fa64b2ca6923174f13b55e91014d675bcae943a355f4335610380526102606103a0610260604060045afa50610280610360526103608051602082012090506106405260406106005261060080516020820183610680018281848460045afa50505080830192505050806106605261066090508051602082012090506106e0526102a051610700526102c051610720526102e05161074052602061076060806106e060015afa506107605114815250565b6102c051613e8c57601a610720527f636f6c6c61746572616c206e6f742077686974656c69737465640000000000006107405261072050610720518061074001601f825f031636823750506308c379a06106e052602061070052601f19601f6107205101166044016106fcfd5b60016101405118613f0e576102a051610160511815614102576015610720527f746f6b656e206964206e6f7420696e206f6666657200000000000000000000006107405261072050610720518061074001601f825f031636823750506308c379a06106e052602061070052601f19601f6107205101166044016106fcfd614102565b6002610140511861400357610180516102a0511015613f8c576019610720527f746f6b656e69642062656c6f77206f666665722072616e6765000000000000006107405261072050610720518061074001601f825f031636823750506308c379a06106e052602061070052601f19601f6107205101166044016106fcfd5b6101a0516102a0511115614102576019610720527f746f6b656e69642061626f7665206f666665722072616e6765000000000000006107405261072050610720518061074001601f825f031636823750506308c379a06106e052602061070052601f19601f6107205101166044016106fcfd614102565b6102c051610760526101e051610780526102a0516107a052606061074052610740805160208201209050610720525f6103005160208111615a9357801561408d57905b8060051b610320015161074052610740515f5260205f20610720515f5260205f20186107805260206107605261076080516020820120905061072052600101818118614046575b5050610720516102e051181561410257600d610740527f70726f6f6620696e76616c6964000000000000000000000000000000000000006107605261074050610740518061076001601f825f031636823750506308c379a061070052602061072052601f19601f61074051011660440161071cfd5b565b5f6103005260c051156141825760405160c051111561418257601c610520527f6f726967696e6174696f6e20666565206774207072696e636970616c000000006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6101005115614192576001614198565b60e05115155b1561420a576101205161420a57601a610520527f62726f6b65722066656520776974686f757420616464726573730000000000006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6102a0511561421a576001614221565b6102c05115155b15614293576102e05161429357601a610520527f62726f6b65722066656520776974686f757420616464726573730000000000006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6020615b955f395f5161010051111561430b57601d610520527f6c656e6465722062726f6b6572206665652065786365656473206d61780000006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6020615bb55f395f516102c051111561438357601f610520527f626f72726f7765722062726f6b6572206665652065786365656473206d6178006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b61271060055461010051808201828110615a935790509050111561440657601c610520527f736574746c656d656e742066656573206774207072696e636970616c000000006105405261052050610520518061054001601f825f031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6103005160038111615a93578060071b6103200160018152600454604051808202811583838304141715615a93579050905061271081049050602082015260055460408201526003546060820152506001810161030052506103005160038111615a93578060071b610320016002815260c05160208201525f6040820152610220516060820152506001810161030052506103005160038111615a93578060071b610320016004815260e0516020820152610100516040820152610120516060820152506001810161030052506103005160038111615a93578060071b61032001600881526102a05160208201526102c05160408201526102e0516060820152506001810161030052506103005160208160071b0180838261030060045afa50505050565b5f6102a0518161032001526020810190506102c0518161032001526020810190506102e05181610320015260208101905080610300526103009050805160208201209050815250565b5f610140518161044001526020810190506101605181610440015260208101905061012051816104400152602081019050610180518161044001526020810190506101a05181610440015260208101905080610420526104209050805160208201209050815250565b600160076040516020525f5260405f20557f08f7f4fedc8c9bd3165579676da5b715f2babe388ed555519fcae0e56c2e507d6040516103205261024051610340526101e0516103605261016051610380526080610320a1565b6102c060406102c06103a060045afa5061465161068061452b565b61068051610660526007610660516020525f5260405f2054156146d357600d610680527f6f66666572207265766f6b6564000000000000000000000000000000000000006106a0526106805061068051806106a001601f825f031636823750506308c379a061064052602061066052601f19601f61068051011660440161065cfd5b60066105e0516020525f5260405f2054610680526105c05161068051106147595760146106a0527f6f666665722066756c6c79207574696c697a65640000000000000000000000006106c0526106a0506106a051806106c001601f825f031636823750506308c379a061066052602061068052601f19601f6106a051011660440161067cfd5b6106805160018101818110615a9357905060066105e0516020525f5260405f205560016104a051186147a457610660516040526102c060606102c06103a060045afa506147a46145dd565b565b6020806104405280610440016101e060405182526060516020830152608051604083015260a051606083015260c051608083015260e05160a08301526101005160c08301526101205160e08301526101405161010083015261016051610120830152610180516101408301526101a051610160830152806101808301528082015f6101c0518083528060071b5f8260048111615a9357801561488457905b8060071b60208701018160071b6101e001805182526020810151602083015260408101516040830152606081015160608301525050600101818118614844575b505082016020019150509050810190506103e0516101a0830152610400516101c083015290508101905061042052610420805160208201209050815250565b604051635817816860805260605160a052602060806024609c845afa6148eb573d5f5f3e3d5ffd5b60203d10615a93576080518060a01c615a935760c05260c0905051815250565b6020615b155f395f5163088f11f3610140526080516101605260a0610140602461015c845afa61493d573d5f5f3e3d5ffd5b60a03d10615a9357610140518060011c615a9357610200526101605161022052610180518060a01c615a9357610240526101a051610260526101c0518060a01c615a9357610280526102009050805160a052602081015160c052604081015160e0526060810151610100526080810151610120525060a0516149bf575f6149f5565b60805160c051186149f357610100516149ed57610120516149e15760016149f5565b306101205118156149f5565b5f6149f5565b5f5b815250565b60605163088f11f3610140526080516101605260a0610140602461015c845afa614a26573d5f5f3e3d5ffd5b60a03d10615a9357610140518060011c615a9357610200526101605161022052610180518060a01c615a9357610240526101a051610260526101c0518060a01c615a9357610280526102009050805160a052602081015160c052604081015160e0526060810151610100526080810151610120525060a051614b07576017610140527f636f6c6c61746572616c206e6f7420666f722073616c650000000000000000006101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b60805160c0511815614b7857601f610140527f636f6c6c61746572616c20776974682077726f6e672070756e6b496e646578006101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b60405160e0511815614be957601e610140527f636f6c6c61746572616c206e6f77206f776e65642062792077616c6c657400006101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b6101005115614c5757601c610140527f636f6c6c61746572616c206f66666572206973206e6f74207a65726f000000006101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b61012051614c66576001614c6e565b306101205118155b614cd7576020610140527f636f6c6c61746572616c20627579696e67206e6f7420617574686f72697a65646101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b606051638264fe986101405260805161016052803b15615a93575f610140602461015c5f855af1614d0a573d5f5f3e3d5ffd5b50565b604051636352211e60805260605160a052602060806024609c845afa614d35573d5f5f3e3d5ffd5b60203d10615a93576080518060a01c615a935760c05260c0905051815250565b60605163e985e9c560a05260405160c0523060e052602060a0604460bc845afa614d81573d5f5f3e3d5ffd5b60203d10615a935760a0518060011c615a935761010052610100905051614df4573060605163081812fc61012052608051610140526020610120602461013c845afa614dcf573d5f5f3e3d5ffd5b60203d10615a9357610120518060a01c615a9357610160526101609050511815614df7565b60015b815250565b60605163b88d4fde60c052608060405160e05230610100526080516101205280610140525f60a05260a08160e0015f81528051806020830101601f825f03163682375050601f19601f825160200101169050905081015050803b15615a93575f60c060a460dc5f855af1614e72573d5f5f3e3d5ffd5b50565b6020615b155f395f5160405114815250565b6102a051614ef4576015610300527f6164647220697320746865207a65726f206164647200000000000000000000006103205261030050610300518061032001601f825f031636823750506308c379a06102c05260206102e052601f19601f6103005101166044016102dcfd5b6102c051614f6157601c610300527f636f6c6c6174206164647220697320746865207a65726f2061646472000000006103205261030050610300518061032001601f825f031636823750506308c379a06102c05260206102e052601f19601f6103005101166044016102dcfd5b6102c051604052614f73610300614e75565b610300516150b5576102a0516102c0516040526102e051606052614f98610300614d0d565b61030051181561500757601e610320527f636f6c6c61746572616c206e6f74206f776e65642062792077616c6c657400006103405261032050610320518061034001601f825f031636823750506308c379a06102e052602061030052601f19601f6103205101166044016102fcfd5b6102a0516040526102c0516060526102e051608052615027610300614d55565b61030051615094576018610320527f7472616e73666572206973206e6f7420617070726f76656400000000000000006103405261032050610320518061034001601f825f031636823750506308c379a06102e052602061030052601f19601f6103205101166044016102fcfd5b6102a0516040526102c0516060526102e0516080526151eb614dfc566151eb565b6102a0516102c0516040526102e0516060526150d26103206148c3565b61032051181561514157601e610340527f636f6c6c61746572616c206e6f74206f776e65642062792077616c6c657400006103605261034050610340518061036001601f825f031636823750506308c379a061030052602061032052601f19601f61034051011660440161031cfd5b6102a0516040526102c0516060526102e05160805261516161032061490b565b610320516151ce576018610340527f7472616e73666572206973206e6f7420617070726f76656400000000000000006103605261034050610340518061036001601f825f031636823750506308c379a061030052602061032052601f19601f61034051011660440161031cfd5b6102a0516040526102c0516060526102e0516080526151eb6149fa565b565b6020615ad55f395f516323b872dd60a05260405160c05260605160e05260805161010052602060a0606460bc5f855af1615229573d5f5f3e3d5ffd5b60203d10615a935760a0518060011c615a9357610120526101209050516152af576013610140527f7472616e7366657246726f6d206661696c6564000000000000000000000000006101605261014050610140518061016001601f825f031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b565b6020615af55f395f5163b18e2bbb60c05260405160e05260605161010052608051610120525f6101405260a05161016052602060c060a460dc5f855af16152fa573d5f5f3e3d5ffd5b60203d10615a935760c05050565b6103e060406103e061086060045afa50615323610c406147a6565b610c40516002610860516020525f5260405f205414815250565b604051331861534d57600161536b565b6008336020525f5260405f2054615364575f61536b565b3260405118155b815250565b6103e0516153875760c0518152506153d8566153d8565b60c0514261012051808203828111615a935790509050808202811583838304141715615a9357905090506101005161012051808203828111615a9357905090508015615a9357808204905090508152505b565b604036610440375f610600525f6101c05160048111615a935780156154c457905b8060071b6101e0018051610620526020810151610640526040810151610660526060810151610680525061066051156154b9576104205161066051808202811583838304141715615a935790509050612710810490506106a0526104605160038111615a935760608102610480016106205181526106a051602082015261068051604082015250600181016104605250610440516106a051808201828110615a93579050905061044052600861062051186154b9576106a051610600525b6001018181186153fb575b5050610460516020606082020180838261046060045afa505050610440516101a0820152610600516101c082015250565b60066040516020525f5260405f20805460018103818111615a93579050815550565b6020615ad55f395f516323b872dd60805260405160a0523060c05260605160e052602060806064609c5f855af1615550573d5f5f3e3d5ffd5b60203d10615a93576080518060011c615a9357610100526101009050516155d4576013610120527f7472616e7366657246726f6d206661696c6564000000000000000000000000006101405261012050610120518061014001601f825f031636823750506308c379a060e052602061010052601f19601f61012051011660440160fcfd5b565b6040366080376020615ad55f395f515a63a9059cbb60e4526004604051610104526060516101245260400160e05260e050602061018060e0516101005f8686f1905090506080523d602081183d602010021861016052610160805160a052602081015160c0525060805161564b57600161565a565b60c05160a05160200360031b1c155b156156b7577f1c43b9761b3fba5321ca8212bfc231945f668ccc0c446f333999eea9ce8fda8160405160e05260605161010052604060e0a160096040516020525f5260405f208054606051808201828110615a9357905090508155505b565b3061010051604052610120516060526156d36101406148c3565b61014051181561574257601d610160527f636f6c6c61746572616c206e6f74206f776e6564206279207661756c740000006101805261016050610160518061018001601f825f031636823750506308c379a061012052602061014052601f19601f61016051011660440161013cfd5b61010051638b72a2ec6101405260e051610160526101205161018052803b15615a93575f610140604461015c5f855af161577e573d5f5f3e3d5ffd5b50565b30610100516040526101205160605261579b610140614d0d565b61014051181561580a57601d610160527f636f6c6c61746572616c206e6f74206f776e6564206279207661756c740000006101805261016050610160518061018001601f825f031636823750506308c379a061012052602061014052601f19601f61016051011660440161013cfd5b6101005163b88d4fde610160526080306101805260e0516101a052610120516101c052806101e0525f6101405261014081610180015f81528051806020830101601f825f03163682375050601f19601f825160200101169050905081015050803b15615a93575f61016060a461017c5f855af1615889573d5f5f3e3d5ffd5b50565b6102405160405261589e610280614e75565b610280516158c9576102205160e052610240516101005261026051610120526158e8615781566158e8565b6102205160e052610240516101005261026051610120526158e86156b9565b565b5f6106c0525f6101c05160048111615a9357801561594d57905b8060071b6101e00180516106e0526020810151610700526040810151610720526060810151610740525060086106e0511861594257610720516106c0525b600101818118615904575b50506106205161595f57610440615968565b5f610700526107005b516106e0526106205161597b575f6159b0565b60c05161068051808203828111615a9357905090506106c051808202811583838304141715615a935790509050612710810490505b6107005260c05161068051808203828111615a93579050905061072052610620516159dd57610440615a21565b610440516101005142808203828111615a935790509050808202811583838304141715615a935790509050610480518015615a935780820490509050610760526107605b51610740526106e0518060ff1c615a9357610740518060ff1c615a9357610720518060ff1c615a93578082038281135f831218615a935790509050610700518060ff1c615a93578082038281135f831218615a935790509050808281188284130218905090505f8112615a9357815250565b5f80fd11e5075c08193c8f3d0807db2a873b0f09081c683d083d0800643d083d0817103d0801023d0811a608e805a0077e00183d080493092a3d081175115315e8000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000447e69651d841bd8d104bed493000000000000000000000000b47e3cd837ddf8e4c57f05d70ab865de6e193bbb000000000000000000000000a11704b3dd545dc0771d9a9c5572064db2a5c06200000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000013886d1b99f1a5c06364c839c0ed8aed09de5cb05b31a10190bbb613cede614558e4

Verified Source Code Partial Match

Compiler: v0.3.10+commit.91361694
P2PLendingNfts.vy 1195 lines
# @version 0.3.10

"""
@title P2PLendingNfts
@author [Zharta](https://zharta.io/)
@notice This contract facilitates peer-to-peer lending using NFTs as collateral.
@dev It facilitates peer-to-peer lending using NFTs as collateral.
      The contract allows lenders to offer loans and borrowers to accept them by providing NFTs as collateral.
      Key functionalities include:
      - Creating and managing loan offers
      - Accepting loan offers and locking NFTs as collateral
      - Accepts ERC721 and CryptoPunks NFTs as collateral
      - Delegating the collateral using [Delegate](https://delegate.xyz/) DelegateRegistry v2
      - Settling loans by repaying the principal and interest
      - Claiming collateral in case of loan default
      - Replacing existing loans with new terms
      - Four types of fees are supported: protocol fee, origination fee, lender broker fee, and borrower broker fee
      - Managing protocol fees and authorized proxies
      - Handling ownership transfer of the contract
      - Loan state is kept hashed in the contract to save gas
      The contract ensures secure and transparent lending operations within the Zharta ecosystem.
"""

# Interfaces

from vyper.interfaces import ERC165 as IERC165
from vyper.interfaces import ERC721 as IERC721
from vyper.interfaces import ERC20 as IERC20


interface CryptoPunksMarket:
    def transferPunk(to: address, punkIndex: uint256): nonpayable
    def buyPunk(punkIndex: uint256): payable
    def punksOfferedForSale(punkIndex: uint256) -> PunkOffer: view
    def punkIndexToAddress(punkIndex: uint256) -> address: view
    def offerPunkForSaleToAddress(punkIndex: uint256, minSalePriceInWei: uint256, toAddress: address): nonpayable

interface DelegationRegistry:
    def delegateERC721(delegate: address, contract: address, token_id: uint256, rights: bytes32, _value: bool) -> bytes32: nonpayable

interface P2PLendingControl:
    def get_collection_status(collection_key_hash: bytes32) -> CollectionStatus: nonpayable

# Structs

PROOF_MAX_SIZE: constant(uint256) = 32
MAX_FEES: constant(uint256) = 4
BPS: constant(uint256) = 10000

enum FeeType:
    PROTOCOL_FEE
    ORIGINATION_FEE
    LENDER_BROKER_FEE
    BORROWER_BROKER_FEE

enum OfferType:
    TOKEN
    COLLECTION
    TRAIT

struct Fee:
    type: FeeType
    upfront_amount: uint256
    interest_bps: uint256
    wallet: address

struct FeeAmount:
    type: FeeType
    amount: uint256
    wallet: address

struct Offer:
    principal: uint256
    interest: uint256
    payment_token: address
    duration: uint256
    origination_fee_amount: uint256
    broker_upfront_fee_amount: uint256
    broker_settlement_fee_bps: uint256
    broker_address: address
    offer_type: OfferType
    token_id: uint256
    token_range_min: uint256
    token_range_max: uint256
    collection_key_hash: bytes32
    trait_hash: bytes32
    expiration: uint256
    lender: address
    pro_rata: bool
    size: uint256
    tracing_id: bytes32


struct Signature:
    v: uint256
    r: uint256
    s: uint256

struct SignedOffer:
    offer: Offer
    signature: Signature

struct Loan:
    id: bytes32
    offer_id: bytes32
    offer_tracing_id: bytes32
    amount: uint256  # principal - origination_fee_amount
    interest: uint256
    payment_token: address
    maturity: uint256
    start_time: uint256
    borrower: address
    lender: address
    collateral_contract: address
    collateral_token_id: uint256
    fees: DynArray[Fee, MAX_FEES]
    pro_rata: bool
    delegate: address


struct CollectionStatus:
    contract: address
    trait_root: bytes32

struct PunkOffer:
    isForSale: bool
    punkIndex: uint256
    seller: address
    minValue: uint256
    onlySellTo: address

event LoanCreated:
    id: bytes32
    amount: uint256
    interest: uint256
    payment_token: address
    maturity: uint256
    start_time: uint256
    borrower: address
    lender: address
    collateral_contract: address
    collateral_token_id: uint256
    fees: DynArray[Fee, MAX_FEES]
    pro_rata: bool
    offer_id: bytes32
    offer_tracing_id: bytes32
    delegate: address

event LoanReplaced:
    id: bytes32
    amount: uint256
    interest: uint256
    payment_token: address
    maturity: uint256
    start_time: uint256
    collateral_contract: address
    collateral_token_id: uint256
    borrower: address
    lender: address
    fees: DynArray[Fee, MAX_FEES]
    pro_rata: bool
    original_loan_id: bytes32
    paid_principal: uint256
    paid_interest: uint256
    paid_settlement_fees: DynArray[FeeAmount, MAX_FEES]
    offer_id: bytes32
    offer_tracing_id: bytes32

event LoanReplacedByLender:
    id: bytes32
    amount: uint256
    interest: uint256
    payment_token: address
    maturity: uint256
    start_time: uint256
    collateral_contract: address
    collateral_token_id: uint256
    borrower: address
    lender: address
    fees: DynArray[Fee, MAX_FEES]
    pro_rata: bool
    original_loan_id: bytes32
    paid_principal: uint256
    paid_interest: uint256
    paid_settlement_fees: DynArray[FeeAmount, MAX_FEES]
    borrower_compensation: uint256
    offer_id: bytes32
    offer_tracing_id: bytes32

event LoanPaid:
    id: bytes32
    borrower: address
    lender: address
    payment_token: address
    paid_principal: uint256
    paid_interest: uint256
    paid_settlement_fees: DynArray[FeeAmount, MAX_FEES]

event LoanCollateralClaimed:
    id: bytes32
    borrower: address
    lender: address
    collateral_contract: address
    collateral_token_id: uint256

event OfferRevoked:
    offer_id: bytes32
    lender: address
    collection_key_hash: bytes32
    offer_type: OfferType

event OwnerProposed:
    owner: address
    proposed_owner: address

event OwnershipTransferred:
    old_owner: address
    new_owner: address

event ProtocolFeeSet:
    old_upfront_fee: uint256
    old_settlement_fee: uint256
    new_upfront_fee: uint256
    new_settlement_fee: uint256

event ProtocolWalletChanged:
    old_wallet: address
    new_wallet: address

event ProxyAuthorizationChanged:
    proxy: address
    value: bool

event TransferFailed:
    _to: address
    amount: uint256

event PendingTransfersClaimed:
    _to: address
    amount: uint256


# Global variables

owner: public(address)
proposed_owner: public(address)

payment_token: public(immutable(address))
loans: public(HashMap[bytes32, bytes32])
delegation_registry: public(immutable(DelegationRegistry))
cryptopunks: public(immutable(CryptoPunksMarket))
p2p_control: public(immutable(P2PLendingControl))

protocol_wallet: public(address)
protocol_upfront_fee: public(uint256)
protocol_settlement_fee: public(uint256)
max_protocol_upfront_fee: public(immutable(uint256))
max_protocol_settlement_fee: public(immutable(uint256))
max_lender_broker_settlement_fee: public(immutable(uint256))
max_borrower_broker_settlement_fee: public(immutable(uint256))

offer_count: public(HashMap[bytes32, uint256])
revoked_offers: public(HashMap[bytes32, bool])

authorized_proxies: public(HashMap[address, bool])
pending_transfers: public(HashMap[address, uint256])

VERSION: public(constant(String[30])) = "P2PLendingNfts.20241002"

ZHARTA_DOMAIN_NAME: constant(String[6]) = "Zharta"
ZHARTA_DOMAIN_VERSION: constant(String[1]) = "1"

DOMAIN_TYPE_HASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
OFFER_TYPE_DEF: constant(String[413]) = "Offer(uint256 principal,uint256 interest,address payment_token,uint256 duration,uint256 origination_fee_amount," \
                                        "uint256 broker_upfront_fee_amount,uint256 broker_settlement_fee_bps,address broker_address," \
                                        "uint256 offer_type,uint256 token_id,uint256 token_range_min,uint256 token_range_max,bytes32 collection_key_hash," \
                                        "bytes32 trait_hash,uint256 expiration,address lender,bool pro_rata,uint256 size,bytes32 tracing_id)"
OFFER_TYPE_HASH: constant(bytes32) = keccak256(OFFER_TYPE_DEF)

offer_sig_domain_separator: immutable(bytes32)


@external
def __init__(
    _payment_token: address,
    _p2p_control: address,
    _delegation_registry: address,
    _cryptopunks: address,
    _protocol_upfront_fee: uint256,
    _protocol_settlement_fee: uint256,
    _protocol_wallet: address,
    _max_protocol_upfront_fee: uint256,
    _max_protocol_settlement_fee: uint256,
    _max_lender_broker_settlement_fee: uint256,
    _max_borrower_broker_settlement_fee: uint256,
):

    """
    @notice Initialize the contract with the given parameters.
    @param _payment_token The address of the payment token.
    @param _delegation_registry The address of the delegation registry.
    @param _cryptopunks The address of the CryptoPunksMarket contract.
    @param _protocol_upfront_fee The percentage (bps) of the principal paid to the protocol at origination.
    @param _protocol_settlement_fee The percentage (bps) of the interest paid to the protocol at settlement.
    @param _protocol_wallet The address where the protocol fees are accrued.
    """

    assert _protocol_wallet != empty(address), "wallet is the zero address"
    assert _payment_token != empty(address), "payment token is zero"
    assert _p2p_control != empty(address), "p2p control is zero"
    assert _delegation_registry != empty(address), "delegation registry is zero"

    assert _protocol_upfront_fee <= _max_protocol_upfront_fee, "upfront fee exceeds max"
    assert _protocol_settlement_fee <= _max_protocol_settlement_fee, "settlement fee exceeds max"

    self.owner = msg.sender
    payment_token = _payment_token
    p2p_control = P2PLendingControl(_p2p_control)
    delegation_registry = DelegationRegistry(_delegation_registry)
    cryptopunks = CryptoPunksMarket(_cryptopunks)
    max_protocol_upfront_fee = _max_protocol_upfront_fee
    max_protocol_settlement_fee = _max_protocol_settlement_fee
    max_lender_broker_settlement_fee = _max_lender_broker_settlement_fee
    max_borrower_broker_settlement_fee = _max_borrower_broker_settlement_fee
    self.protocol_upfront_fee = _protocol_upfront_fee
    self.protocol_settlement_fee = _protocol_settlement_fee
    self.protocol_wallet = _protocol_wallet

    offer_sig_domain_separator = keccak256(
        _abi_encode(
            DOMAIN_TYPE_HASH,
            keccak256(ZHARTA_DOMAIN_NAME),
            keccak256(ZHARTA_DOMAIN_VERSION),
            chain.id,
            self
        )
    )



# Config functions


@external
def set_protocol_fee(protocol_upfront_fee: uint256, protocol_settlement_fee: uint256):

    """
    @notice Set the protocol fee
    @dev Sets the protocol fee to the given value and logs the event. Admin function.
    @param protocol_upfront_fee The new protocol upfront fee.
    @param protocol_settlement_fee The new protocol settlement fee.
    """

    assert msg.sender == self.owner, "not owner"
    assert protocol_upfront_fee <= max_protocol_upfront_fee, "upfront fee exceeds max"
    assert protocol_settlement_fee <= max_protocol_settlement_fee, "settlement fee exceeds max"

    log ProtocolFeeSet(self.protocol_upfront_fee, self.protocol_settlement_fee, protocol_upfront_fee, protocol_settlement_fee)
    self.protocol_upfront_fee = protocol_upfront_fee
    self.protocol_settlement_fee = protocol_settlement_fee


@external
def change_protocol_wallet(new_protocol_wallet: address):

    """
    @notice Change the protocol wallet
    @dev Changes the protocol wallet to the given address and logs the event. Admin function.
    @param new_protocol_wallet The new protocol wallet.
    """

    assert msg.sender == self.owner, "not owner"
    assert new_protocol_wallet != empty(address), "wallet is the zero address"

    log ProtocolWalletChanged(self.protocol_wallet, new_protocol_wallet)
    self.protocol_wallet = new_protocol_wallet


@external
def set_proxy_authorization(_proxy: address, _value: bool):

    """
    @notice Set authorization
    @dev Sets the authorization for the given proxy and logs the event. Admin function.
    @param _proxy The address of the proxy.
    @param _value The value of the authorization.
    """

    assert msg.sender == self.owner, "not owner"

    self.authorized_proxies[_proxy] = _value

    log ProxyAuthorizationChanged(_proxy, _value)


@external
def propose_owner(_address: address):

    """
    @notice Propose a new owner
    @dev Proposes a new owner and logs the event. Admin function.
    @param _address The address of the proposed owner.
    """

    assert msg.sender == self.owner, "not owner"
    assert _address != empty(address), "_address is zero"

    log OwnerProposed(self.owner, _address)
    self.proposed_owner = _address


@external
def claim_ownership():

    """
    @notice Claim the ownership of the contract
    @dev Claims the ownership of the contract and logs the event. Requires the caller to be the proposed owner.
    """

    assert msg.sender == self.proposed_owner, "not the proposed owner"

    log OwnershipTransferred(self.owner, self.proposed_owner)
    self.owner = msg.sender
    self.proposed_owner = empty(address)


# Core functions

@external
def create_loan(
    offer: SignedOffer,
    collateral_token_id: uint256,
    collateral_proof: DynArray[bytes32, PROOF_MAX_SIZE],
    delegate: address,
    borrower_broker_upfront_fee_amount: uint256,
    borrower_broker_settlement_fee_bps: uint256,
    borrower_broker: address
) -> bytes32:

    """
    @notice Create a loan.
    @param offer The signed offer.
    @param collateral_token_id The ID of the collateral token.
    @param delegate The address of the delegate. If empty, no delegation is set.
    @param borrower_broker_upfront_fee_amount The upfront fee amount for the borrower broker.
    @param borrower_broker_settlement_fee_bps The settlement fee basis points relative to the interest for the borrower broker.
    @param borrower_broker The address of the borrower broker.
    @return The ID of the created loan.
    """


    assert self._is_offer_signed_by_lender(offer, offer.offer.lender), "offer not signed by lender"
    assert offer.offer.expiration > block.timestamp, "offer expired"
    assert offer.offer.payment_token == payment_token, "invalid payment token"
    assert offer.offer.origination_fee_amount <= offer.offer.principal, "origination fee gt principal"

    collection_status: CollectionStatus = p2p_control.get_collection_status(offer.offer.collection_key_hash)
    self._validate_token_ids(offer.offer, collateral_token_id, collection_status, collateral_proof)

    fees: DynArray[Fee, MAX_FEES] = self._get_loan_fees(offer.offer, borrower_broker_upfront_fee_amount, borrower_broker_settlement_fee_bps, borrower_broker)
    total_upfront_fees: uint256 = 0
    for fee in fees:
        total_upfront_fees += fee.upfront_amount

    offer_id: bytes32 = self._compute_signed_offer_id(offer)
    loan: Loan = Loan({
        id: empty(bytes32),
        offer_id: offer_id,
        offer_tracing_id: offer.offer.tracing_id,
        amount: offer.offer.principal,
        interest: offer.offer.interest,
        payment_token: offer.offer.payment_token,
        maturity: block.timestamp + offer.offer.duration,
        start_time: block.timestamp,
        borrower: msg.sender if not self.authorized_proxies[msg.sender] else tx.origin,
        lender: offer.offer.lender,
        collateral_contract: collection_status.contract,
        collateral_token_id: collateral_token_id,
        fees: fees,
        pro_rata: offer.offer.pro_rata,
        delegate: delegate
    })
    loan.id = self._compute_loan_id(loan)

    assert self.loans[loan.id] == empty(bytes32), "loan already exists"
    self._check_and_update_offer_state(offer)
    self.loans[loan.id] = self._loan_state_hash(loan)

    self._store_collateral(loan.borrower, loan.collateral_contract, loan.collateral_token_id)
    self._transfer_funds(loan.lender, loan.borrower, loan.amount - total_upfront_fees + offer.offer.broker_upfront_fee_amount)

    for fee in fees:
        if fee.type != FeeType.ORIGINATION_FEE and fee.upfront_amount > 0:
            self._transfer_funds(loan.lender, fee.wallet, fee.upfront_amount)

    if delegate != empty(address):
        self._set_delegation(delegate, loan.collateral_contract, loan.collateral_token_id, True)

    log LoanCreated(
        loan.id,
        loan.amount,
        loan.interest,
        loan.payment_token,
        loan.maturity,
        loan.start_time,
        loan.borrower,
        loan.lender,
        loan.collateral_contract,
        loan.collateral_token_id,
        loan.fees,
        loan.pro_rata,
        offer_id,
        offer.offer.tracing_id,
        delegate
    )
    return loan.id


@external
def settle_loan(loan: Loan):

    """
    @notice Settle a loan.
    @param loan The loan to be settled.
    """

    assert self._is_loan_valid(loan), "invalid loan"
    assert block.timestamp <= loan.maturity, "loan defaulted"
    assert self._check_user(loan.borrower), "not borrower"

    interest: uint256 = self._compute_settlement_interest(loan)
    settlement_fees_total: uint256 = 0
    settlement_fees: DynArray[FeeAmount, MAX_FEES] = []
    borrower_broker_fee_amount: uint256 = 0
    settlement_fees, settlement_fees_total, borrower_broker_fee_amount = self._get_settlement_fees(loan, interest)

    self.loans[loan.id] = empty(bytes32)
    self._reduce_offer_count(loan.offer_tracing_id)

    self._receive_funds(loan.borrower, loan.amount + interest + borrower_broker_fee_amount)

    self._send_funds(loan.lender, loan.amount + interest - settlement_fees_total + borrower_broker_fee_amount)
    for fee in settlement_fees:
        self._send_funds(fee.wallet, fee.amount)

    self._transfer_collateral(loan.borrower, loan.collateral_contract, loan.collateral_token_id)

    if loan.delegate != empty(address):
        self._set_delegation(loan.delegate, loan.collateral_contract, loan.collateral_token_id, False)

    log LoanPaid(
        loan.id,
        loan.borrower,
        loan.lender,
        loan.payment_token,
        loan.amount,
        interest,
        settlement_fees
    )


@external
def claim_defaulted_loan_collateral(loan: Loan):

    """
    @notice Claim defaulted loan collateral.
    @param loan The loan whose collateral is to be claimed. The loan maturity must have been passed.
    """

    assert self._is_loan_valid(loan), "invalid loan"
    assert block.timestamp > loan.maturity, "loan not defaulted"
    assert self._check_user(loan.lender), "not lender"

    self.loans[loan.id] = empty(bytes32)

    self._transfer_collateral(loan.lender, loan.collateral_contract, loan.collateral_token_id)

    if loan.delegate != empty(address):
        self._set_delegation(loan.delegate, loan.collateral_contract, loan.collateral_token_id, False)

    log LoanCollateralClaimed(
        loan.id,
        loan.borrower,
        loan.lender,
        loan.collateral_contract,
        loan.collateral_token_id
    )


@external
def replace_loan(
    loan: Loan,
    offer: SignedOffer,
    collateral_proof: DynArray[bytes32, PROOF_MAX_SIZE],
    borrower_broker_upfront_fee_amount: uint256,
    borrower_broker_settlement_fee_bps: uint256,
    borrower_broker: address
) -> bytes32:

    """
    @notice Replace an existing loan by accepting a new offer over the same collateral. The current loan is settled and the new loan is created. Must be called by the borrower.
    @dev No collateral transfer is required and the delegation is not changed. The borrower must be the same as the borrower of the current loan.
    @param loan The loan to be replaced.
    @param offer The new signed offer.
    @param borrower_broker_upfront_fee_amount The upfront fee amount for the borrower broker.
    @param borrower_broker_settlement_fee_bps The settlement fee basis points relative to the interest for the borrower broker.
    @param borrower_broker The address of the borrower broker, if any.
    @return The ID of the new loan.
    """

    assert self._is_loan_valid(loan), "invalid loan"
    assert self._check_user(loan.borrower), "not borrower"
    assert block.timestamp <= loan.maturity, "loan defaulted"

    assert self._is_offer_signed_by_lender(offer, offer.offer.lender), "offer not signed by lender"
    assert offer.offer.expiration > block.timestamp, "offer expired"
    assert offer.offer.payment_token == payment_token, "invalid payment token"
    assert offer.offer.origination_fee_amount <= offer.offer.principal, "origination fee gt principal"

    collection_status: CollectionStatus = p2p_control.get_collection_status(offer.offer.collection_key_hash)
    self._validate_token_ids(offer.offer, loan.collateral_token_id, collection_status, collateral_proof)
    assert collection_status.contract == loan.collateral_contract, "collateral contract mismatch"

    self._check_and_update_offer_state(offer)
    self._reduce_offer_count(loan.offer_tracing_id)

    principal_delta: int256 = convert(offer.offer.principal, int256) - convert(loan.amount, int256)
    interest: uint256 = self._compute_settlement_interest(loan)

    settlement_fees_total: uint256 = 0
    settlement_fees: DynArray[FeeAmount, MAX_FEES] = []
    borrower_broker_fee_amount: uint256 = 0
    settlement_fees, settlement_fees_total, borrower_broker_fee_amount = self._get_settlement_fees(loan, interest)

    new_loan_fees: DynArray[Fee, MAX_FEES] = self._get_loan_fees(offer.offer, borrower_broker_upfront_fee_amount, borrower_broker_settlement_fee_bps, borrower_broker)
    total_upfront_fees: uint256 = 0
    for fee in new_loan_fees:
        total_upfront_fees += fee.upfront_amount

    self.loans[loan.id] = empty(bytes32)

    borrower_delta: int256 = principal_delta - convert(total_upfront_fees + interest + borrower_broker_fee_amount, int256) + convert(offer.offer.broker_upfront_fee_amount, int256)
    current_lender_delta: uint256 = loan.amount + interest - settlement_fees_total + borrower_broker_fee_amount
    new_lender_delta_abs: uint256 = offer.offer.principal - offer.offer.origination_fee_amount + offer.offer.broker_upfront_fee_amount

    if borrower_delta < 0:
        self._receive_funds(loan.borrower, convert(-1 * borrower_delta, uint256))

    if loan.lender != offer.offer.lender:
        self._receive_funds(offer.offer.lender, new_lender_delta_abs)
        self._send_funds(loan.lender, current_lender_delta)
    elif current_lender_delta > new_lender_delta_abs:
        self._send_funds(loan.lender, current_lender_delta - new_lender_delta_abs)
    elif current_lender_delta < new_lender_delta_abs:
        self._receive_funds(loan.lender, new_lender_delta_abs - current_lender_delta)

    if borrower_delta > 0:
        self._send_funds(loan.borrower, convert(borrower_delta, uint256))

    for fee in settlement_fees:
        self._send_funds(fee.wallet, fee.amount)

    for fee in new_loan_fees:
        if fee.type != FeeType.ORIGINATION_FEE and fee.upfront_amount > 0:
            self._send_funds(fee.wallet, fee.upfront_amount)

    offer_id: bytes32 = self._compute_signed_offer_id(offer)
    new_loan: Loan = Loan({
        id: empty(bytes32),
        offer_id: offer_id,
        offer_tracing_id: offer.offer.tracing_id,
        amount: offer.offer.principal,
        interest: offer.offer.interest,
        payment_token: offer.offer.payment_token,
        maturity: block.timestamp + offer.offer.duration,
        start_time: block.timestamp,
        borrower: loan.borrower,
        lender: offer.offer.lender,
        collateral_contract: collection_status.contract,
        collateral_token_id: loan.collateral_token_id,
        fees: new_loan_fees,
        pro_rata: offer.offer.pro_rata,
        delegate: loan.delegate
    })
    new_loan.id = self._compute_loan_id(new_loan)

    assert self.loans[new_loan.id] == empty(bytes32), "loan already exists"
    self.loans[new_loan.id] = self._loan_state_hash(new_loan)

    log LoanReplaced(
        new_loan.id,
        new_loan.amount,
        new_loan.interest,
        new_loan.payment_token,
        new_loan.maturity,
        new_loan.start_time,
        new_loan.collateral_contract,
        new_loan.collateral_token_id,
        new_loan.borrower,
        new_loan.lender,
        new_loan.fees,
        new_loan.pro_rata,
        loan.id,
        loan.amount,
        interest,
        settlement_fees,
        offer_id,
        offer.offer.tracing_id
    )

    return new_loan.id


@external
def replace_loan_lender(loan: Loan, offer: SignedOffer, collateral_proof: DynArray[bytes32, PROOF_MAX_SIZE]) -> bytes32:

    """
    @notice Replace a loan by the lender. The current loan is settled and the new loan is created. Must be called by the lender.
    @dev No collateral transfer is required and the delegation is not changed. The borrower must be the same as the borrower of the current loan. No funds are required from the borrower. Also no funds are required from the lender, except when the current and new lender are the same.
    @param loan The loan to be replaced.
    @param offer The new signed offer.
    @return The ID of the new loan.
    """

    assert self._is_loan_valid(loan), "invalid loan"
    assert self._check_user(loan.lender), "not lender"
    assert block.timestamp <= loan.maturity, "loan defaulted"

    assert self._is_offer_signed_by_lender(offer, offer.offer.lender), "offer not signed by lender"
    assert offer.offer.expiration > block.timestamp, "offer expired"
    assert offer.offer.payment_token == payment_token, "invalid payment token"
    assert offer.offer.origination_fee_amount <= offer.offer.principal, "origination fee gt principal"
    assert block.timestamp + offer.offer.duration >= loan.maturity, "maturity before loan maturity"

    collection_status: CollectionStatus = p2p_control.get_collection_status(offer.offer.collection_key_hash)
    self._validate_token_ids(offer.offer, loan.collateral_token_id, collection_status, collateral_proof)
    assert collection_status.contract == loan.collateral_contract, "collateral contract mismatch"

    self._check_and_update_offer_state(offer)
    self._reduce_offer_count(loan.offer_tracing_id)

    principal_delta: int256 = convert(offer.offer.principal, int256) - convert(loan.amount, int256)
    interest: uint256 = self._compute_settlement_interest(loan)

    settlement_fees_total: uint256 = 0
    settlement_fees: DynArray[FeeAmount, MAX_FEES] = []
    borrower_broker_fee_amount: uint256 = 0
    settlement_fees, settlement_fees_total, borrower_broker_fee_amount = self._get_settlement_fees(loan, interest)

    new_loan_fees: DynArray[Fee, MAX_FEES] = self._get_loan_fees(offer.offer, 0, 0, empty(address))
    total_upfront_fees: uint256 = 0
    for fee in new_loan_fees:
        total_upfront_fees += fee.upfront_amount

    self.loans[loan.id] = empty(bytes32)

    max_interest_delta: uint256 = self._compute_max_interest_delta(loan, offer.offer, interest, borrower_broker_fee_amount)
    borrower_compensation: uint256 = convert(max(convert(max_interest_delta, int256), convert(interest + borrower_broker_fee_amount, int256) - principal_delta), uint256)

    borrower_delta: int256 = principal_delta - convert(interest, int256) - convert(borrower_broker_fee_amount, int256) + convert(borrower_compensation, int256)
    current_lender_delta: int256 = convert(loan.amount + interest + borrower_broker_fee_amount + offer.offer.broker_upfront_fee_amount, int256) - convert(total_upfront_fees + settlement_fees_total + borrower_compensation, int256)
    new_lender_delta_abs: uint256 = offer.offer.principal - offer.offer.origination_fee_amount + offer.offer.broker_upfront_fee_amount

    assert borrower_delta >= 0, "borrower delta < 0"

    if loan.lender != offer.offer.lender:
        assert current_lender_delta >= 0, "lender delta < 0"
        self._receive_funds(offer.offer.lender, new_lender_delta_abs)
        if current_lender_delta > 0:
            self._send_funds(loan.lender, convert(current_lender_delta, uint256))
    else:
        lender_delta: int256 = current_lender_delta - convert(new_lender_delta_abs, int256)

        # cant have lender delta > 0 and borrower delta >= 0
        assert lender_delta <= 0, "lender delta > 0"

        if lender_delta < 0:
            self._receive_funds(loan.lender, convert(-1 * lender_delta, uint256))

    if borrower_delta > 0:
        self._send_funds(loan.borrower, convert(borrower_delta, uint256))

    for fee in settlement_fees:
        self._send_funds(fee.wallet, fee.amount)

    for fee in new_loan_fees:
        if fee.type != FeeType.ORIGINATION_FEE and fee.upfront_amount > 0:
            self._send_funds(fee.wallet, fee.upfront_amount)

    offer_id: bytes32 = self._compute_signed_offer_id(offer)
    new_loan: Loan = Loan({
        id: empty(bytes32),
        offer_id: offer_id,
        offer_tracing_id: offer.offer.tracing_id,
        amount: offer.offer.principal,
        interest: offer.offer.interest,
        payment_token: offer.offer.payment_token,
        maturity: block.timestamp + offer.offer.duration,
        start_time: block.timestamp,
        borrower: loan.borrower,
        lender: offer.offer.lender,
        collateral_contract: collection_status.contract,
        collateral_token_id: loan.collateral_token_id,
        fees: new_loan_fees,
        pro_rata: offer.offer.pro_rata,
        delegate: loan.delegate
    })
    new_loan.id = self._compute_loan_id(new_loan)

    assert self.loans[new_loan.id] == empty(bytes32), "loan already exists"
    self.loans[new_loan.id] = self._loan_state_hash(new_loan)

    log LoanReplacedByLender(
        new_loan.id,
        new_loan.amount,
        new_loan.interest,
        new_loan.payment_token,
        new_loan.maturity,
        new_loan.start_time,
        new_loan.collateral_contract,
        new_loan.collateral_token_id,
        new_loan.borrower,
        new_loan.lender,
        new_loan.fees,
        new_loan.pro_rata,
        loan.id,
        loan.amount,
        interest,
        settlement_fees,
        borrower_compensation,
        offer_id,
        offer.offer.tracing_id
    )

    return new_loan.id


@external
def revoke_offer(offer: SignedOffer):

    """
    @notice Revoke an offer.
    @param offer The signed offer to be revoked.
    """

    assert self._check_user(offer.offer.lender), "not lender"
    assert offer.offer.expiration > block.timestamp, "offer expired"
    assert self._is_offer_signed_by_lender(offer, offer.offer.lender), "offer not signed by lender"

    offer_id: bytes32 = self._compute_signed_offer_id(offer)
    assert not self.revoked_offers[offer_id], "offer already revoked"

    self._revoke_offer(offer_id, offer)


@external
def claim_pending_transfers():
    assert self.pending_transfers[msg.sender] > 0, "no pending transfers"
    _amount: uint256 = self.pending_transfers[msg.sender]
    self.pending_transfers[msg.sender] = 0

    assert IERC20(payment_token).transfer(msg.sender, _amount), "error sending funds"
    log PendingTransfersClaimed(msg.sender, _amount)



@view
@external
def onERC721Received(_operator: address, _from: address, _tokenId: uint256, _data: Bytes[1024]) -> bytes4:

    """
    @notice ERC721 token receiver callback.
    @dev Returns the ERC721 receiver callback selector.
    @param _operator The address which called `safeTransferFrom` function.
    @param _from The address which previously owned the token.
    @param _tokenId The NFT identifier which is being transferred.
    @param _data Additional data with no specified format.
    @return The ERC721 receiver callback selector.
    """

    return method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)



# Internal functions

@pure
@internal
def _compute_loan_id(loan: Loan) -> bytes32:
    return keccak256(concat(
        convert(loan.borrower, bytes32),
        convert(loan.lender, bytes32),
        convert(loan.start_time, bytes32),
        convert(loan.collateral_contract, bytes32),
        convert(loan.collateral_token_id, bytes32),
    ))

@pure
@internal
def _compute_signed_offer_id(offer: SignedOffer) -> bytes32:
    return keccak256(concat(
        convert(offer.signature.v, bytes32),
        convert(offer.signature.r, bytes32),
        convert(offer.signature.s, bytes32),
    ))

@internal
def _check_and_update_offer_state(offer: SignedOffer):
    offer_id: bytes32 = self._compute_signed_offer_id(offer)
    assert not self.revoked_offers[offer_id], "offer revoked"

    count: uint256 = self.offer_count[offer.offer.tracing_id]
    assert count < offer.offer.size, "offer fully utilized"
    self.offer_count[offer.offer.tracing_id] = count + 1

    if offer.offer.offer_type == OfferType.TOKEN:
        self._revoke_offer(offer_id, offer)


@internal
def _revoke_offer(offer_id: bytes32, offer: SignedOffer):

    self.revoked_offers[offer_id] = True

    log OfferRevoked(
        offer_id,
        offer.offer.lender,
        offer.offer.collection_key_hash,
        offer.offer.offer_type,
    )


@internal
def _reduce_offer_count(tracing_id: bytes32):
    self.offer_count[tracing_id] -= 1

@view
@internal
def _is_loan_valid(loan: Loan) -> bool:
    return self.loans[loan.id] == self._loan_state_hash(loan)

@pure
@internal
def _loan_state_hash(loan: Loan) -> bytes32:
    return keccak256(_abi_encode(loan))


@internal
def _is_offer_signed_by_lender(signed_offer: SignedOffer, lender: address) -> bool:
    return ecrecover(
        keccak256(
            concat(
                convert("\x19\x01", Bytes[2]),
                _abi_encode(
                    offer_sig_domain_separator,
                    keccak256(_abi_encode(OFFER_TYPE_HASH, signed_offer.offer))
                )
            )
        ),
        signed_offer.signature.v,
        signed_offer.signature.r,
        signed_offer.signature.s
    ) == lender


@internal
def _get_loan_fees(offer: Offer, borrower_broker_upfront_fee_amount: uint256, borrower_broker_settlement_fee_bps: uint256, borrower_broker: address) -> DynArray[Fee, MAX_FEES]:
    fees: DynArray[Fee, MAX_FEES] = []
    if offer.origination_fee_amount > 0:
        assert offer.origination_fee_amount <= offer.principal, "origination fee gt principal"
    if offer.broker_settlement_fee_bps > 0 or offer.broker_upfront_fee_amount > 0:
        assert offer.broker_address != empty(address), "broker fee without address"
    if borrower_broker_upfront_fee_amount > 0 or borrower_broker_settlement_fee_bps > 0:
        assert borrower_broker != empty(address), "broker fee without address"
    assert offer.broker_settlement_fee_bps <= max_lender_broker_settlement_fee, "lender broker fee exceeds max"
    assert borrower_broker_settlement_fee_bps <= max_borrower_broker_settlement_fee, "borrower broker fee exceeds max"
    assert self.protocol_settlement_fee + offer.broker_settlement_fee_bps <= BPS, "settlement fees gt principal"

    fees.append(Fee({
        type: FeeType.PROTOCOL_FEE,
        upfront_amount: self.protocol_upfront_fee * offer.principal / BPS,
        interest_bps: self.protocol_settlement_fee,
        wallet: self.protocol_wallet
    }))
    fees.append(Fee({
        type: FeeType.ORIGINATION_FEE,
        upfront_amount: offer.origination_fee_amount,
        interest_bps: 0,
        wallet: offer.lender
    }))
    fees.append(Fee({
        type: FeeType.LENDER_BROKER_FEE,
        upfront_amount: offer.broker_upfront_fee_amount,
        interest_bps: offer.broker_settlement_fee_bps,
        wallet: offer.broker_address
    }))
    fees.append(Fee({
        type: FeeType.BORROWER_BROKER_FEE,
        upfront_amount: borrower_broker_upfront_fee_amount,
        interest_bps: borrower_broker_settlement_fee_bps,
        wallet: borrower_broker
    }))
    return fees

@internal
def _get_settlement_fees(loan: Loan, settlement_interest: uint256) -> (DynArray[FeeAmount, MAX_FEES], uint256, uint256):
    total: uint256 = 0
    settlement_fees: DynArray[FeeAmount, MAX_FEES] = []
    borrower_broker_fee_amount: uint256 = 0
    for fee in loan.fees:
        if fee.interest_bps > 0:
            fee_amount: uint256 = settlement_interest * fee.interest_bps / BPS
            settlement_fees.append(FeeAmount({type: fee.type, amount: fee_amount, wallet: fee.wallet}))
            total += fee_amount
            if fee.type == FeeType.BORROWER_BROKER_FEE:
                borrower_broker_fee_amount = fee_amount

    return (settlement_fees, total, borrower_broker_fee_amount)


@internal
def _compute_settlement_interest(loan: Loan) -> uint256:
    if loan.pro_rata:
        return loan.interest * (block.timestamp - loan.start_time) / (loan.maturity - loan.start_time)
    else:
        return loan.interest


@internal
def _compute_max_interest_delta(loan: Loan, offer: Offer, interest: uint256, borrower_broker_fee_amount: uint256) -> uint256:
    """
    Computes the maximum interest difference between the new offer and the current loan.
    That max difference can be reached either at the refinance timestamp or at the original loan maturity.
    The difference can never be negative because at the refinance timestamp the delta is just the offer interest.
    """

    borrower_broker_fee_bps: uint256 = 0
    for fee in loan.fees:
        if fee.type == FeeType.BORROWER_BROKER_FEE:
            borrower_broker_fee_bps = fee.interest_bps
    delta_at_refinance: uint256 = 0 if offer.pro_rata else offer.interest
    borrower_broker_fee_delta_at_maturity: uint256 = (loan.interest - interest) * borrower_broker_fee_bps / BPS if offer.pro_rata else 0
    loan_interest_delta_at_maturity: uint256 = loan.interest - interest
    offer_interest_at_loan_maturity: uint256 = offer.interest * (loan.maturity - block.timestamp) / offer.duration if offer.pro_rata else offer.interest

    return convert(max(
        convert(delta_at_refinance, int256),
        convert(offer_interest_at_loan_maturity, int256) - convert(loan_interest_delta_at_maturity, int256) - convert(borrower_broker_fee_delta_at_maturity, int256)
    ), uint256)


@internal
def _set_delegation(_wallet: address, _collateral_address: address, _token_id: uint256, _value: bool):
    delegation_registry.delegateERC721(_wallet, _collateral_address, _token_id, empty(bytes32), _value)


@internal
def _store_punk(_wallet: address, _collateralAddress: address, _tokenId: uint256):
    offer: PunkOffer = CryptoPunksMarket(_collateralAddress).punksOfferedForSale(_tokenId)

    assert offer.isForSale, "collateral not for sale"
    assert offer.punkIndex == _tokenId, "collateral with wrong punkIndex"
    assert offer.seller == _wallet, "collateral now owned by wallet"
    assert offer.minValue == 0, "collateral offer is not zero"
    assert offer.onlySellTo == empty(address) or offer.onlySellTo == self, "collateral buying not authorized"

    CryptoPunksMarket(_collateralAddress).buyPunk(_tokenId)


@internal
def _store_erc721(_wallet: address, _collateralAddress: address, _tokenId: uint256):
    IERC721(_collateralAddress).safeTransferFrom(_wallet, self, _tokenId, b"")


@internal
def _transfer_punk(_wallet: address, _collateralAddress: address, _tokenId: uint256):
    assert self._punk_owner(_collateralAddress, _tokenId) == self, "collateral not owned by vault"
    CryptoPunksMarket(_collateralAddress).transferPunk(_wallet, _tokenId)


@internal
def _transfer_erc721(_wallet: address, _collateralAddress: address, _tokenId: uint256):
    assert self._erc721_owner(_collateralAddress, _tokenId) == self, "collateral not owned by vault"
    IERC721(_collateralAddress).safeTransferFrom(self, _wallet, _tokenId, b"")


@internal
def _transfer_collateral(wallet: address, collateral_contract: address, token_id: uint256):
    if self._is_punk(collateral_contract):
        self._transfer_punk(wallet, collateral_contract, token_id)
    else:
        self._transfer_erc721(wallet, collateral_contract, token_id)


@internal
def _send_funds(_to: address, _amount: uint256):
    success: bool = False
    response: Bytes[32] = b""

    success, response = raw_call(
        payment_token,
        _abi_encode(_to, _amount, method_id=method_id("transfer(address,uint256)")),
        max_outsize=32,
        revert_on_failure=False
    )

    if not success or not convert(response, bool):
        log TransferFailed(_to, _amount)
        self.pending_transfers[_to] += _amount


@internal
def _receive_funds(_from: address, _amount: uint256):
    assert IERC20(payment_token).transferFrom(_from, self, _amount), "transferFrom failed"


@internal
def _transfer_funds(_from: address, _to: address, _amount: uint256):
    assert IERC20(payment_token).transferFrom(_from, _to, _amount), "transferFrom failed"

@pure
@internal
def _is_punk(_collateralAddress: address) -> bool:
    return _collateralAddress == cryptopunks.address


@view
@internal
def _punk_owner(_collateralAddress: address, _tokenId: uint256) -> address:
    return CryptoPunksMarket(_collateralAddress).punkIndexToAddress(_tokenId)


@view
@internal
def _erc721_owner(_collateralAddress: address, _tokenId: uint256) -> address:
    return IERC721(_collateralAddress).ownerOf(_tokenId)


@view
@internal
def _is_punk_approved_for_vault(_borrower: address, _collateralAddress: address, _tokenId: uint256) -> bool:
    offer: PunkOffer = cryptopunks.punksOfferedForSale(_tokenId)
    return (
        offer.isForSale and
        offer.punkIndex == _tokenId and
        offer.minValue == 0 and
        (offer.onlySellTo == empty(address) or offer.onlySellTo == self)
    )


@view
@internal
def _is_erc721_approved_for_vault(_borrower: address, _collateralAddress: address, _tokenId: uint256) -> bool:
    return IERC721(_collateralAddress).isApprovedForAll(_borrower, self) or IERC721(_collateralAddress).getApproved(_tokenId) == self


@internal
def _store_collateral(wallet: address, collateral_contract: address, token_id: uint256):

    assert wallet != empty(address), "addr is the zero addr"
    assert collateral_contract != empty(address), "collat addr is the zero addr"

    if self._is_punk(collateral_contract):
        assert self._punk_owner(collateral_contract, token_id) == wallet, "collateral not owned by wallet"
        assert self._is_punk_approved_for_vault(wallet, collateral_contract, token_id), "transfer is not approved"
        self._store_punk(wallet, collateral_contract, token_id)

    else:
        assert self._erc721_owner(collateral_contract, token_id) == wallet, "collateral not owned by wallet"
        assert self._is_erc721_approved_for_vault(wallet, collateral_contract, token_id), "transfer is not approved"
        self._store_erc721(wallet, collateral_contract, token_id)


@internal
def _check_user(user: address) -> bool:
    return msg.sender == user or (self.authorized_proxies[msg.sender] and user == tx.origin)

@internal
def _validate_token_ids(
    offer: Offer,
    collateral_token_id: uint256,
    collection_status: CollectionStatus,
    collateral_proof: DynArray[bytes32, PROOF_MAX_SIZE]
):
    assert collection_status.contract != empty(address), "collateral not whitelisted"
    if offer.offer_type == OfferType.TOKEN:
        assert offer.token_id == collateral_token_id, "token id not in offer"
    elif offer.offer_type == OfferType.COLLECTION:
        assert collateral_token_id >= offer.token_range_min, "tokenid below offer range"
        assert collateral_token_id <= offer.token_range_max, "tokenid above offer range"
    else:
        _hash: bytes32 = keccak256(_abi_encode(collection_status.contract, offer.trait_hash, collateral_token_id))
        for p in collateral_proof:
            _hash = keccak256(_abi_encode(convert(keccak256(_hash), uint256) ^ convert(keccak256(p), uint256)))
        assert collection_status.trait_root == _hash, "proof invalid"

Read Contract

VERSION 0xffa1ad74 → string
authorized_proxies 0x96069a5f → bool
cryptopunks 0x5556eda9 → address
delegation_registry 0xe5107ed7 → address
loans 0xc4a90815 → bytes32
max_borrower_broker_settlement_fee 0x1b0b0c63 → uint256
max_lender_broker_settlement_fee 0xf8c26629 → uint256
max_protocol_settlement_fee 0x87c8f694 → uint256
max_protocol_upfront_fee 0x50d0d5ad → uint256
offer_count 0x225daa0f → uint256
onERC721Received 0x150b7a02 → bytes4
owner 0x8da5cb5b → address
p2p_control 0x7719e9ea → address
payment_token 0x331c6587 → address
pending_transfers 0x9274bbcd → uint256
proposed_owner 0x885753de → address
protocol_settlement_fee 0x61dd0b8b → uint256
protocol_upfront_fee 0x58e77837 → uint256
protocol_wallet 0x67db7499 → address
revoked_offers 0x61acb559 → bool

Write Contract 12 functions

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

change_protocol_wallet 0x72e76f13
address new_protocol_wallet
claim_defaulted_loan_collateral 0xdf19cbdb
tuple loan
claim_ownership 0xf7a79a02
No parameters
claim_pending_transfers 0x8199abaf
No parameters
create_loan 0xc48ac26e
tuple offer
uint256 collateral_token_id
bytes32[] collateral_proof
address delegate
uint256 borrower_broker_upfront_fee_amount
uint256 borrower_broker_settlement_fee_bps
address borrower_broker
returns: bytes32
propose_owner 0xbc71771d
address _address
replace_loan 0x7a7ab06f
tuple loan
tuple offer
bytes32[] collateral_proof
uint256 borrower_broker_upfront_fee_amount
uint256 borrower_broker_settlement_fee_bps
address borrower_broker
returns: bytes32
replace_loan_lender 0xaab4ffb9
tuple loan
tuple offer
bytes32[] collateral_proof
returns: bytes32
revoke_offer 0x5449d1d5
tuple offer
set_protocol_fee 0xc2a5022b
uint256 protocol_upfront_fee
uint256 protocol_settlement_fee
set_proxy_authorization 0x9544fa7c
address _proxy
bool _value
settle_loan 0x546dd516
tuple loan

Token Balances (1)

View Transfers →
USDC 0.004

Recent Transactions

No transactions found for this address