Address Contract Verified
Address
0xcBbf25BC6cccD5962388852d21343559f35918aA
Balance
0 ETH
Nonce
1
Code Size
13679 bytes
Creator
0xa68be02a...1b95 at tx 0xf6fc7a18...ea7d19
Indexed Transactions
0 (1 on-chain, 0.7% indexed)
Contract Bytecode
13679 bytes
0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816306fdde031461200257508063095ea7b314611f8057806316343da414611f6457806318160ddd14611f475780631c0fc3f614611f1f5780632203fddd14611ef057806323b872dd14611e2557806326ad3b5014611db9578063313ce56714611d9e5780633f4ba83a14611d03578063413d445a14611ccb578063427c37c314611cb1578063431cf41f14611c6d5780634eca400f14611bfb5780634f64b2be14611bc55780635228472914611b8d5780635abadfad14611b555780635c505a5614611af05780635c975abb14611acb5780635ec3a9b414611aa8578063616921e91461143957806362b27f5b1461141b57806362c06767146112e5578063636da418146112325780636664eb16146111ed578063691754f2146111985780636f88d64d1461117a57806370a0823114611142578063715018a6146110e557806372914f4f146110c157806375451b4f146110a557806379f20a9214610ff55780637a25f04514610fbc5780638456cb5914610f5957806385bb7b1f14610f3e5780638a440b5514610ddc5780638d8e09b614610db55780638da5cb5b14610d8c578063944349c914610d7157806395d89b4114610c6c57806399cb795314610c51578063a9059cbb14610c1f578063a9a694d914610c04578063abf2cbb014610b4b578063b32b4e2614610b12578063b7013d9a14610ac9578063bdbf6ff814610aad578063bf41233414610a8a578063c40f1eaa14610a61578063cc38368e14610a3a578063cc76bc4414610a1c578063ce6f68b2146109ee578063d02ceb51146109b5578063d450b2b014610973578063d4e20b0114610483578063d5b57ca614610465578063d6879d0a1461041f578063dd62ed3e146103bb578063e2cd77421461036d578063f2fde38b146102e45763f63aa40f146102bd575f80fd5b346102e157806003193601126102e157602061ffff600c5460a01c16604051908152f35b80fd5b50346102e15760203660031901126102e1576102fe61211d565b610306612b66565b6001600160a01b0316801561035957600580546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b631e4fbdf760e01b82526004829052602482fd5b50346102e15760203660031901126102e1577fac19ace99877772d1db79da469ca6b4c4beb90ce458aefdefaa1d0a10f733d1960206004356103ad612b66565b80600755604051908152a180f35b50346102e15760403660031901126102e1576103d561211d565b60243591906001600160a01b038316830361041b579060409160018060a01b031681526001602052209060018060a01b03165f52602052602060405f2054604051908152f35b5080fd5b50346102e15760203660031901126102e15761046161044461043f61211d565b612ad8565b604080519384526020840192909252908201529081906060820190565b0390f35b50346102e157806003193601126102e1576020600f54604051908152f35b50346102e15760603660031901126102e15736602312156102e1576040516104ac6060826121c4565b803660641161096f576004905b6064821061095f5750506104cb612e1b565b6104d3612e71565b338252601060205260408220546001810180911161094b576104f69043116124c6565b6104fe612eb8565b815b600381101561057857806105166001928461228a565b51610522575b01610500565b610573828060a01b0382600801541661053b838661228a565b5190604051916323b872dd60e01b602084015233602484015230604484015260648301526064825261056e6084836121c4565b613403565b61051c565b50815b600381101561060057806105916001928461228a565b5161059d575b0161057b565b6105fb81600801838060a01b03815416906105e0858060a01b037f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae69168093612ed2565b848060a01b03905416906105f4848761228a565b5191612f8c565b610597565b506040516370a0823160e01b81523060048201527f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b0316602082602481845afa9182156108d0578492610917575b5060405163d4e20b0160e01b8152610670600482018561216d565b602081606481887f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b03165af1801561090c576108db575b506020602491604051928380926370a0823160e01b82523060048301525afa80156108d05782908590610896575b6106e69250612217565b9081156108615760025480159183918315610849575050905b33156108355761070f8233613293565b670de0b6b3a764000061072961072361270e565b846121e6565b048061079d575b5061078d575b43600f55610747604051809461216d565b606083015260808201527fd3281a0e50078c381ef7ef4793a7bd232b3a04e001b2b2addfc13fd09c34d22360a03392a23381526010602052436040822055600160065580f35b6107956126cb565b600e55610736565b61082f903387526012602052604087206107b88282546122af565b9055338752601260205260408720546040519082825260208201527f1aa2067ce2e9db61a9d383bcf8fb5e0f8bc378d542a222db48bafba848c1763960403392a23387526013602052604087206040519161081283612194565b88835267ffffffffffffffff42166020840152604083015261251f565b5f610730565b63ec442f0560e01b85526004859052602485fd5b61085b92610856916121e6565b6121f9565b906106ff565b60405162461bcd60e51b815260206004820152600d60248201526c0574c3a206e6f20647366206c7609c1b6044820152606490fd5b50506020813d6020116108c8575b816108b1602093836121c4565b810103126108c457816106e691516106dc565b5f80fd5b3d91506108a4565b6040513d86823e3d90fd5b6020813d602011610904575b816108f4602093836121c4565b810103126108c4575060206106ae565b3d91506108e7565b6040513d87823e3d90fd5b9091506020813d602011610943575b81610933602093836121c4565b810103126108c45751905f610655565b3d9150610926565b634e487b7160e01b83526011600452602483fd5b81358152602091820191016104b9565b8280fd5b50346102e15760203660031901126102e157610461610993600435612a7f565b6040805194855260208501939093529183015260608201529081906080820190565b50346102e157806003193601126102e1576109ce612b66565b6109d6612e1b565b6109de612e71565b6109e66127ee565b600160065580f35b50346102e15760203660031901126102e1576060610a0d60043561271f565b610a1a604051809261216d565bf35b50346102e157806003193601126102e1576020600754604051908152f35b50346102e157806003193601126102e15760206001600160801b03600b5416604051908152f35b50346102e157806003193601126102e157600d546040516001600160a01b039091168152602090f35b50346102e157806003193601126102e1576020610aa561270e565b604051908152f35b50346102e157806003193601126102e157602060405160018152f35b50346102e15760203660031901126102e157610a0d6040606092610aeb61211d565b848351610af882826121c4565b3690376001600160a01b031681526020819052205461271f565b50346102e15760203660031901126102e1576020906040906001600160a01b03610b3a61211d565b168152601283522054604051908152f35b50346102e15760203660031901126102e15760043561ffff81169081810361096f57610b75612b66565b6127108211610bcc57600c805461ffff60a01b191660a09290921b61ffff60a01b169190911790556040519081527f4ce4c6a6258d4db07314f0d3c079f13b6ddc1c402ff05281ffe8c8fcb3fb8efa90602090a180f35b60405162461bcd60e51b815260206004820152601060248201526f0ae987440cccaca40e8dede40d0d2ced60831b6044820152606490fd5b50346102e157806003193601126102e1576020610aa56126e9565b50346102e15760403660031901126102e157610c46610c3c61211d565b6024359033612b28565b602060405160018152f35b50346102e157806003193601126102e1576020610aa56126cb565b50346102e157806003193601126102e1576040519080600454908160011c91600181168015610d67575b602084108114610d5357838652908115610d2c5750600114610ccf575b61046184610cc3818603826121c4565b604051918291826120f3565b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b808210610d1257509091508101602001610cc382610cb3565b919260018160209254838588010152019101909291610cf9565b60ff191660208087019190915292151560051b85019092019250610cc39150839050610cb3565b634e487b7160e01b83526022600452602483fd5b92607f1692610c96565b50346102e157806003193601126102e1576020610aa561268f565b50346102e157806003193601126102e1576005546040516001600160a01b039091168152602090f35b50346102e157806003193601126102e1576040610dd0612656565b82519182526020820152f35b50346102e15760203660031901126102e1576001600160a01b03610dfe61211d565b1680825260136020526040822054610e158161262a565b91610e2360405193846121c4565b818352601f19610e328361262a565b01845b818110610f15575050835b828110610eb1578385604051918291602083016020845282518091526020604085019301915b818110610e74575050500390f35b91935091602060606001926040875160ff815116835267ffffffffffffffff85820151168584015201516040820152019401910191849392610e66565b6001908286526013602052610ec9816040882061247b565b508260405191610ed883612194565b67ffffffffffffffff815460ff8116855260081c16602084015201546040820152610f038287612642565b52610f0e8186612642565b5001610e40565b602090604051610f2481612194565b878152878382015287604082015282828801015201610e35565b50346102e157806003193601126102e1576020610aa56125f7565b50346102e157806003193601126102e157610f72612b66565b610f7a612e71565b6005805460ff60a01b1916600160a01b1790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602090a180f35b50346102e15760203660031901126102e1576020906040906001600160a01b03610fe461211d565b168152601383522054604051908152f35b50346102e15760203660031901126102e15761100f61211d565b611017612b66565b6001600160a01b0316801561106d576020817f060ccc39d633083c9ad15095f5e4620811a410c5ad85aa634915c38d0f0a4798926bffffffffffffffffffffffff60a01b600c541617600c55604051908152a180f35b60405162461bcd60e51b815260206004820152601060248201526f574c3a2062616420747265617375727960801b6044820152606490fd5b50346102e157806003193601126102e157602060405160038152f35b50346102e15760203660031901126102e1576020610aa56110e061211d565b61259c565b50346102e157806003193601126102e1576110fe612b66565b600580546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346102e15760203660031901126102e1576020906040906001600160a01b0361116a61211d565b1681528083522054604051908152f35b50346102e157806003193601126102e1576020601154604051908152f35b50346102e15760203660031901126102e157602090670de0b6b3a7640000906111e4906001600160a01b036111cb61211d565b16815280845260409020546111de61270e565b906121e6565b04604051908152f35b50346102e157806003193601126102e1576040517f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b03168152602090f35b50346102e15760203660031901126102e1576004356001600160801b03811680910361041b57611260612b66565b60038110156112a9576020817fd625744a59a002e8dd5025bc59ecdaa84efd672455671393f389b8a85f43d37c926001600160801b0319600b541617600b55604051908152a180f35b60405162461bcd60e51b81526020600482015260146024820152730ae987440c4c2c840e6cae8e8d8ca40d2dcc8caf60631b6044820152606490fd5b50346102e1576112f436612133565b61130093919293612b66565b6001600160a01b038316156113e95791926001600160a01b031691815b60038110156113785760088101546001600160a01b031684146113425760010161131d565b60405162461bcd60e51b815260206004820152600e60248201526d2ba61d103837b7b6103a37b5b2b760911b6044820152606490fd5b5090927f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b031683146113b7576113b4926130b9565b80f35b60405162461bcd60e51b815260206004820152600a6024820152690574c3a20647366206c760b41b6044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269574c3a2062616420746f60b01b6044820152606490fd5b50346102e157806003193601126102e1576020600e54604051908152f35b50346108c45760c03660031901126108c4576004359036604312156108c4576060916040519161146984846121c4565b82366084116108c4576024905b60848210611a985750506084359060028210156108c45760a435926001600160801b0384168085036108c4576114aa612e1b565b6114b2612e71565b335f52601060205260405f205460018101809111611a84576114d59043116124c6565b6114de84612501565b60018403611a4b576114ee612eb8565b81151580611a36575b15611a0057670de0b6b3a764000061151061072361270e565b04335f52601260205260405f20548111155f146119ed5780915b6115348383612217565b90836119cf575b6002546040516370a0823160e01b81523060048201527f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae69916020826024816001600160a01b0387165afa91821561166d575f9261199b575b5080151580611992575b1561196157610856886115af936121e6565b97881561192a573315611917576115c6873361326f565b604051996115d48d8c6121c4565b8c368c375f5b60038110156116785760088101546040516370a0823160e01b81523060048201529190602090839060249082906001600160a01b03165afa801561166d578d925f91611635575b5061162e8260019461228a565b52016115da565b9250506020823d8211611665575b81611650602093836121c4565b810103126108c45790518c919061162e611621565b3d9150611643565b6040513d5f823e3d90fd5b506116c690828e9d6116c18d9e9c9d60018060a01b037f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae6916886116bc82809b612ed2565b612f8c565b61304b565b906116d081612501565b61190f575f915b803b156108c4578a5f60c49261170a968296604051988997889663616921e960e01b88526004880152602487019061216d565b61171381612501565b608485015260a48401525af1801561166d576118fa575b5060405197611739818a6121c4565b368937855b60038110156118135760088101546040516370a0823160e01b81523060048201526001600160a01b0390911690602081602481855afa9081156118085790888c9493928b926117ce575b50936117ad6117a5859361179e8560019961228a565b5190612217565b92839261228a565b52806117bc575b50500161173e565b6117c79133906130b9565b8a806117b4565b945050506020833d8211611800575b816117ea602093836121c4565b810103126108c45791518a9290886117ad611788565b3d91506117dd565b6040513d8b823e3d90fd5b507fcc0ec4ff73dddc4d894cd03fc663aff3917c71bd4cf7d8e7666269e12fcae23e9394506118e190876118968a43600f55338a52601360205261188060408b206040519061186182612194565b6001825267ffffffffffffffff4216602083015288604083015261251f565b604051928984526020840152604083019061216d565b7f5fca3b365487d12b87821c0bff15acbc002d86c9cff6af6ecbadeada7eeddb7060a03392a26040519384933397859094939260609260808301968352602083015260408201520152565b0390a23381526010602052436040822055600160065580f35b6119079196505f906121c4565b5f948861172a565b6001916116d7565b634b637e8f60e11b5f525f60045260245ffd5b60405162461bcd60e51b815260206004820152600f60248201526e574c3a207a65726f2072656465656d60881b6044820152606490fd5b60405162461bcd60e51b8152602060048201526009602482015268574c3a20656d70747960b81b6044820152606490fd5b5081151561159d565b9091506020813d6020116119c7575b816119b7602093836121c4565b810103126108c45751905f611593565b3d91506119aa565b335f52601260205260405f206119e6858254612217565b905561153b565b335f52601260205260405f20549161152a565b60405162461bcd60e51b815260206004820152600e60248201526d574c3a206261642073686172657360901b6044820152606490fd5b50335f525f6020528160405f205410156114f7565b60405162461bcd60e51b81526020600482015260116024820152702ba61d1037b7363c9037b73296b1b7b4b760791b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b8135815260209182019101611476565b346108c45760203660031901126108c4576020610aa5611ac661211d565b612494565b346108c4575f3660031901126108c457602060ff60055460a01c166040519015158152f35b346108c45760403660031901126108c4576001600160a01b03611b1161211d565b165f5260136020526060611b2a60243560405f2061247b565b506001815491015467ffffffffffffffff6040519260ff8116845260081c1660208301526040820152f35b346108c4575f3660031901126108c4576020611b84611b72612b8d565b6001600160801b03600b54169061228a565b51604051908152f35b346108c45760203660031901126108c4576001600160a01b03611bae61211d565b165f526010602052602060405f2054604051908152f35b346108c45760203660031901126108c45760043560038110156108c457600801546040516001600160a01b039091168152602090f35b346108c45760203660031901126108c4576101c0611c50611c5a611c65611c28611c2361211d565b6122bc565b989560409a94929591979a519a8b5260208b015260408a01526060890152608088019061216d565b60e086019061216d565b61014084019061216d565b6101a0820152f35b346108c4575f3660031901126108c4576040517f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b03168152602090f35b346108c4575f3660031901126108c4576020610aa5612224565b346108c45760203660031901126108c4576001600160a01b03611cec61211d565b165f526012602052602060405f2054604051908152f35b346108c4575f3660031901126108c457611d1b612b66565b60055460ff8160a01c1615611d625760ff60a01b19166005556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346108c4575f3660031901126108c457602060405160128152f35b346108c45760203660031901126108c4577f9304853076fc5c433027e191edaaca8630c43664a04d2733c158d6df8616d56c6020611df561211d565b611dfd612b66565b600d80546001600160a01b0319166001600160a01b03929092169182179055604051908152a1005b346108c457611e3336612133565b6001600160a01b0383165f8181526001602090815260408083203384529091529020549093919291905f198110611e70575b50610c469350612b28565b838110611ed5578415611ec2573315611eaf57610c46945f52600160205260405f2060018060a01b0333165f526020528360405f209103905584611e65565b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b8390637dc7a0d960e11b5f523360045260245260445260645ffd5b346108c45760203660031901126108c4576020670de0b6b3a76400006111e4611f1761270e565b6004356121e6565b346108c4575f3660031901126108c457600c546040516001600160a01b039091168152602090f35b346108c4575f3660031901126108c4576020600254604051908152f35b346108c4575f3660031901126108c45760206040516127108152f35b346108c45760403660031901126108c457611f9961211d565b602435903315611ec2576001600160a01b0316908115611eaf57335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346108c4575f3660031901126108c4575f6003548060011c906001811680156120e9575b6020831081146120d5578285529081156120b15750600114612053575b61046183610cc3818503826121c4565b91905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f905b80821061209757509091508101602001610cc3612043565b91926001816020925483858801015201910190929161207f565b60ff191660208086019190915291151560051b84019091019150610cc39050612043565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612026565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b03821682036108c457565b60609060031901126108c4576004356001600160a01b03811681036108c457906024356001600160a01b03811681036108c4579060443590565b905f905b6003821061217e57505050565b6020806001928551815201930191019091612171565b6060810190811067ffffffffffffffff8211176121b057604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176121b057604052565b81810292918115918404141715611a8457565b8115612203570490565b634e487b7160e01b5f52601260045260245ffd5b91908203918211611a8457565b60025480158015612280575b61227b5761223c6126cb565b600e5480821115612274576127109261225b6122609261227094612217565b6121e6565b61ffff600c5460a01c16906121e6565b0490565b5050505f90565b505f90565b50600e5415612230565b90600381101561229b5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b91908201809211611a8457565b906060604051916122cd82846121c4565b81368437604051906122df83836121c4565b823683378194604051936122f381866121c4565b368537839160018060a01b03821696875f52601260205260405f2054935f5b600381101561240c5760088101546040516370a0823160e01b8152600481018c90526001600160a01b039091169190602081602481865afa90811561166d575f916123db575b50612363828a61228a565b5260208b604460405180958193636eb1769f60e11b835260048301523060248301525afa801561166d575f906123a9575b600192506123a2828b61228a565b5201612312565b506020823d82116123d3575b816123c2602093836121c4565b810103126108c45760019151612394565b3d91506123b5565b90506020813d8211612404575b816123f5602093836121c4565b810103126108c457515f612358565b3d91506123e8565b50919397959092968731965f525f60205260405f205495861561246857505050670de0b6b3a764000061244661244061270e565b866121e6565b049461246561245f6124588899612494565b80986122af565b9561271f565b93565b5f98508897889750919590945091925090565b805482101561229b575f5260205f209060011b01905f90565b6001600160a01b03165f90815260208190526040902054801561227b57612270670de0b6b3a7640000916111de61268f565b156124cd57565b60405162461bcd60e51b815260206004820152600c60248201526b2ba61d1031b7b7b63237bbb760a11b6044820152606490fd5b6002111561250b57565b634e487b7160e01b5f52602160045260245ffd5b8054680100000000000000008110156121b0576125419160018201815561247b565b9190916125895760408160ff60019351168454908060ff19831617865568ffffffffffffffff00602084015160081b169168ffffffffffffffffff1916171784550151910155565b634e487b7160e01b5f525f60045260245ffd5b6001600160a01b03165f8181526020819052604090205480156125f1576125ce670de0b6b3a7640000916111de61270e565b04905f52601260205260405f20548082115f146125f1576125ee91612217565b90565b50505f90565b6125ff6130f5565b801561227b5761ffff600c5460a01c1661271003906127108211611a845761271091612270916121e6565b67ffffffffffffffff81116121b05760051b60200190565b805182101561229b5760209160051b010190565b61265e6130f5565b80156126885761268561271061267d61ffff600c5460a01c16846121e6565b048092612217565b91565b505f905f90565b600254801561227b576126a06125f7565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715611a84576125ee916121f9565b600254801561227b576125ee906126e3611b72612b8d565b516121f9565b6126f4611b72612b8d565b516126fd612224565b808211156125f1576125ee91612217565b600254801561227b576126a06126e9565b906040519161272f6060846121c4565b60603684378281156127e95750612744612b8d565b906002549081156127e4576127646001600160801b03600b54168461228a565b5161276d612224565b81156127d657808211156127dd576127859082612217565b80156127d6575f5b6003811061279d57505050505050565b806127c5866108566127bf87610856886127b96001998f61228a565b516121e6565b886121e6565b6127cf828b61228a565b520161278d565b5050505050565b505f612785565b505050565b925050565b600f54431115612a485760025415612a42576128086126cb565b600e5415612a3d5761271081028181046127101482151715611a8457600e54600754612710018061271011611a8457612840916121e6565b11612a3a5761284d612224565b908115612a34576040516370a0823160e01b8152306004820152917f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b0381169290602085602481875afa94851561166d575f95612a00575b506128b8611b72612b8d565b5194851580156129f8575b6129ee576128d9602496610856602093876121e6565b94604051968780926370a0823160e01b82523060048301525afa801561166d575f9061299c575b7f503499d11fb8707d7ece7d653a90f736146427aa9fde77ef9bf5ca3c7d420cf29550808511612992575b509061295b918480612960575b505080600e55604051938493846040919493926060820195825260208201520152565b0390a1565b600c54612978926001600160a01b03909116906130b9565b612984836011546122af565b60115543600f555f84612938565b935061295b61292b565b506020853d6020116129e6575b816129b6602093836121c4565b810103126108c4577f503499d11fb8707d7ece7d653a90f736146427aa9fde77ef9bf5ca3c7d420cf29451612900565b3d91506129a9565b5050600e55505050565b5080156128c3565b9094506020813d602011612a2c575b81612a1c602093836121c4565b810103126108c45751935f6128ac565b3d9150612a0f565b600e5550565b50565b600e55565b5f600e55565b60405162461bcd60e51b815260206004820152600f60248201526e15d30e881b985d881d1bdd58da1959608a1b6044820152606490fd5b612a8a611b72612b8d565b51908192612a96612224565b9283811115612ad157612aa98482612217565b925b83600254928315612ac657505090610856846125ee936121e6565b965093505f92915050565b5f92612aab565b9060018060a01b0382165f525f60205260405f20548015612b1e57612b08670de0b6b3a7640000916111de61270e565b04906125ee612b178394612494565b80936122af565b505f915081908190565b91906001600160a01b03831615611917576001600160a01b03811615612b5357612b519261329e565b565b63ec442f0560e01b5f525f60045260245ffd5b6005546001600160a01b03163303612b7a57565b63118cdaa760e01b5f523360045260245ffd5b604051906060612b9d81846121c4565b803684376040516370a0823160e01b8152306004820152836020826024817f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b03165afa91821561166d575f92612de7575b508115612de15750604051637701efe560e11b81527f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b031690602081600481855afa801561166d5784915f91612dac575b50602460405180948193631526fe2760e01b835260048301525afa90811561166d575f935f92612d67575b505080156127e457670de0b6b3a76400008202918204670de0b6b3a764000003611a8457612ca5916121f9565b8015612d6357670de0b6b3a76400008111612d54575b5f5b60038110612cca57505050565b604051630f1c89b960e21b8152600481018390526001600160801b0382166024820152906020826044816001600160a01b0388165afa801561166d575f90612d22575b60019250612d1b828861228a565b5201612cbd565b506020823d8211612d4c575b81612d3b602093836121c4565b810103126108c45760019151612d0d565b3d9150612d2e565b50670de0b6b3a7640000612cbb565b5050565b8194508092503d8311612da5575b612d7f81836121c4565b810103126108c4578151916001600160a01b03831683036108c457604001515f80612c78565b503d612d75565b9150506020813d602011612dd9575b81612dc8602093836121c4565b810103126108c4578390515f612c4d565b3d9150612dbb565b93505050565b9091506020813d602011612e13575b81612e03602093836121c4565b810103126108c45751905f612bf5565b3d9150612df6565b600260065414612e2c576002600655565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60ff60055460a01c16612e8057565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b600f54431115612b515760025415612a42576128086126cb565b60405163095ea7b360e01b60208083019182526001600160a01b03909416602483018190525f60448085018290528452909492939291612f136064866121c4565b84519082855af15f513d82612f67575b505015612f2f57505050565b61056e612b51936040519063095ea7b360e01b602083015260248201525f604482015260448152612f616064826121c4565b82613403565b909150612f8457506001600160a01b0381163b15155b5f80612f23565b600114612f7d565b60405163095ea7b360e01b60208083019182526001600160a01b0385166024840152604480840196909652948252929390925f90612fcb6064866121c4565b84519082855af15f513d82613026575b505015612fe757505050565b60405163095ea7b360e01b60208201526001600160a01b0390931660248401525f6044808501919091528352612b519261056e90612f616064826121c4565b90915061304357506001600160a01b0381163b15155b5f80612fdb565b60011461303c565b909291600160405161305e6060826121c4565b60603682379461306d81612501565b036130b4575f5b6003811061309f57506130946001600160801b0361309c9216809361228a565b51918461228a565b52565b805f6130ad6001938861228a565b5201613074565b509150565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152612b519161056e6064836121c4565b600d546001600160a01b0316801561227b5760206004916040519283809263685163b360e01b82525afa90811561166d575f9161323d575b50801561227b576040516370a0823160e01b81523060048201527f00000000000000000000000022586ea4fdaa9ef012581109b336f0124530ae696001600160a01b031691602082602481865afa91821561166d575f92613208575b50602060049293604051938480926318160ddd60e01b82525afa91821561166d575f926131d4575b50821580156131cc575b612274576125ee92610856916121e6565b5081156131bb565b9091506020813d602011613200575b816131f0602093836121c4565b810103126108c45751905f6131b1565b3d91506131e3565b91506020823d602011613235575b81613223602093836121c4565b810103126108c4579051906020613189565b3d9150613216565b90506020813d602011613267575b81613258602093836121c4565b810103126108c457515f61312d565b3d915061324b565b906001600160a01b03821615613289575f612b519261345b565b5f612b519261345b565b90612b51915f61345b565b6001600160a01b03811692919083156133f9576001600160a01b0382169384156133ee57612b5194846132d3575b505061345b565b815f525f60205260405f2054825f52601260205260405f205490801515806133e5575b613399575b5050670de0b6b3a764000061331761331161270e565b876121e6565b0480613324575b506132cc565b613391925f52601360205260405f209161336567ffffffffffffffff4216936040519061335082612194565b6002825285602083015284604083015261251f565b5f52601360205260405f20906040519261337e84612194565b600384526020840152604083015261251f565b5f808061331e565b6133a79061085688846121e6565b9081156132fb57816133b891612217565b835f52601260205260405f2055815f5260126020526133dc60405f209182546122af565b90555f806132fb565b508115156132f6565b50612b51935061345b565b612b51935061345b565b905f602091828151910182855af11561166d575f513d61345257506001600160a01b0381163b155b6134325750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561342b565b6001600160a01b031690816134d45760207fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9161349a856002546122af565b6002555b6001600160a01b031693846134bf5780600254036002555b604051908152a3565b845f525f825260405f208181540190556134b6565b815f525f60205260405f205483811061351e577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9184602092855f525f84520360405f205561349e565b91905063391434e360e21b5f5260045260245260445260645ffdfea264697066735822122019c0d5c1e086f8c4c69f4df105a58761e274f25e55397a909a07c5c4959a8f9164736f6c634300081e0033
Verified Source Code Full Match
Compiler: v0.8.30+commit.73712a01
EVM: prague
Optimization: Yes (200 runs)
wl_Mk6en.sol 852 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/*──────────────────────────────────────────────────────────────────────────────
* DSF White-Label Vault (WL) — accounting of the deposit principal in USDT (settleIndex)
*
* Public methods are identical to DSF by signatures:
* - deposit(uint256[POOL_ASSETS] memory amounts)
* - withdraw(uint256 lpShares, uint256[POOL_ASSETS] memory tokenAmounts,
* IStrategy.WithdrawalType withdrawalType, uint128 tokenIndex)
*
* Calculation order:
* 1) totalGrossSettle = previewTotalGrossPerToken()[settleIndex] for the entire WL
* 2) pendingVaultFeeSettle = uncrystallized HWM fee of the WL
* 3) totalHoldingsSettle = totalGrossSettle - pendingVaultFeeSettle
* 4) LPprice = totalHoldingsSettle / totalSupply
* 5) userNet = wlShares * LPprice
*
* Principal accounting (in USDT):
* - Deposit: userPrincipalSettle += availableToWithdrawal(wlMint)
* - Withdrawal: we compute availableToWithdrawal(wlShares),
* split it into principal / profit, decrease userPrincipalSettle
* only by the “principal” part.
* - Transfer: proportionally move part of userPrincipalSettle from → to.
*
* User transaction history (always in the settle token, e.g. USDT):
* kind:
* 0 = deposit (amountSettle = NET deposit in settle)
* 1 = withdrawal (amountSettle = NET withdrawal in settle)
* 2 = OUT transfer (from → to, amountSettle = NET settle-value of WL shares sent from `from`)
* 3 = IN transfer (to ← from, amountSettle = NET settle-value of WL shares received by `to`)
*
* Future income (CRV + CVX and others):
* - External contract futureIncomeSource provides futureIncome() — GROSS income
* for ALL DSF LP (for dsfLp.totalSupply()), in the settle token.
* - WL share (GROSS): futureGrossWL = futureTotal * dsfLpBalance(WL) / dsfLpTotalSupply
* - WL users’ share (NET of WL fee):
* totalFutureIncomeUsers = futureGrossWL * (DENOM - partnerFeeBps) / DENOM
* - User share:
* userFuture = totalFutureIncomeUsers * wlUser / totalSupply
*
* IMPORTANT: Slippage protection is fully implemented in the main DSF contracts.
* WL only forwards parameters.
*──────────────────────────────────────────────────────────────────────────────*/
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/* ── Mini-interfaces to the already deployed DSF ───────────────────────────── */
interface IDSF {
enum WithdrawalType { Base, OneCoin }
function deposit(uint256[3] calldata amounts) external returns (uint256 lpMinted);
function withdraw(
uint256 lpShares,
uint256[3] calldata minOut,
WithdrawalType wType,
uint128 tokenIndex
) external;
function defaultWithdrawPid() external view returns (uint256);
function poolInfo(uint256 pid)
external
view
returns (address strategy, uint256 startTime, uint256 lpShares);
}
// Only to keep enum in the signature identical to DSF
interface IStrategy {
enum WithdrawalType { Base, OneCoin }
}
// DSF Strategy: one-coin preview by LP share (1e18)
interface IStrategyView {
function calcWithdrawOneCoin(uint256 lpShareRatio1e18, uint128 tokenIndex)
external
view
returns (uint256 tokenAmount);
}
/**
* @dev External contract that provides total future income for DSF LP (CRV+CVX etc.)
* in the settle token (USDT).
*
* Semantics:
* - futureIncome() = future income for the entire dsfLp.totalSupply() (GROSS).
*/
interface IFutureIncome {
function futureIncome() external view returns (uint256);
}
contract DsfWhiteLabelVault_USDT is ERC20, Ownable, Pausable, ReentrancyGuard {
using SafeERC20 for IERC20;
// ───────── Constants / parameters
uint8 public constant POOL_ASSETS = 3;
uint256 public constant DENOM = 10_000; // bps
uint256 public constant USER_COOLDOWN_BLOCKS = 1;
/// @notice Threshold for HWM-skim, GROSS WL price growth in bps
uint256 public minSkimGainBps = 1; // 0.01%
// ───────── External addresses
IDSF public immutable dsf;
IERC20 public immutable dsfLp;
IERC20[POOL_ASSETS] public tokens; // [DAI, USDC, USDT]
/// @notice Index of the settle token (0..2) — by default USDT
uint128 public settleIndex = 2;
/// @notice Treasury of the WL owner (receives fee in DSF LP)
address public principalTreasury;
/// @notice WL owner fee (HWM fee), in bps (e.g., 3000 = 30%)
uint16 public partnerFeeBps;
/// @notice External source of future income (in the settle token)
IFutureIncome public futureIncomeSource;
// ───────── Accounting / anti-MEV
uint256 public HWM_settlePerWL; // high-water mark (GROSS settle per WL)
uint256 public lastNavTouchBlock; // NAV protection within a single block
mapping(address => uint256) public lastUserActionBlock; // per-user cooldown
/// @notice Total crystallized fee (in settle token equivalent) — analytics only
uint256 public ownerCrystallizedSettle;
/// @notice User deposit principal (“body”) in the settle token (NET)
mapping(address => uint256) public userPrincipalSettle;
// ───────── User operation history (always in the settle token)
struct TxRecord {
uint8 kind; // 0=deposit, 1=withdraw, 2=transferOut, 3=transferIn
uint64 timestamp; // block.timestamp
uint256 amountSettle; // amount in settle token (USDT equivalent), NET at operation time
}
mapping(address => TxRecord[]) private _userTxHistory;
// ───────── Events
event DepositWL(address indexed user, uint256[3] amounts, uint256 wlMinted, uint256 dsfLpGained);
event WithdrawWL(address indexed user, uint256 wlBurned, uint256 dsfLpSpent, uint256[3] paidToUser);
event SkimPrincipal(uint256 feeSettle, uint256 dsfLpFeeToPrincipal, uint256 newHwmPriceSettlePerWL);
event UpdatePartnerFeeBps(uint16 newBps);
event UpdatePrincipalTreasury(address newTreasury);
event UpdateMinSkimGainBps(uint256 newMinBps);
event UpdateSettleIndex(uint128 newIndex);
event UpdateFutureIncomeSource(address newSource);
event PrincipalSettleIncreased(address indexed user, uint256 addedSettle, uint256 newPrincipalSettle);
event WithdrawBreakdownSettle(
address indexed user,
uint256 wlBurned,
uint256 previewSettleOutNet,
uint256 principalSettlePortion,
uint256 profitSettlePortion
);
constructor(
address _dsf,
address _dsfLp,
address[POOL_ASSETS] memory _tokens,
address _principalTreasury,
uint16 _partnerFeeBps,
uint128 _settleIndex,
address _futureIncomeSource // can be address(0) if not configured yet
)
Ownable(msg.sender)
ERC20("DSF WL Shares", "DSFWL")
{
require(_dsf != address(0) && _dsfLp != address(0), "WL: bad DSF addrs");
require(_principalTreasury != address(0), "WL: bad treasury");
require(_partnerFeeBps <= DENOM, "WL: fee too high");
require(_settleIndex < POOL_ASSETS, "WL: bad settle index");
dsf = IDSF(_dsf);
dsfLp = IERC20(_dsfLp);
principalTreasury = _principalTreasury;
partnerFeeBps = _partnerFeeBps;
settleIndex = _settleIndex;
for (uint i = 0; i < POOL_ASSETS; i++) {
require(_tokens[i] != address(0), "WL: bad token");
tokens[i] = IERC20(_tokens[i]);
}
if (_futureIncomeSource != address(0)) {
futureIncomeSource = IFutureIncome(_futureIncomeSource);
}
HWM_settlePerWL = 0; // will be set on first deposit
}
// ───────── Admin settings
function setPartnerFeeBps(uint16 bps) external onlyOwner {
require(bps <= DENOM, "WL: fee too high");
partnerFeeBps = bps;
emit UpdatePartnerFeeBps(bps);
}
function setPrincipalTreasury(address t) external onlyOwner {
require(t != address(0), "WL: bad treasury");
principalTreasury = t;
emit UpdatePrincipalTreasury(t);
}
function setMinSkimGainBps(uint256 bps) external onlyOwner {
minSkimGainBps = bps;
emit UpdateMinSkimGainBps(bps);
}
/// Recommendation: change settleIndex only when WL is empty (require can be uncommented)
function setSettleIndex(uint128 idx) external onlyOwner {
require(idx < POOL_ASSETS, "WL: bad settle index");
// require(totalSupply() == 0, "WL: change settleIndex only when empty");
settleIndex = idx;
emit UpdateSettleIndex(idx);
}
/// @notice Set external source of future income (DSF / strategy / analytics)
function setFutureIncomeSource(address src) external onlyOwner {
// address(0) — disable future income accounting
futureIncomeSource = IFutureIncome(src);
emit UpdateFutureIncomeSource(src);
}
function pause() external onlyOwner { _pause(); }
function unpause() external onlyOwner { _unpause(); }
// ───────── Internal utilities / cooldown
function _touchNav() internal {
lastNavTouchBlock = block.number;
}
modifier userCooldown() {
require(block.number > lastUserActionBlock[msg.sender] + USER_COOLDOWN_BLOCKS, "WL: cooldown");
_;
lastUserActionBlock[msg.sender] = block.number;
}
function _amountsMask(
uint256[POOL_ASSETS] memory amounts,
IStrategy.WithdrawalType wType,
uint128 tokenIdx
) internal pure returns (uint256[POOL_ASSETS] memory out) {
if (wType == IStrategy.WithdrawalType.OneCoin) {
for (uint i = 0; i < POOL_ASSETS; i++) out[i] = 0;
out[uint256(tokenIdx)] = amounts[uint256(tokenIdx)];
} else {
return amounts;
}
}
// ───────── NAV / GROSS / NET (preview through DSF strategy)
/// @dev GROSS preview: how much of each token [0..2] WL would receive
/// if it withdrew ALL dsfLp as one-coin via defaultWithdrawPid.
function _previewTotalGrossPerToken()
internal
view
returns (uint256[POOL_ASSETS] memory grossAll)
{
uint256 lpBalance = dsfLp.balanceOf(address(this));
if (lpBalance == 0) return grossAll;
uint256 pid = dsf.defaultWithdrawPid();
(address strat,, uint256 poolLpShares) = dsf.poolInfo(pid);
if (poolLpShares == 0) return grossAll;
uint256 ratio1e18 = (lpBalance * 1e18) / poolLpShares;
if (ratio1e18 == 0) return grossAll;
if (ratio1e18 > 1e18) ratio1e18 = 1e18;
for (uint i = 0; i < POOL_ASSETS; i++) {
grossAll[i] = IStrategyView(strat).calcWithdrawOneCoin(ratio1e18, uint128(i));
}
}
/// @dev GROSS NAV of WL in the settle token (before WL fee)
function totalGrossSettle() public view returns (uint256) {
uint256[POOL_ASSETS] memory grossAll = _previewTotalGrossPerToken();
return grossAll[settleIndex];
}
/// @dev GROSS price of 1 WL share in the settle token
function wlPriceGrossSettle() public view returns (uint256) {
uint256 ts = totalSupply();
if (ts == 0) return 0;
uint256 g = totalGrossSettle();
return g / ts;
}
/// @dev Uncrystallized WL HWM fee (in the settle token)
function pendingVaultFeeSettle() public view returns (uint256) {
uint256 ts = totalSupply();
if (ts == 0 || HWM_settlePerWL == 0) return 0;
uint256 priceGross = wlPriceGrossSettle();
if (priceGross <= HWM_settlePerWL) return 0;
uint256 profit = (priceGross - HWM_settlePerWL) * ts;
return (profit * partnerFeeBps) / DENOM;
}
/// @notice NET total holdings of all WL users in the settle token
function totalHoldingsSettle() public view returns (uint256) {
uint256 g = totalGrossSettle();
uint256 f = pendingVaultFeeSettle();
return g > f ? g - f : 0;
}
/// @notice NET price of 1 WL share (LPprice) in the settle token, 1e18 precision
function LPprice() public view returns (uint256) {
uint256 ts = totalSupply();
if (ts == 0) return 0;
return (totalHoldingsSettle() * 1e18) / ts;
}
/// @notice NET value of `wlShares` in the settle token (what can be withdrawn now)
function availableToWithdrawal(uint256 wlShares) public view returns (uint256) {
uint256 px = LPprice();
return (wlShares * px) / 1e18;
}
/// @notice NET value of `wlShares` expressed in each of the three tokens (DAI/USDC/USDT)
function availableToWithdrawalPerToken(uint256 wlShares)
public
view
returns (uint256[POOL_ASSETS] memory out)
{
if (wlShares == 0) return out;
uint256[POOL_ASSETS] memory grossAll = _previewTotalGrossPerToken();
uint256 ts = totalSupply();
if (ts == 0) return out;
uint256 grossSettle = grossAll[settleIndex];
uint256 feeSettle = pendingVaultFeeSettle();
if (grossSettle == 0) return out;
uint256 netSettleAll_ = grossSettle > feeSettle ? grossSettle - feeSettle : 0;
if (netSettleAll_ == 0) return out;
// netFactor ~= NET/GROSS in settle; apply it as a factor to all tokens
for (uint i = 0; i < POOL_ASSETS; i++) {
uint256 netAllToken = (grossAll[i] * netSettleAll_) / grossSettle;
out[i] = (wlShares * netAllToken) / ts;
}
}
// ───────── User views (current NAV / PnL)
function userAvailableNow(address user) external view returns (uint256) {
return availableToWithdrawal(balanceOf(user));
}
/// @notice Current NET user value per three tokens (DAI/USDC/USDT)
function userAvailableNowPerToken(address user)
external
view
returns (uint256[POOL_ASSETS] memory)
{
return availableToWithdrawalPerToken(balanceOf(user));
}
/// @notice Current user deposit principal in the settle token
function userDepositeNow(address user) external view returns (uint256) {
return userPrincipalSettle[user];
}
/// @notice Current NET profit of the user in the settle token (NAV - principal)
function userNetProfit(address user) external view returns (uint256) {
uint256 bal = balanceOf(user);
if (bal == 0) return 0;
uint256 totalSettle = availableToWithdrawal(bal);
uint256 principal = userPrincipalSettle[user];
return totalSettle > principal ? totalSettle - principal : 0;
}
// ───────── Future income (CRV+CVX+...), NET user income only
/// @dev Internal GROSS future income of WL (before WL fee), in the settle token
function _totalFutureIncomeGross() internal view returns (uint256) {
IFutureIncome src = futureIncomeSource;
if (address(src) == address(0)) return 0;
uint256 futureTotal = src.futureIncome(); // income for the entire dsfLp.totalSupply()
if (futureTotal == 0) return 0;
uint256 lpBalance = dsfLp.balanceOf(address(this));
uint256 lpTotalSupply = dsfLp.totalSupply();
if (lpBalance == 0 || lpTotalSupply == 0) return 0;
// GROSS WL share in the total future DSF LP income
return (futureTotal * lpBalance) / lpTotalSupply;
}
/// @notice NET future income of WL users (after WL fee), in the settle token
function totalFutureIncomeUsers() public view returns (uint256) {
uint256 gross = _totalFutureIncomeGross();
if (gross == 0) return 0;
// Users’ net share: (DENOM - partnerFeeBps) / DENOM
return (gross * (DENOM - partnerFeeBps)) / DENOM;
}
/// @notice Split of future income: users’ share and WL owner’s share (estimated)
function futureIncomeSplit()
external
view
returns (uint256 usersFutureSettle, uint256 ownerFutureSettle)
{
uint256 gross = _totalFutureIncomeGross();
if (gross == 0) return (0, 0);
ownerFutureSettle = (gross * partnerFeeBps) / DENOM;
usersFutureSettle = gross - ownerFutureSettle;
}
/// @notice NET future income per 1 WL share, 1e18 precision
function futureIncomePerShare1e18() public view returns (uint256) {
uint256 ts = totalSupply();
if (ts == 0) return 0;
return (totalFutureIncomeUsers() * 1e18) / ts;
}
/// @notice Future income of a specific user (NET, after WL fee), in the settle token
function userFutureIncome(address user) public view returns (uint256) {
uint256 wl = balanceOf(user);
if (wl == 0) return 0;
uint256 px = futureIncomePerShare1e18();
return (wl * px) / 1e18;
}
/// @notice totalWithFuture: current NET + future income for the user (all in settle token)
function userTotalWithFuture(address user)
external
view
returns (uint256 currentNet, uint256 futureNet, uint256 total)
{
uint256 wl = balanceOf(user);
if (wl == 0) return (0, 0, 0);
currentNet = availableToWithdrawal(wl);
futureNet = userFutureIncome(user);
total = currentNet + futureNet;
}
// ───────── User history
function getUserTxCount(address user) external view returns (uint256) {
return _userTxHistory[user].length;
}
function getUserTx(address user, uint256 index)
external
view
returns (uint8 kind, uint64 timestamp, uint256 amountSettle)
{
TxRecord storage r = _userTxHistory[user][index];
return (r.kind, r.timestamp, r.amountSettle);
}
/// Return the entire history (for off-chain reading)
function getUserTxHistory(address user) external view returns (TxRecord[] memory history) {
uint256 len = _userTxHistory[user].length;
history = new TxRecord[](len);
for (uint256 i = 0; i < len; i++) {
history[i] = _userTxHistory[user][i];
}
}
// ───────── soft auto-skim (crystallization of HWM fee in DSF LP)
function _trySkim() internal {
if (block.number <= lastNavTouchBlock) return;
uint256 ts = totalSupply();
if (ts == 0) { HWM_settlePerWL = 0; return; }
uint256 priceGross = wlPriceGrossSettle();
if (HWM_settlePerWL == 0) { HWM_settlePerWL = priceGross; return; }
if (priceGross * DENOM < HWM_settlePerWL * (DENOM + minSkimGainBps)) return;
uint256 feeSettle = pendingVaultFeeSettle();
if (feeSettle == 0) { HWM_settlePerWL = priceGross; return; }
uint256 lpBalance = dsfLp.balanceOf(address(this));
uint256 grossSettle = totalGrossSettle();
if (grossSettle == 0 || lpBalance == 0) { HWM_settlePerWL = priceGross; return; }
// feeLp = feeSettle * lpBalance / grossSettle
uint256 feeLp = (feeSettle * lpBalance) / grossSettle;
uint256 balLp = dsfLp.balanceOf(address(this));
if (feeLp > balLp) feeLp = balLp;
if (feeLp > 0) {
dsfLp.safeTransfer(principalTreasury, feeLp);
ownerCrystallizedSettle += feeSettle;
_touchNav();
}
HWM_settlePerWL = priceGross;
emit SkimPrincipal(feeSettle, feeLp, priceGross);
}
// ───────── DEPOSIT (signature identical to DSF)
function deposit(uint256[POOL_ASSETS] memory amounts)
external
nonReentrant
whenNotPaused
userCooldown
{
_trySkim();
// 1) Pull tokens from the user
for (uint i = 0; i < POOL_ASSETS; i++) {
if (amounts[i] > 0) {
tokens[i].safeTransferFrom(msg.sender, address(this), amounts[i]);
}
}
// 2) Approve DSF and call deposit
for (uint i = 0; i < POOL_ASSETS; i++) {
if (amounts[i] > 0) {
tokens[i].forceApprove(address(dsf), 0);
tokens[i].forceApprove(address(dsf), amounts[i]);
}
}
uint256 lpBefore = dsfLp.balanceOf(address(this));
dsf.deposit(amounts); // slippage checks are inside DSF
uint256 lpAfter = dsfLp.balanceOf(address(this));
uint256 lpGained = lpAfter - lpBefore;
require(lpGained > 0, "WL: no dsf lp");
// 3) Mint WL shares proportionally to DSF LP
uint256 tsBefore = totalSupply();
uint256 wlMint;
if (tsBefore == 0) {
wlMint = lpGained; // start 1:1 WL↔LP
} else {
wlMint = (tsBefore * lpGained) / lpBefore;
}
_mint(msg.sender, wlMint);
// 4) Fix deposit principal in the settle token at CURRENT NET price
uint256 addedPrincipalSettle = availableToWithdrawal(wlMint);
if (addedPrincipalSettle > 0) {
userPrincipalSettle[msg.sender] += addedPrincipalSettle;
emit PrincipalSettleIncreased(msg.sender, addedPrincipalSettle, userPrincipalSettle[msg.sender]);
// History: deposit (kind = 0), amountSettle = NET deposit in settle
_userTxHistory[msg.sender].push(
TxRecord({
kind: 0,
timestamp: uint64(block.timestamp),
amountSettle: addedPrincipalSettle
})
);
}
// 5) If this is the first deposit — set HWM by GROSS price
if (tsBefore == 0) {
HWM_settlePerWL = wlPriceGrossSettle();
}
_touchNav();
emit DepositWL(msg.sender, amounts, wlMint, lpGained);
}
// ───────── WITHDRAW (signature identical to DSF)
/// @param lpShares Amount of WL shares to burn (name kept for DSF compatibility)
/// @param tokenAmounts In DSF this is minOut per token — forwarded as is
/// @param withdrawalType DSF mode: Base/OneCoin — forwarded as is
/// @param tokenIndex Index for OneCoin — forwarded as is (0..2)
function withdraw(
uint256 lpShares,
uint256[POOL_ASSETS] memory tokenAmounts,
IStrategy.WithdrawalType withdrawalType,
uint128 tokenIndex
)
external
nonReentrant
whenNotPaused
userCooldown
{
require(
withdrawalType == IStrategy.WithdrawalType.OneCoin,
"WL: only one-coin"
);
_trySkim();
require(lpShares > 0 && balanceOf(msg.sender) >= lpShares, "WL: bad shares");
// NET preview in the settle token for these WL shares
uint256 previewSettleOutNet = availableToWithdrawal(lpShares);
// Split into principal/profit in the user’s accounting
uint256 principalSettlePortion =
previewSettleOutNet <= userPrincipalSettle[msg.sender]
? previewSettleOutNet
: userPrincipalSettle[msg.sender];
uint256 profitSettlePortion = previewSettleOutNet - principalSettlePortion;
if (principalSettlePortion > 0) {
userPrincipalSettle[msg.sender] -= principalSettlePortion;
}
uint256 tsBefore = totalSupply();
uint256 lpBalBefore = dsfLp.balanceOf(address(this));
require(tsBefore > 0 && lpBalBefore > 0, "WL: empty");
// Proportional WL → DSF LP conversion
uint256 dsfLpRedeem = (lpBalBefore * lpShares) / tsBefore;
require(dsfLpRedeem > 0, "WL: zero redeem");
_burn(msg.sender, lpShares);
uint256[POOL_ASSETS] memory balBefore;
for (uint i = 0; i < POOL_ASSETS; i++) {
balBefore[i] = IERC20(tokens[i]).balanceOf(address(this));
}
dsfLp.forceApprove(address(dsf), 0);
dsfLp.forceApprove(address(dsf), dsfLpRedeem);
// All slippage and minOut conditions are inside DSF
dsf.withdraw(
dsfLpRedeem,
_amountsMask(tokenAmounts, withdrawalType, tokenIndex),
withdrawalType == IStrategy.WithdrawalType.Base ? IDSF.WithdrawalType.Base : IDSF.WithdrawalType.OneCoin,
tokenIndex
);
uint256[POOL_ASSETS] memory got;
for (uint i = 0; i < POOL_ASSETS; i++) {
uint256 balAfter = IERC20(tokens[i]).balanceOf(address(this));
uint256 delta = balAfter - balBefore[i];
got[i] = delta;
if (delta > 0) tokens[i].safeTransfer(msg.sender, delta);
}
_touchNav();
// History: withdraw (kind = 1), log NET in settle token
_userTxHistory[msg.sender].push(
TxRecord({
kind: 1,
timestamp: uint64(block.timestamp),
amountSettle: previewSettleOutNet
})
);
emit WithdrawWL(msg.sender, lpShares, dsfLpRedeem, got);
emit WithdrawBreakdownSettle(
msg.sender,
lpShares,
previewSettleOutNet,
principalSettlePortion,
profitSettlePortion
);
}
// ───────── Override transfer logic to account principal and history
/// ERC20 v5: _update is called on mint/burn/transfer
function _update(address from, address to, uint256 value) internal override {
// Mint
if (from == address(0)) {
super._update(from, to, value);
return;
}
// Burn
if (to == address(0)) {
super._update(from, to, value);
return;
}
// Regular transfer: move proportional principal + log history in settle token
if (value > 0) {
uint256 fromBalanceBefore = balanceOf(from);
uint256 fromPrincipal = userPrincipalSettle[from];
// Proportional principal transfer from `from` → `to`
if (fromBalanceBefore > 0 && fromPrincipal > 0) {
uint256 movedPrincipal = (fromPrincipal * value) / fromBalanceBefore;
if (movedPrincipal > 0) {
userPrincipalSettle[from] = fromPrincipal - movedPrincipal;
userPrincipalSettle[to] += movedPrincipal;
}
}
// Log transfer volume in the settle token at current NET WL price
uint256 transferNetSettle = availableToWithdrawal(value);
if (transferNetSettle > 0) {
// OUT - from
_userTxHistory[from].push(
TxRecord({
kind: 2,
timestamp: uint64(block.timestamp),
amountSettle: transferNetSettle
})
);
// IN - to
_userTxHistory[to].push(
TxRecord({
kind: 3,
timestamp: uint64(block.timestamp),
amountSettle: transferNetSettle
})
);
}
}
super._update(from, to, value);
}
// ───────── Manual HWM-skim
function skimPrincipalFeeInDsfLp() external onlyOwner nonReentrant whenNotPaused {
require(block.number > lastNavTouchBlock, "WL: nav touched");
uint256 ts = totalSupply();
if (ts == 0) { HWM_settlePerWL = 0; return; }
uint256 priceGross = wlPriceGrossSettle();
if (HWM_settlePerWL == 0) { HWM_settlePerWL = priceGross; return; }
if (priceGross * DENOM < HWM_settlePerWL * (DENOM + minSkimGainBps)) return;
uint256 feeSettle = pendingVaultFeeSettle();
if (feeSettle == 0) { HWM_settlePerWL = priceGross; return; }
uint256 lpBalance = dsfLp.balanceOf(address(this));
uint256 grossSettle = totalGrossSettle();
if (grossSettle == 0 || lpBalance == 0) { HWM_settlePerWL = priceGross; return; }
uint256 feeLp = (feeSettle * lpBalance) / grossSettle;
uint256 balLp = dsfLp.balanceOf(address(this));
if (feeLp > balLp) feeLp = balLp;
if (feeLp > 0) {
dsfLp.safeTransfer(principalTreasury, feeLp);
ownerCrystallizedSettle += feeSettle;
_touchNav();
}
HWM_settlePerWL = priceGross;
emit SkimPrincipal(feeSettle, feeLp, priceGross);
}
// ───────── emergency sweep (tokens other than DSF LP and pool tokens)
function sweep(address token, address to, uint256 amount) external onlyOwner {
require(to != address(0), "WL: bad to");
for (uint i = 0; i < POOL_ASSETS; i++) {
require(token != address(tokens[i]), "WL: pool token");
}
require(token != address(dsfLp), "WL: dsf lp");
IERC20(token).safeTransfer(to, amount);
}
// ───────── Debug view for calculations
function debugPreviewAllAndUser(uint256 wlShares)
external
view
returns (uint256 grossAll, uint256 feeUncrystallized, uint256 netAll, uint256 userNet)
{
grossAll = totalGrossSettle();
feeUncrystallized = pendingVaultFeeSettle();
netAll = grossAll > feeUncrystallized ? grossAll - feeUncrystallized : 0;
if (totalSupply() == 0) return (grossAll, feeUncrystallized, netAll, 0);
userNet = (wlShares * netAll) / totalSupply();
}
/// @notice Full snapshot for a user:
/// - principalSettle: deposit principal (“body”) in the settle token
/// - netNowSettle: current NET (NAV) in the settle token, if fully withdrawn now
/// - futureIncomeSettle: user’s future income (CRV+CVX+...) in the settle token, already NET
/// - totalWithFutureSettle: netNow + futureIncome
/// - perTokenNet: current NET per each of the three tokens [DAI, USDC, USDT]
/// - walletTokenBalances: user wallet balances in [DAI, USDC, USDT]
/// - walletTokenAllowances: user allowances for this contract in [DAI, USDC, USDT]
/// - ethBalance: user’s ETH balance
function userFullView(address user)
external
view
returns (
uint256 principalSettle,
uint256 netNowSettle,
uint256 futureIncomeSettle,
uint256 totalWithFutureSettle,
uint256[POOL_ASSETS] memory perTokenNet,
uint256[POOL_ASSETS] memory walletTokenBalances,
uint256[POOL_ASSETS] memory walletTokenAllowances,
uint256 ethBalance
)
{
principalSettle = userPrincipalSettle[user];
// Wallet balances and allowances per the three tokens
for (uint256 i = 0; i < POOL_ASSETS; i++) {
IERC20 t = tokens[i];
walletTokenBalances[i] = t.balanceOf(user);
walletTokenAllowances[i] = t.allowance(user, address(this));
}
// ETH balance
ethBalance = user.balance;
uint256 wl = balanceOf(user);
if (wl == 0) {
// perTokenNet is zero-initialized by default
return (
principalSettle,
0,
0,
0,
perTokenNet,
walletTokenBalances,
walletTokenAllowances,
ethBalance
);
}
// Current NET in the settle token
netNowSettle = availableToWithdrawal(wl);
// Future user yield (NET, after WL fee) in the settle token
futureIncomeSettle = userFutureIncome(user);
// Total potential (current + future)
totalWithFutureSettle = netNowSettle + futureIncomeSettle;
// NET per each of the three tokens
perTokenNet = availableToWithdrawalPerToken(wl);
}
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
Pausable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
SafeERC20.sol 212 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
ERC20.sol 305 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Read Contract
DENOM 0x16343da4 → uint256
HWM_settlePerWL 0x62b27f5b → uint256
LPprice 0xbf412334 → uint256
POOL_ASSETS 0x75451b4f → uint8
USER_COOLDOWN_BLOCKS 0xbdbf6ff8 → uint256
allowance 0xdd62ed3e → uint256
availableToWithdrawal 0x2203fddd → uint256
availableToWithdrawalPerToken 0xce6f68b2 → uint256[3]
balanceOf 0x70a08231 → uint256
debugPreviewAllAndUser 0xd450b2b0 → uint256, uint256, uint256, uint256
decimals 0x313ce567 → uint8
dsf 0x6664eb16 → address
dsfLp 0x431cf41f → address
futureIncomePerShare1e18 0x944349c9 → uint256
futureIncomeSource 0xc40f1eaa → address
futureIncomeSplit 0x8d8e09b6 → uint256, uint256
getUserTx 0x5c505a56 → uint8, uint64, uint256
getUserTxCount 0x7a25f045 → uint256
getUserTxHistory 0x8a440b55 → tuple[]
lastNavTouchBlock 0xd5b57ca6 → uint256
lastUserActionBlock 0x52284729 → uint256
minSkimGainBps 0xcc76bc44 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
ownerCrystallizedSettle 0x6f88d64d → uint256
partnerFeeBps 0xf63aa40f → uint16
paused 0x5c975abb → bool
pendingVaultFeeSettle 0x427c37c3 → uint256
principalTreasury 0x1c0fc3f6 → address
settleIndex 0xcc38368e → uint128
symbol 0x95d89b41 → string
tokens 0x4f64b2be → address
totalFutureIncomeUsers 0x85bb7b1f → uint256
totalGrossSettle 0x5abadfad → uint256
totalHoldingsSettle 0xa9a694d9 → uint256
totalSupply 0x18160ddd → uint256
userAvailableNow 0x691754f2 → uint256
userAvailableNowPerToken 0xb7013d9a → uint256[3]
userDepositeNow 0xb32b4e26 → uint256
userFullView 0x4eca400f → uint256, uint256, uint256, uint256, uint256[3], uint256[3], uint256[3], uint256
userFutureIncome 0x5ec3a9b4 → uint256
userNetProfit 0x72914f4f → uint256
userPrincipalSettle 0x413d445a → uint256
userTotalWithFuture 0xd6879d0a → uint256, uint256, uint256
wlPriceGrossSettle 0x99cb7953 → uint256
Write Contract 16 functions
These functions modify contract state and require a wallet transaction to execute.
approve 0x095ea7b3
address spender
uint256 value
returns: bool
deposit 0xd4e20b01
uint256[3] amounts
pause 0x8456cb59
No parameters
renounceOwnership 0x715018a6
No parameters
setFutureIncomeSource 0x26ad3b50
address src
setMinSkimGainBps 0xe2cd7742
uint256 bps
setPartnerFeeBps 0xabf2cbb0
uint16 bps
setPrincipalTreasury 0x79f20a92
address t
setSettleIndex 0x636da418
uint128 idx
skimPrincipalFeeInDsfLp 0xd02ceb51
No parameters
sweep 0x62c06767
address token
address to
uint256 amount
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
withdraw 0x616921e9
uint256 lpShares
uint256[3] tokenAmounts
uint8 withdrawalType
uint128 tokenIndex
Recent Transactions
This address has 1 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →