Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x10705D774fBE0a3802d7a915E23F6f2c109Fd77f
Balance 0 ETH
Nonce 1
Code Size 14375 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

14375 bytes
0x6080604052600436106102375760003560e01c806380935aa911610138578063c7acb01f116100b0578063ddd59b9f1161007f578063e47a882d11610064578063e47a882d1461086e578063f53eddcc146108a4578063fad3cc4b146108d857600080fd5b8063ddd59b9f14610805578063df8879b81461083957600080fd5b8063c7acb01f14610719578063c89c74ea14610739578063d331bef71461077d578063dd62ed3e146107b257600080fd5b806397b87b4a11610107578063a457c2d7116100ec578063a457c2d7146106ac578063a9059cbb146106cc578063bd4dbda0146106ec57600080fd5b806397b87b4a146106135780639e65741e1461064757600080fd5b806380935aa91461059557806392eb1656146105c95780639410ae88146105de57806395d89b41146105fe57600080fd5b8063313ce567116101cb578063443ec74d1161019a5780635ee04d781161017f5780635ee04d78146104fe5780636cfd15531461053257806370a082311461055257600080fd5b8063443ec74d146104aa5780634fa1aa8a146104de57600080fd5b8063313ce5671461043957806335f0df981461045557806339509351146104755780633bfaa7e31461049557600080fd5b806318160ddd1161020757806318160ddd1461039257806319d8ac61146103b157806323b872dd146103e55780632861c7d11461040557600080fd5b8062c3cdae146102bb57806301d22ccd146102ee57806306fdde0314610340578063095ea7b31461036257600080fd5b366102b6573373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c16146102b4576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024015b60405180910390fd5b005b600080fd5b6102ce6102c93660046132ee565b6108eb565b604080519384526020840192909252908201526060015b60405180910390f35b3480156102fa57600080fd5b5060065461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b34801561034c57600080fd5b50610355610dd8565b6040516102e59190613393565b34801561036e57600080fd5b5061038261037d3660046133ad565b610e6a565b60405190151581526020016102e5565b34801561039e57600080fd5b506002545b6040519081526020016102e5565b3480156103bd57600080fd5b506005546103cf9064ffffffffff1681565b60405164ffffffffff90911681526020016102e5565b3480156103f157600080fd5b506103826104003660046133d9565b610e84565b34801561041157600080fd5b5061031b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781565b34801561044557600080fd5b50604051601281526020016102e5565b34801561046157600080fd5b506102ce6104703660046132ee565b610ea8565b34801561048157600080fd5b506103826104903660046133ad565b6113ec565b3480156104a157600080fd5b506102b4611438565b3480156104b657600080fd5b5061031b7f0000000000000000000000004956b52ae2ff65d74ca2d61207523288e4528f9681565b3480156104ea57600080fd5b506102b46104f936600461341a565b6114be565b34801561050a57600080fd5b5061031b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b34801561053e57600080fd5b506102b461054d366004613433565b611715565b34801561055e57600080fd5b506103a361056d366004613433565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3480156105a157600080fd5b5061031b7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c81565b3480156105d557600080fd5b506103a36118c8565b3480156105ea57600080fd5b506102b46105f93660046133ad565b611a23565b34801561060a57600080fd5b50610355611ae0565b34801561061f57600080fd5b5061031b7f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908581565b34801561065357600080fd5b50600554610684906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1681565b60405176ffffffffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b3480156106b857600080fd5b506103826106c73660046133ad565b611aef565b3480156106d857600080fd5b506103826106e73660046133ad565b611bc0565b3480156106f857600080fd5b5060075461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561072557600080fd5b5061035561073436600461347f565b611bce565b34801561074557600080fd5b5061074e611d11565b6040805176ffffffffffffffffffffffffffffffffffffffffffffff90931683529015156020830152016102e5565b34801561078957600080fd5b5061079d6107983660046132ee565b611e3f565b604080519283526020830191909152016102e5565b3480156107be57600080fd5b506103a36107cd366004613561565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b34801561081157600080fd5b506103827f000000000000000000000000000000000000000000000000000000000000000081565b61084c61084736600461359a565b6120f9565b60408051948552602085019390935291830152151560608201526080016102e5565b34801561087a57600080fd5b506005546108919065010000000000900460030b81565b60405160039190910b81526020016102e5565b3480156108b057600080fd5b5061031b7f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b81565b61079d6108e63660046132ee565b612658565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808080610922611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561099b57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146109f6576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790557f0000000000000000000000000000000000000000000000000000000000000000610b75573415610a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c16634d9036de610add8a60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152602481018a9052604481018990526000606482015260840160408051808303816000875af1158015610b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6b91906135fe565b9093509150610d13565b60007f0000000000000000000000004956b52ae2ff65d74ca2d61207523288e4528f9673ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bce5786610bd0565b875b905034811115610c10576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6000610c1c3447613622565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c16634d9036de83610c668d60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526004810191909152602481018d9052604481018c905260006064820152608401604080518083038185885af1158015610ccf573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cf491906135fe565b9095509350303181811115610d0f57610d0f33838303612a04565b5050505b73ffffffffffffffffffffffffffffffffffffffff851615610d355784610d37565b335b6005549095506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff16610d7589670de0b6b3a7640000613635565b610d7f919061364c565b9350610d8b8585612a55565b50600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead1790559196909550909350915050565b606060038054610de790613687565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1390613687565b8015610e605780601f10610e3557610100808354040283529160200191610e60565b820191906000526020600020905b815481529060010190602001808311610e4357829003601f168201915b5050505050905090565b600033610e78818585612b48565b60019150505b92915050565b600033610e92858285612cfb565b610e9d858585612dd2565b506001949350505050565b600080600080610eb6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015610f2f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114610f8a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790556001880161103f5733600090815260208190526040902054600554909450600190670de0b6b3a764000090611024906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1687613635565b61102e919061364c565b6110389190613622565b9750611092565b6005546901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661107a89670de0b6b3a7640000613635565b611084919061364c565b61108f9060016135eb565b93505b61109c3385613041565b73ffffffffffffffffffffffffffffffffffffffff8516156110be57846110c0565b335b94506000871180156110d25750600086115b156111a7576040517f35f0df9800000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c16906335f0df989060840160408051808303816000875af1158015611179573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119d91906135fe565b909350915061139f565b6000871180156111b5575085155b15611288576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c1690634c89bfd4906084016020604051808303816000875af115801561125d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128191906136da565b925061139f565b861580156112965750600086115b15611369576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c1690634c89bfd4906084016020604051808303816000875af115801561133e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136291906136da565b915061139f565b6040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f760048201526024016102ab565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790559196909550909350915050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152812054909190610e7890829086906114339087906135eb565b612b48565b6000611442611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff90931692909217909155905080156114bb57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b50565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa15801561154e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157291906136f3565b6115ac576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b60006115b6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561162f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b620f424082138061165f57507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0bdc082125b1561169a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f460048201526024016102ab565b600580544264ffffffffff167fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000909116176501000000000063ffffffff8516021790556040518281527f7f11439341687b064c9a44aed86bf97e498b897829329ebebee243a8aeab35bb906020015b60405180910390a15050565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa1580156117a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c991906136f3565b611803576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b8073ffffffffffffffffffffffffffffffffffffffff8116611855576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f160048201526024016102ab565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527f336a7807b53df027467559d6ed5ba292805d0e7df83ec3fd0df46e81675dbc5e90602001611709565b6040805130602080830191909152600382840152825180830384018152606083019384905280519101207fb5c736e400000000000000000000000000000000000000000000000000000000909252606481019190915260009081907f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c73ffffffffffffffffffffffffffffffffffffffff169063b5c736e490608401602060405180830381865afa158015611981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a591906136da565b600554600254600983901c66ffffffffffffff1660019390931c60ff169290921b9250600091670de0b6b3a7640000916901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1690611a069190613635565b611a10919061364c565b9050611a1c8183613715565b9250505090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c1614611a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600754611adc90839073ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49784613205565b5050565b606060048054610de790613687565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015611bb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016102ab565b610e9d8286868403612b48565b600033610e78818585612dd2565b60607f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5f919061373c565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cc7576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e811560018103611d0857816000803e816000fd5b50505092915050565b60055476ffffffffffffffffffffffffffffffffffffffffffffff69010000000000000000008204169060009065010000000000900460030b81811315611dc257611d636301e13380620f4240613635565b600554611d779064ffffffffff1642613622565b611d9a8376ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611da49190613635565b611dae919061364c565b611db89084613759565b9360019350915050565b6000811215611e3a57611ddc6301e13380620f4240613635565b600554611df09064ffffffffff1642613622565b611df983613789565b611e1c9076ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611e269190613635565b611e30919061364c565b611db890846137c1565b509091565b6000806000611e4c611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015611ec557600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114611f20576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff841615611f815783611f83565b335b6040517fd331bef700000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff80831660648301529195507f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c9091169063d331bef7906084016020604051808303816000875af115801561202b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204f91906136da565b6005549092506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661208d83670de0b6b3a7640000613635565b612097919061364c565b6120a29060016135eb565b92506120ae3384613041565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055909590945092505050565b6000806000806000612109611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561218257600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146121dd576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b60078054740200000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90911617905560065473ffffffffffffffffffffffffffffffffffffffff163314612273576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f560048201526024016102ab565b600061227d6118c8565b9050600081131561239b576001925034156122c8576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6040517f35f0df98000000000000000000000000000000000000000000000000000000008152600481018290526024810189905260448101889052336064820152909550859073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c16906335f0df989060840160408051808303816000875af115801561236d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239191906135fe565b90955093506125c5565b60008112156125c55760009250826123b33447613622565b905060007f00000000000000000000000000000000000000000000000000000000000000001561243a577f0000000000000000000000004956b52ae2ff65d74ca2d61207523288e4528f9673ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612434578861243d565b8961243d565b60005b90503481111561247d576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b61248683613789565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556040517f4d9036de00000000000000000000000000000000000000000000000000000000815260048101829052602481018c9052604481018b9052600060648201529098507f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c73ffffffffffffffffffffffffffffffffffffffff1690634d9036de908390608401604080518083038185885af1158015612555573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061257a91906135fe565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905590975095503031828111156125c1576125c133848303612a04565b5050505b604080518781526020810187905290810185905283151560608201527f074a87ec182f91973b93d344745a07f667c7edd70584cb334add5392a31e173a9060800160405180910390a15050600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905592959194509250565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808061268e611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561270757600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114612762576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905560007f000000000000000000000000000000000000000000000000000000000000000015612826577f0000000000000000000000004956b52ae2ff65d74ca2d61207523288e4528f9673ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146128205786612829565b87612829565b60005b9050348114612868576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff85161561288a578461288c565b335b6040517fe980e1eb000000000000000000000000000000000000000000000000000000008152600481018a905260248101899052604481018890526000606482015290955073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000050facbcfbf9352523f82c67832e6d3d7ce731d4c169063e980e1eb90839060840160206040518083038185885af1158015612932573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061295791906136da565b6005549093506001906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661299885670de0b6b3a7640000613635565b6129a2919061364c565b6129ac9190613622565b93506129b88585612a55565b5050600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead179055909590945092505050565b60008060008060008587614e20f1905080612a50576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016102ab565b505050565b73ffffffffffffffffffffffffffffffffffffffff8216612ad2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016102ab565b8060026000828254612ae491906135eb565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8316612bea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612c8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612dcc5781811015612dbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016102ab565b612dcc8484848403612b48565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316612e75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612f18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015612fce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612dcc565b73ffffffffffffffffffffffffffffffffffffffff82166130e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff82166000908152602081905260409020548181101561319a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806132c5576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016102ab565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146114bb57600080fd5b6000806000806080858703121561330457600080fd5b8435935060208501359250604085013591506060850135613324816132cc565b939692955090935050565b6000815180845260005b8181101561335557602081850181015186830182015201613339565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006133a6602083018461332f565b9392505050565b600080604083850312156133c057600080fd5b82356133cb816132cc565b946020939093013593505050565b6000806000606084860312156133ee57600080fd5b83356133f9816132cc565b92506020840135613409816132cc565b929592945050506040919091013590565b60006020828403121561342c57600080fd5b5035919050565b60006020828403121561344557600080fd5b81356133a6816132cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561349257600080fd5b823561349d816132cc565b9150602083013567ffffffffffffffff808211156134ba57600080fd5b818501915085601f8301126134ce57600080fd5b8135818111156134e0576134e0613450565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561352657613526613450565b8160405282815288602084870101111561353f57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000806040838503121561357457600080fd5b823561357f816132cc565b9150602083013561358f816132cc565b809150509250929050565b600080604083850312156135ad57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610e7e57610e7e6135bc565b6000806040838503121561361157600080fd5b505080516020909101519092909150565b81810381811115610e7e57610e7e6135bc565b8082028115828204841417610e7e57610e7e6135bc565b600082613682577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600181811c9082168061369b57607f821691505b6020821081036136d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156136ec57600080fd5b5051919050565b60006020828403121561370557600080fd5b815180151581146133a657600080fd5b8181036000831280158383131683831282161715613735576137356135bc565b5092915050565b60006020828403121561374e57600080fd5b81516133a6816132cc565b76ffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115613735576137356135bc565b60007f800000000000000000000000000000000000000000000000000000000000000082036137ba576137ba6135bc565b5060000390565b76ffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613735576137356135bc56fea26469706673582212204654438f3b79a876fe2fc2b3d81f7a21c873c62ff82b82f71339ec45602f71e564736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: paris Optimization: Yes (10000000 runs)
ERC20.sol 389 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

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

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

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @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;
    }
}
addressCalcs.sol 35 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements calculation of address for contracts deployed through CREATE.
/// Accepts contract deployed from which address & nonce
library AddressCalcs {

    /// @notice                         Computes the address of a contract based
    /// @param deployedFrom_            Address from which the contract was deployed
    /// @param nonce_                   Nonce at which the contract was deployed
    /// @return contract_               Address of deployed contract
    function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {
        // @dev based on https://ethereum.stackexchange.com/a/61413

        // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment
        // hence, nonce of vault deployment starts with 1.
        bytes memory data;
        if (nonce_ == 0x00) {
            return address(0);
        } else if (nonce_ <= 0x7f) {
            data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));
        } else if (nonce_ <= 0xff) {
            data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));
        } else if (nonce_ <= 0xffff) {
            data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));
        } else if (nonce_ <= 0xffffff) {
            data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));
        } else {
            data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));
        }

        return address(uint160(uint256(keccak256(data))));
    }

}
bigMathMinified.sol 156 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.
/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision
/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can
/// result in significant gas cost reduction due to storage space reduction.
/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.
/// @dev roundUp is more like a increase 1, which happens everytime for the same number.
/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.
library BigMathMinified {
    /// @dev constants to use for `roundUp` input param to increase readability
    bool internal constant ROUND_DOWN = false;
    bool internal constant ROUND_UP = true;

    /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).
    /// e.g.:
    /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]
    /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)
    ///                                     => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000
    ///                                                                        ^-------------------- 51(exponent) -------------- ^
    /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011               (2236301563)
    /// exponent =                                            0011,0011     (51)
    /// bigNumber =   1000,0101,0100,1011,0100,0000,1111,1011,0011,0011     (572493200179)
    ///
    /// @param normal number which needs to be converted into Big Number
    /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))
    /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))
    /// @param roundUp signals if result should be rounded down or up
    /// @return bigNumber converted bigNumber (coefficient << exponent)
    function toBigNumber(
        uint256 normal,
        uint256 coefficientSize,
        uint256 exponentSize,
        bool roundUp
    ) internal pure returns (uint256 bigNumber) {
        assembly {
            let lastBit_
            let number_ := normal
            if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit_ := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit_ := add(lastBit_, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit_ := add(lastBit_, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit_ := add(lastBit_, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit_ := add(lastBit_, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit_ := add(lastBit_, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit_ := add(lastBit_, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit_ := add(lastBit_, 1)
            }
            if gt(number_, 0) {
                lastBit_ := add(lastBit_, 1)
            }
            if lt(lastBit_, coefficientSize) {
                // for throw exception
                lastBit_ := coefficientSize
            }
            let exponent := sub(lastBit_, coefficientSize)
            let coefficient := shr(exponent, normal)
            if and(roundUp, gt(exponent, 0)) {
                // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number
                coefficient := add(coefficient, 1)
                if eq(shl(coefficientSize, 1), coefficient) {
                    // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits
                    // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.
                    coefficient := shl(sub(coefficientSize, 1), 1)
                    exponent := add(exponent, 1)
                }
            }
            if iszero(lt(exponent, shl(exponentSize, 1))) {
                // if exponent is >= exponentSize, the normal number is too big to fit within
                // BigNumber with too small sizes for coefficient and exponent
                revert(0, 0)
            }
            bigNumber := shl(exponentSize, coefficient)
            bigNumber := add(bigNumber, exponent)
        }
    }

    /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`
    function fromBigNumber(
        uint256 bigNumber,
        uint256 exponentSize,
        uint256 exponentMask
    ) internal pure returns (uint256 normal) {
        assembly {
            let coefficient := shr(exponentSize, bigNumber)
            let exponent := and(bigNumber, exponentMask)
            normal := shl(exponent, coefficient)
        }
    }

    /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).
    /// e.g.
    /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111
    /// lastBit =                   ^---------------------------------   83   ----------------------------------------^
    function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {
        assembly {
            let number_ := normal
            if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit := add(lastBit, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit := add(lastBit, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit := add(lastBit, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit := add(lastBit, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit := add(lastBit, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit := add(lastBit, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit := add(lastBit, 1)
            }
            if gt(number_, 0) {
                lastBit := add(lastBit, 1)
            }
        }
    }
}
dexCalcs.sol 256 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { BigMathMinified } from "./bigMathMinified.sol";
import { DexSlotsLink } from "./dexSlotsLink.sol";

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// @DEV ATTENTION: ON ANY CHANGES HERE, MAKE SURE THAT LOGIC IN VAULTS WILL STILL BE VALID.
// SOME CODE THERE ASSUMES DEXCALCS == LIQUIDITYCALCS.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

/// @notice implements calculation methods used for Fluid Dex such as updated withdrawal / borrow limits.
library DexCalcs {
    // constants used for BigMath conversion from and to storage
    uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
    uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;

    uint256 internal constant FOUR_DECIMALS = 1e4;
    uint256 internal constant X14 = 0x3fff;
    uint256 internal constant X18 = 0x3ffff;
    uint256 internal constant X24 = 0xffffff;
    uint256 internal constant X33 = 0x1ffffffff;
    uint256 internal constant X64 = 0xffffffffffffffff;

    ///////////////////////////////////////////////////////////////////////////
    //////////                      CALC LIMITS                       /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev calculates withdrawal limit before an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath
    /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.
    ///         returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitBeforeOperate(
        uint256 userSupplyData_,
        uint256 userSupply_
    ) internal view returns (uint256 currentWithdrawalLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).
        // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.
        // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be
        // a deposit anyway. Important is that it would not revert.

        // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit
        // is the fully expanded limit immediately.

        // extract last set withdrawal limit
        uint256 lastWithdrawalLimit_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) &
            X64;
        lastWithdrawalLimit_ =
            (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);
        if (lastWithdrawalLimit_ == 0) {
            // withdrawal limit is not activated. Max withdrawal allowed
            return 0;
        }

        uint256 maxWithdrawableLimit_;
        uint256 temp_;
        unchecked {
            // extract max withdrawable percent of user supply and
            // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed
            // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.

            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxWithdrawableLimit_ =
                (((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /
                FOUR_DECIMALS;

            // time elapsed since last withdrawal limit was set (in seconds)
            // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.
            // last timestamp can not be > current timestamp
            temp_ = block.timestamp - ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);
        }
        // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.
        // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.
        // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.
        temp_ =
            (maxWithdrawableLimit_ * temp_) /
            // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)
            ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0
        // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.
        // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,
        // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0
        // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.
        unchecked {
            // underflow explicitly checked & handled
            currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;
            // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - maxWithdrawableLimit_;
        }
        // if withdrawal limit is decreased below minimum then set minimum
        // (e.g. when more than expandDuration time has elapsed)
        if (temp_ > currentWithdrawalLimit_) {
            currentWithdrawalLimit_ = temp_;
        }
    }

    /// @dev calculates withdrawal limit after an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount
    /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`
    /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in
    ///                          raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitAfterOperate(
        uint256 userSupplyData_,
        uint256 userSupply_,
        uint256 newWithdrawalLimit_
    ) internal pure returns (uint256) {
        // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed
        uint256 temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // if user supply is below base limit then max withdrawals are allowed
        if (userSupply_ < temp_) {
            return 0;
        }
        // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)
        temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;
        unchecked {
            // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))
            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);
        }
        // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.
        // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where
        // increased deposit amount outpaces withrawals.
        if (temp_ > newWithdrawalLimit_) {
            return temp_;
        }
        return newWithdrawalLimit_;
    }

    /// @dev calculates borrow limit before an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`
    /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in
    ///                             raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitBeforeOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_
    ) internal view returns (uint256 currentBorrowLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.
        // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus
        // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.

        // temp_ = extract borrow expand percent (is in 1e2 decimals)
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;

        uint256 maxExpansionLimit_;
        uint256 maxExpandedBorrowLimit_;
        unchecked {
            // calculate max expansion limit: Max amount limit can expand to since last interaction
            // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);

            // calculate max borrow limit: Max point limit can increase to since last interaction
            maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;
        }

        // currentBorrowLimit_ = extract base borrow limit
        currentBorrowLimit_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        currentBorrowLimit_ =
            (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);

        if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {
            return currentBorrowLimit_;
        }
        // time elapsed since last borrow limit was set (in seconds)
        unchecked {
            // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)
            temp_ = block.timestamp - ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp
        }

        // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit
        currentBorrowLimit_ =
            // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.
            // divisor is extract expand duration (after this, full expansion to expandPercentage happened).
            ((maxExpansionLimit_ * temp_) /
                ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0
            //  extract last set borrow limit
            BigMathMinified.fromBigNumber(
                (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,
                DEFAULT_EXPONENT_SIZE,
                DEFAULT_EXPONENT_MASK
            );

        // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,
        // so set to `maxExpandedBorrowLimit_` in that case.
        // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)
        if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {
            currentBorrowLimit_ = maxExpandedBorrowLimit_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (currentBorrowLimit_ > temp_) {
            currentBorrowLimit_ = temp_;
        }
    }

    /// @dev calculates borrow limit after an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount
    /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`
    /// @return borrowLimit_ updated borrow limit that should be written to storage.
    ///                      returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitAfterOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_,
        uint256 newBorrowLimit_
    ) internal pure returns (uint256 borrowLimit_) {
        // temp_ = extract borrow expand percent
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)

        unchecked {
            // borrowLimit_ = calculate maximum borrow limit at full expansion.
            // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);
        }

        // temp_ = extract base borrow limit
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (borrowLimit_ < temp_) {
            // below base limit, borrow limit is always base limit
            return temp_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // make sure fully expanded borrow limit is not above hard max borrow limit
        if (borrowLimit_ > temp_) {
            borrowLimit_ = temp_;
        }
        // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.
        // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
        if (newBorrowLimit_ > borrowLimit_) {
            return borrowLimit_;
        }
        return newBorrowLimit_;
    }
}
dexSlotsLink.sol 65 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice library that helps in reading / working with storage slot data of Fluid Dex.
/// @dev as all data for Fluid Dex is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidDexResolver.
library DexSlotsLink {
    /// @dev storage slot for variables at Dex
    uint256 internal constant DEX_VARIABLES_SLOT = 0;
    /// @dev storage slot for variables2 at Dex
    uint256 internal constant DEX_VARIABLES2_SLOT = 1;
    /// @dev storage slot for total supply shares at Dex
    uint256 internal constant DEX_TOTAL_SUPPLY_SHARES_SLOT = 2;
    /// @dev storage slot for user supply mapping at Dex
    uint256 internal constant DEX_USER_SUPPLY_MAPPING_SLOT = 3;
    /// @dev storage slot for total borrow shares at Dex
    uint256 internal constant DEX_TOTAL_BORROW_SHARES_SLOT = 4;
    /// @dev storage slot for user borrow mapping at Dex
    uint256 internal constant DEX_USER_BORROW_MAPPING_SLOT = 5;
    /// @dev storage slot for oracle mapping at Dex
    uint256 internal constant DEX_ORACLE_MAPPING_SLOT = 6;
    /// @dev storage slot for range and threshold shifts at Dex
    uint256 internal constant DEX_RANGE_THRESHOLD_SHIFTS_SLOT = 7;
    /// @dev storage slot for center price shift at Dex
    uint256 internal constant DEX_CENTER_PRICE_SHIFT_SLOT = 8;

    // --------------------------------
    // @dev stacked uint256 storage slots bits position data for each:

    // UserSupplyData
    uint256 internal constant BITS_USER_SUPPLY_ALLOWED = 0;
    uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
    uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
    uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;

    // UserBorrowData
    uint256 internal constant BITS_USER_BORROW_ALLOWED = 0;
    uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
    uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
    uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
    uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;

    // --------------------------------

    /// @notice Calculating the slot ID for Dex contract for single mapping at `slot_` for `key_`
    function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
        return keccak256(abi.encode(key_, slot_));
    }

    /// @notice Calculating the slot ID for Dex contract for double mapping at `slot_` for `key1_` and `key2_`
    function calculateDoubleMappingStorageSlot(
        uint256 slot_,
        address key1_,
        address key2_
    ) internal pure returns (bytes32) {
        bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
        return keccak256(abi.encode(key2_, intermediateSlot_));
    }
}
errorTypes.sol 34 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library LibsErrorTypes {
    /***********************************|
    |         LiquidityCalcs            | 
    |__________________________________*/

    /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
    uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;

    /// @notice thrown when rate data is set to a version that is not implemented
    uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;

    /// @notice thrown when the calculated borrow rate turns negative. This should never happen.
    uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;

    /***********************************|
    |           SafeTransfer            | 
    |__________________________________*/

    /// @notice thrown when safe transfer from for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFromFailed = 71001;

    /// @notice thrown when safe transfer for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFailed = 71002;

    /***********************************|
    |           SafeApprove             | 
    |__________________________________*/

    /// @notice thrown when safe approve from for an ERC20 fails
    uint256 internal constant SafeApprove__ApproveFailed = 81001;
}
safeTransfer.sol 97 lines
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.21;

import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";

/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
    uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers

    error FluidSafeTransferError(uint256 errorId_);

    /// @dev Transfer `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.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
    function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
            mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
        }
    }

    /// @dev Transfer `amount_` of `token_` to `to_`.
    /// If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
    function safeTransfer(address token_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }

    /// @dev Transfer `amount_` of ` native token to `to_`.
    /// Minimally modified from Solmate SafeTransferLib (Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
    function safeTransferNative(address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not. Pass limited gas
            success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0)
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }
}
storageRead.sol 11 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.
contract StorageRead {
    function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {
        assembly {
            result_ := sload(slot_) // read value from the storage slot
        }
    }
}
error.sol 25 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Structs } from "./poolT1/coreModule/structs.sol";

abstract contract Error {
    error FluidDexError(uint256 errorId_);

    error FluidDexFactoryError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares_);

    error FluidDexPricesAndExchangeRates(Structs.PricesAndExchangePrice pex_);

    error FluidSmartLendingError(uint256 errorId_);

    error FluidSmartLendingFactoryError(uint256 errorId_);
}
errorTypes.sol 214 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library ErrorTypes {
    /***********************************|
    |             DexT1                 | 
    |__________________________________*/

    /// @notice thrown at reentrancy
    uint256 internal constant DexT1__AlreadyEntered = 51001;

    uint256 internal constant DexT1__NotAnAuth = 51002;

    uint256 internal constant DexT1__SmartColNotEnabled = 51003;

    uint256 internal constant DexT1__SmartDebtNotEnabled = 51004;

    uint256 internal constant DexT1__PoolNotInitialized = 51005;

    uint256 internal constant DexT1__TokenReservesTooLow = 51006;

    uint256 internal constant DexT1__EthAndAmountInMisMatch = 51007;

    uint256 internal constant DexT1__EthSentForNonNativeSwap = 51008;

    uint256 internal constant DexT1__NoSwapRoute = 51009;

    uint256 internal constant DexT1__NotEnoughAmountOut = 51010;

    uint256 internal constant DexT1__LiquidityLayerTokenUtilizationCapReached = 51011;

    uint256 internal constant DexT1__HookReturnedFalse = 51012;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserSupplyInNotOn = 51013;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserDebtInNotOn = 51014;

    // Thrown when contract asks for more token0 or token1 than what user's wants to give on deposit
    uint256 internal constant DexT1__AboveDepositMax = 51015;

    uint256 internal constant DexT1__MsgValueLowOnDepositOrPayback = 51016;

    uint256 internal constant DexT1__WithdrawLimitReached = 51017;

    // Thrown when contract gives less token0 or token1 than what user's wants on withdraw
    uint256 internal constant DexT1__BelowWithdrawMin = 51018;

    uint256 internal constant DexT1__DebtLimitReached = 51019;

    // Thrown when contract gives less token0 or token1 than what user's wants on borrow
    uint256 internal constant DexT1__BelowBorrowMin = 51020;

    // Thrown when contract asks for more token0 or token1 than what user's wants on payback
    uint256 internal constant DexT1__AbovePaybackMax = 51021;

    uint256 internal constant DexT1__InvalidDepositAmts = 51022;

    uint256 internal constant DexT1__DepositAmtsZero = 51023;

    uint256 internal constant DexT1__SharesMintedLess = 51024;

    uint256 internal constant DexT1__WithdrawalNotEnough = 51025;

    uint256 internal constant DexT1__InvalidWithdrawAmts = 51026;

    uint256 internal constant DexT1__WithdrawAmtsZero = 51027;

    uint256 internal constant DexT1__WithdrawExcessSharesBurn = 51028;

    uint256 internal constant DexT1__InvalidBorrowAmts = 51029;

    uint256 internal constant DexT1__BorrowAmtsZero = 51030;

    uint256 internal constant DexT1__BorrowExcessSharesMinted = 51031;

    uint256 internal constant DexT1__PaybackAmtTooHigh = 51032;

    uint256 internal constant DexT1__InvalidPaybackAmts = 51033;

    uint256 internal constant DexT1__PaybackAmtsZero = 51034;

    uint256 internal constant DexT1__PaybackSharedBurnedLess = 51035;

    uint256 internal constant DexT1__NothingToArbitrage = 51036;

    uint256 internal constant DexT1__MsgSenderNotLiquidity = 51037;

    // On liquidity callback reentrancy bit should be on
    uint256 internal constant DexT1__ReentrancyBitShouldBeOn = 51038;

    // Thrown is reentrancy is already on and someone tries to fetch oracle price. Should not be possible to this
    uint256 internal constant DexT1__OraclePriceFetchAlreadyEntered = 51039;

    // Thrown when swap changes the current price by more than 5%
    uint256 internal constant DexT1__OracleUpdateHugeSwapDiff = 51040;

    uint256 internal constant DexT1__Token0ShouldBeSmallerThanToken1 = 51041;

    uint256 internal constant DexT1__OracleMappingOverflow = 51042;

    /// @notice thrown if governance has paused the swapping & arbitrage so only perfect functions are usable
    uint256 internal constant DexT1__SwapAndArbitragePaused = 51043;

    uint256 internal constant DexT1__ExceedsAmountInMax = 51044;

    /// @notice thrown if amount in is too high or too low
    uint256 internal constant DexT1__SwapInLimitingAmounts = 51045;

    /// @notice thrown if amount out is too high or too low
    uint256 internal constant DexT1__SwapOutLimitingAmounts = 51046;

    uint256 internal constant DexT1__MintAmtOverflow = 51047;

    uint256 internal constant DexT1__BurnAmtOverflow = 51048;

    uint256 internal constant DexT1__LimitingAmountsSwapAndNonPerfectActions = 51049;

    uint256 internal constant DexT1__InsufficientOracleData = 51050;

    uint256 internal constant DexT1__SharesAmountInsufficient = 51051;

    uint256 internal constant DexT1__CenterPriceOutOfRange = 51052;

    uint256 internal constant DexT1__DebtReservesTooLow = 51053;

    uint256 internal constant DexT1__SwapAndDepositTooLowOrTooHigh = 51054;

    uint256 internal constant DexT1__WithdrawAndSwapTooLowOrTooHigh = 51055;

    uint256 internal constant DexT1__BorrowAndSwapTooLowOrTooHigh = 51056;

    uint256 internal constant DexT1__SwapAndPaybackTooLowOrTooHigh = 51057;

    uint256 internal constant DexT1__InvalidImplementation = 51058;

    uint256 internal constant DexT1__OnlyDelegateCallAllowed = 51059;

    uint256 internal constant DexT1__IncorrectDataLength = 51060;

    uint256 internal constant DexT1__AmountToSendLessThanAmount = 51061;

    uint256 internal constant DexT1__InvalidCollateralReserves = 51062;

    uint256 internal constant DexT1__InvalidDebtReserves = 51063;

    uint256 internal constant DexT1__SupplySharesOverflow = 51064;

    uint256 internal constant DexT1__BorrowSharesOverflow = 51065;

    uint256 internal constant DexT1__OracleNotActive = 51066;

    /***********************************|
    |            DEX Admin              | 
    |__________________________________*/

    /// @notice thrown when pool is not initialized
    uint256 internal constant DexT1Admin__PoolNotInitialized = 52001;

    uint256 internal constant DexT1Admin__SmartColIsAlreadyOn = 52002;

    uint256 internal constant DexT1Admin__SmartDebtIsAlreadyOn = 52003;

    /// @notice thrown when any of the configs value overflow the maximum limit
    uint256 internal constant DexT1Admin__ConfigOverflow = 52004;

    uint256 internal constant DexT1Admin__AddressNotAContract = 52005;

    uint256 internal constant DexT1Admin__InvalidParams = 52006;

    uint256 internal constant DexT1Admin__UserNotDefined = 52007;

    uint256 internal constant DexT1Admin__OnlyDelegateCallAllowed = 52008;

    uint256 internal constant DexT1Admin__UnexpectedPoolState = 52009;

    /// @notice thrown when trying to pause or unpause but user is already in the target pause state
    uint256 internal constant DexT1Admin__InvalidPauseToggle = 52009;

    /***********************************|
    |            DEX Factory            | 
    |__________________________________*/

    uint256 internal constant DexFactory__InvalidOperation = 53001;
    uint256 internal constant DexFactory__Unauthorized = 53002;
    uint256 internal constant DexFactory__SameTokenNotAllowed = 53003;
    uint256 internal constant DexFactory__TokenConfigNotProper = 53004;
    uint256 internal constant DexFactory__InvalidParams = 53005;
    uint256 internal constant DexFactory__OnlyDelegateCallAllowed = 53006;
    uint256 internal constant DexFactory__InvalidDexAddress = 53007;

    /***********************************|
    |            Smart Lending          | 
    |__________________________________*/

    uint256 internal constant SmartLending__ZeroAddress = 54001;
    uint256 internal constant SmartLending__Unauthorized = 54002;
    uint256 internal constant SmartLending__InvalidMsgValue = 54003;
    uint256 internal constant SmartLending__OutOfRange = 54004;
    uint256 internal constant SmartLending__InvalidRebalancer = 54005;
    uint256 internal constant SmartLending__Reentrancy = 54006;
    uint256 internal constant SmartLending__InvalidAmounts = 54007;

    /***********************************|
    |        Smart Lending Factory       | 
    |__________________________________*/

    uint256 internal constant SmartLendingFactory__ZeroAddress = 55001;
    uint256 internal constant SmartLendingFactory__Unauthorized = 55002;
    uint256 internal constant SmartLendingFactory__AlreadyDeployed = 55003;
    uint256 internal constant SmartLendingFactory__InvalidParams = 55004;
    uint256 internal constant SmartLendingFactory__InvalidOperation = 55005;
}
main.sol 279 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Owned } from "solmate/src/auth/Owned.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error } from "../error.sol";
import { AddressCalcs } from "../../../libraries/addressCalcs.sol";
import { StorageRead } from "../../../libraries/storageRead.sol";

abstract contract DexFactoryVariables is Owned, StorageRead, Error {
    /*//////////////////////////////////////////////////////////////
                          STORAGE VARIABLES
    //////////////////////////////////////////////////////////////*/

    // ------------ storage variables from inherited contracts (Owned) come before vars here --------

    // ----------------------- slot 0 ---------------------------
    // address public owner; // from Owned

    // 12 bytes empty

    // ----------------------- slot 1  ---------------------------
    /// @dev deployer can deploy new Dex Pool contract
    /// owner can add/remove deployer.
    /// Owner is deployer by default.
    mapping(address => bool) internal _deployers;

    // ----------------------- slot 2  ---------------------------
    /// @dev global auths can update any dex pool config.
    /// owner can add/remove global auths.
    /// Owner is global auth by default.
    mapping(address => bool) internal _globalAuths;

    // ----------------------- slot 3  ---------------------------
    /// @dev dex auths can update specific dex config.
    /// owner can add/remove dex auths.
    /// Owner is dex auth by default.
    /// dex => auth => add/remove
    mapping(address => mapping(address => bool)) internal _dexAuths;

    // ----------------------- slot 4 ---------------------------
    /// @dev total no of dexes deployed by the factory
    /// only addresses that have deployer role or owner can deploy new dex pool.
    uint256 internal _totalDexes;

    // ----------------------- slot 5 ---------------------------
    /// @dev dex deployment logics for deploying dex pool
    /// These logic contracts hold the deployment logics of specific dexes and are called via .delegatecall inside deployDex().
    /// only addresses that have owner can add/remove new dex deployment logic.
    mapping(address => bool) internal _dexDeploymentLogics;

    /*//////////////////////////////////////////////////////////////
                          CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    constructor(address owner_) Owned(owner_) {}
}

abstract contract DexFactoryEvents {
    /// @dev Emitted when a new dex is deployed.
    /// @param dex The address of the newly deployed dex.
    /// @param dexId The id of the newly deployed dex.
    event LogDexDeployed(address indexed dex, uint256 indexed dexId);

    /// @dev Emitted when the deployer is modified by owner.
    /// @param deployer Address whose deployer status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    event LogSetDeployer(address indexed deployer, bool indexed allowed);

    /// @dev Emitted when the globalAuth is modified by owner.
    /// @param globalAuth Address whose globalAuth status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    event LogSetGlobalAuth(address indexed globalAuth, bool indexed allowed);

    /// @dev Emitted when the dexAuth is modified by owner.
    /// @param dexAuth Address whose dexAuth status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    /// @param dex Address of the specific dex related to the authorization change.
    event LogSetDexAuth(address indexed dexAuth, bool indexed allowed, address indexed dex);

    /// @dev Emitted when the dex deployment logic is modified by owner.
    /// @param dexDeploymentLogic The address of the dex deployment logic contract.
    /// @param allowed  Indicates whether the address is authorized as a deployer or not.
    event LogSetDexDeploymentLogic(address indexed dexDeploymentLogic, bool indexed allowed);
}

abstract contract DexFactoryCore is DexFactoryVariables, DexFactoryEvents {
    constructor(address owner_) validAddress(owner_) DexFactoryVariables(owner_) {}

    /// @dev validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidDexFactoryError(ErrorTypes.DexFactory__InvalidParams);
        }
        _;
    }
}

/// @dev Implements Dex Factory auth-only callable methods. Owner / auths can set various config values and
/// can define the allow-listed deployers.
abstract contract DexFactoryAuth is DexFactoryCore {
    /// @notice                         Sets an address (`deployer_`) as allowed deployer or not.
    ///                                 This function can only be called by the owner.
    /// @param deployer_                The address to be set as deployer.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to deploy dexes.
    function setDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {
        _deployers[deployer_] = allowed_;

        emit LogSetDeployer(deployer_, allowed_);
    }

    /// @notice                         Sets an address (`globalAuth_`) as a global authorization or not.
    ///                                 This function can only be called by the owner.
    /// @param globalAuth_              The address to be set as global authorization.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to update any dex config.
    function setGlobalAuth(address globalAuth_, bool allowed_) external onlyOwner validAddress(globalAuth_) {
        _globalAuths[globalAuth_] = allowed_;

        emit LogSetGlobalAuth(globalAuth_, allowed_);
    }

    /// @notice                         Sets an address (`dexAuth_`) as allowed dex authorization or not for a specific dex (`dex_`).
    ///                                 This function can only be called by the owner.
    /// @param dex_                     The address of the dex for which the authorization is being set.
    /// @param dexAuth_                 The address to be set as dex authorization.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to update the specific dex config.
    function setDexAuth(address dex_, address dexAuth_, bool allowed_) external onlyOwner validAddress(dexAuth_) {
        _dexAuths[dex_][dexAuth_] = allowed_;

        emit LogSetDexAuth(dexAuth_, allowed_, dex_);
    }

    /// @notice                         Sets an address as allowed dex deployment logic (`deploymentLogic_`) contract or not.
    ///                                 This function can only be called by the owner.
    /// @param deploymentLogic_         The address of the dex deployment logic contract to be set.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to deploy new type of dex.
    function setDexDeploymentLogic(
        address deploymentLogic_,
        bool allowed_
    ) public onlyOwner validAddress(deploymentLogic_) {
        _dexDeploymentLogics[deploymentLogic_] = allowed_;

        emit LogSetDexDeploymentLogic(deploymentLogic_, allowed_);
    }

    /// @notice                         Spell allows owner aka governance to do any arbitrary call on factory
    /// @param target_                  Address to which the call needs to be delegated
    /// @param data_                    Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @notice                         Checks if the provided address (`deployer_`) is authorized as a deployer.
    /// @param deployer_                The address to be checked for deployer authorization.
    /// @return                         Returns `true` if the address is a deployer, otherwise `false`.
    function isDeployer(address deployer_) public view returns (bool) {
        return _deployers[deployer_] || owner == deployer_;
    }

    /// @notice                         Checks if the provided address (`globalAuth_`) has global dex authorization privileges.
    /// @param globalAuth_              The address to be checked for global authorization privileges.
    /// @return                         Returns `true` if the given address has global authorization privileges, otherwise `false`.
    function isGlobalAuth(address globalAuth_) public view returns (bool) {
        return _globalAuths[globalAuth_] || owner == globalAuth_;
    }

    /// @notice                         Checks if the provided address (`dexAuth_`) has dex authorization privileges for the specified dex (`dex_`).
    /// @param dex_                     The address of the dex to check.
    /// @param dexAuth_                 The address to be checked for dex authorization privileges.
    /// @return                         Returns `true` if the given address has dex authorization privileges for the specified dex, otherwise `false`.
    function isDexAuth(address dex_, address dexAuth_) public view returns (bool) {
        return _dexAuths[dex_][dexAuth_] || owner == dexAuth_;
    }

    /// @notice                         Checks if the provided (`dexDeploymentLogic_`) address has authorization for dex deployment.
    /// @param dexDeploymentLogic_      The address of the dex deploy logic to check for authorization privileges.
    /// @return                         Returns `true` if the given address has authorization privileges for dex deployment, otherwise `false`.
    function isDexDeploymentLogic(address dexDeploymentLogic_) public view returns (bool) {
        return _dexDeploymentLogics[dexDeploymentLogic_];
    }
}

/// @dev implements DexFactory deploy dex related methods.
abstract contract DexFactoryDeployment is DexFactoryCore, DexFactoryAuth {
    /// @dev                            Deploys a contract using the CREATE opcode with the provided bytecode (`bytecode_`).
    ///                                 This is an internal function, meant to be used within the contract to facilitate the deployment of other contracts.
    /// @param bytecode_                The bytecode of the contract to be deployed.
    /// @return address_                Returns the address of the deployed contract.
    function _deploy(bytes memory bytecode_) internal returns (address address_) {
        if (bytecode_.length == 0) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidOperation);
        }
        /// @solidity memory-safe-assembly
        assembly {
            address_ := create(0, add(bytecode_, 0x20), mload(bytecode_))
        }
        if (address_ == address(0)) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidOperation);
        }
    }

    /// @notice                       Deploys a new dex using the specified deployment logic `dexDeploymentLogic_` and data `dexDeploymentData_`.
    ///                               Only accounts with deployer access or the owner can deploy a new dex.
    /// @param dexDeploymentLogic_    The address of the dex deployment logic contract.
    /// @param dexDeploymentData_     The data to be used for dex deployment.
    /// @return dex_                  Returns the address of the newly deployed dex.
    function deployDex(address dexDeploymentLogic_, bytes calldata dexDeploymentData_) external returns (address dex_) {
        // Revert if msg.sender doesn't have deployer access or is an owner.
        if (!isDeployer(msg.sender)) revert FluidDexError(ErrorTypes.DexFactory__Unauthorized);
        // Revert if dexDeploymentLogic_ is not whitelisted.
        if (!isDexDeploymentLogic(dexDeploymentLogic_)) revert FluidDexError(ErrorTypes.DexFactory__Unauthorized);

        // Dex ID for the new dex and also acts as `nonce` for CREATE
        uint256 dexId_ = ++_totalDexes;

        // compute dex address for dex id.
        dex_ = getDexAddress(dexId_);

        // deploy the dex using dex deployment logic by making .delegatecall
        (bool success_, bytes memory data_) = dexDeploymentLogic_.delegatecall(dexDeploymentData_);

        if (!(success_ && dex_ == _deploy(abi.decode(data_, (bytes))) && isDex(dex_))) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidDexAddress);
        }

        emit LogDexDeployed(dex_, dexId_);
    }

    /// @notice                       Computes the address of a dex based on its given ID (`dexId_`).
    /// @param dexId_                 The ID of the dex.
    /// @return dex_                  Returns the computed address of the dex.
    function getDexAddress(uint256 dexId_) public view returns (address dex_) {
        return AddressCalcs.addressCalc(address(this), dexId_);
    }

    /// @notice                         Checks if a given address (`dex_`) corresponds to a valid dex.
    /// @param dex_                     The dex address to check.
    /// @return                         Returns `true` if the given address corresponds to a valid dex, otherwise `false`.
    function isDex(address dex_) public view returns (bool) {
        if (dex_.code.length == 0) {
            return false;
        } else {
            // DEX_ID() function signature is 0xf4b9a3fb
            (bool success_, bytes memory data_) = dex_.staticcall(hex"f4b9a3fb");
            return success_ && dex_ == getDexAddress(abi.decode(data_, (uint256)));
        }
    }

    /// @notice                   Returns the total number of dexes deployed by the factory.
    /// @return                   Returns the total number of dexes.
    function totalDexes() external view returns (uint256) {
        return _totalDexes;
    }
}

/// @title Fluid DexFactory
/// @notice creates Fluid dex protocol dexes, which are interacting with Fluid Liquidity to deposit / borrow funds.
/// Dexes are created at a deterministic address, given an incrementing `dexId` (see `getDexAddress()`).
/// Dexes can only be deployed by allow-listed deployer addresses.
/// @dev Note the deployed dexes start out with no config at Liquidity contract.
/// This must be done by Liquidity auths in a separate step, otherwise no deposits will be possible.
/// This contract is not upgradeable. It supports adding new dex deployment logic contracts for new, future dexes.
contract FluidDexFactory is DexFactoryCore, DexFactoryAuth, DexFactoryDeployment {
    constructor(address owner_) DexFactoryCore(owner_) {}
}
iDexT1.sol 294 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFluidDexT1 {
    error FluidDexError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares);

    error FluidDexPricesAndExchangeRates(PricesAndExchangePrice pex_);

    /// @notice returns the dex id
    function DEX_ID() external view returns (uint256);

    /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndOracle;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }

    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    function getCollateralReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0SupplyExchangePrice_,
        uint token1SupplyExchangePrice_
    ) external view returns (CollateralReserves memory c_);

    function getDebtReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0BorrowExchangePrice_,
        uint token1BorrowExchangePrice_
    ) external view returns (DebtReserves memory d_);

    // reverts with FluidDexPricesAndExchangeRates(pex_);
    function getPricesAndExchangePrices() external;

    function constantsView() external view returns (ConstantViews memory constantsView_);

    function constantsView2() external view returns (ConstantViews2 memory constantsView2_);

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    /// @dev This function allows users to swap a specific amount of input tokens for output tokens
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of input tokens to swap
    /// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    /// @return amountOut_ The amount of output tokens received from the swap
    function swapIn(
        bool swap0to1_,
        uint256 amountIn_,
        uint256 amountOutMin_,
        address to_
    ) external payable returns (uint256 amountOut_);

    /// @dev Swap tokens with perfect amount out
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountOut_ The exact amount of tokens to receive after swap
    /// @param amountInMax_ Maximum amount of tokens to swap in
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
    /// @return amountIn_ The amount of input tokens used for the swap
    function swapOut(
        bool swap0to1_,
        uint256 amountOut_,
        uint256 amountInMax_,
        address to_
    ) external payable returns (uint256 amountIn_);

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint shares_,
        uint maxToken0Deposit_,
        uint maxToken1Deposit_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint shares_,
        uint minToken0Withdraw_,
        uint minToken1Withdraw_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to borrow
    /// @param minToken0Borrow_ Minimum amount of token0 to borrow
    /// @param minToken1Borrow_ Minimum amount of token1 to borrow
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ Amount of token0 borrowed
    /// @return token1Amt_ Amount of token1 borrowed
    function borrowPerfect(
        uint shares_,
        uint minToken0Borrow_,
        uint minToken1Borrow_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to pay back
    /// @param maxToken0Payback_ Maximum amount of token0 to pay back
    /// @param maxToken1Payback_ Maximum amount of token1 to pay back
    /// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback
    /// @return token0Amt_ Amount of token0 paid back
    /// @return token1Amt_ Amount of token1 paid back
    function paybackPerfect(
        uint shares_,
        uint maxToken0Payback_,
        uint maxToken1Payback_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param estimate_ If true, function will revert with estimated shares without executing the deposit
    /// @return shares_ The amount of shares minted for the deposit
    function deposit(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The number of shares burned for the withdrawal
    function withdraw(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to borrow tokens in any proportion from the debt pool
    /// @param token0Amt_ The amount of token0 to borrow
    /// @param token1Amt_ The amount of token1 to borrow
    /// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The amount of borrow shares minted to represent the borrowed amount
    function borrow(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to payback tokens in any proportion to the debt pool
    /// @param token0Amt_ The amount of token0 to payback
    /// @param token1Amt_ The amount of token1 to payback
    /// @param minSharesAmt_ The minimum amount of shares the user expects to burn
    /// @param estimate_ If true, function will revert with estimated shares without executing the payback
    /// @return shares_ The amount of borrow shares burned for the payback
    function payback(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw their collateral with perfect shares in one token
    /// @param shares_ The number of shares to burn for withdrawal
    /// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1)
    /// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0)
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_
    /// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token
    function withdrawPerfectInOneToken(
        uint shares_,
        uint minToken0_,
        uint minToken1_,
        address to_
    ) external returns (
        uint withdrawAmt_
    );

    /// @dev This function allows users to payback their debt with perfect shares in one token
    /// @param shares_ The number of shares to burn for payback
    /// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1)
    /// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0)
    /// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback
    /// @return paybackAmt_ The amount of tokens paid back in the chosen token
    function paybackPerfectInOneToken(
        uint shares_,
        uint maxToken0_,
        uint maxToken1_,
        bool estimate_
    ) external payable returns (
        uint paybackAmt_
    );

    /// @dev the oracle assumes last set price of pool till the next swap happens.
    /// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration
    /// but the difference due to interest will be super low so this difference is ignored
    /// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other.
    /// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002%
    /// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30]
    /// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints
    /// @return currentPrice_ price of pool after the most recent swap
    function oraclePrice(
        uint[] memory secondsAgos_
    ) external view returns (
        Oracle[] memory twaps_,
        uint currentPrice_
    );
}
structs.sol 163 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct ExchangePrices {
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct CollateralReservesSwap {
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReservesSwap {
        uint tokenInDebt;
        uint tokenOutDebt;
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct SwapInMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtInAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee; // fee of pool
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
    }

    struct SwapOutMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtOutAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee;
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
        uint msgValue;
    }

    struct DepositColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
    }

    struct WithdrawColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
        address to;
    }

    struct BorrowDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
        address to;
    }

    struct PaybackDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
    }

    struct OraclePriceMemory {
        uint lowestPrice1by0;
        uint highestPrice1by0;
        uint oracleSlot;
        uint oracleMap;
        uint oracle;
    }

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndSwapOut;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }
}
main.sol 252 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Owned } from "solmate/src/auth/Owned.sol";
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
import { CREATE3 } from "solmate/src/utils/CREATE3.sol";
import { ErrorTypes } from "../../errorTypes.sol";
import { Error } from "../../error.sol";

abstract contract Constants {
    address public immutable DEX_FACTORY;
    address public immutable LIQUIDITY;
}

abstract contract Variables is Owned {
    // ------------ storage variables from inherited contracts (Owned) come before vars here --------

    // ----------------------- slot 0 ---------------------------
    // address public owner;

    // 12 bytes empty

    // ----------------------- slot 1  ---------------------------
    /// @dev smart lending auths can update specific configs.
    /// owner can add/remove auths.
    /// Owner is auth by default.
    mapping(address => mapping(address => uint256)) internal _smartLendingAuths;

    // ----------------------- slot 2 ---------------------------
    /// @dev deployers can deploy new smartLendings.
    /// owner can add/remove deployers.
    /// Owner is deployer by default.
    mapping(address => uint256) internal _deployers;

    // ----------------------- slot 3 ---------------------------
    /// @notice list of all created tokens.
    /// @dev Solidity creates an automatic getter only to fetch at a certain position, so explicitly define a getter that returns all.
    address[] public createdTokens;

    // ----------------------- slot 4 ---------------------------

    /// @dev smart lending creation code, accessed via SSTORE2.
    address internal _smartLendingCreationCodePointer;

    /*//////////////////////////////////////////////////////////////
                          CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address owner_) Owned(owner_) {}
}

abstract contract Events {
    /// @dev Emitted when a new smart lending is deployed
    /// @param dexId The ID of the deployed DEX
    /// @param smartLending The address of the deployed smart lending
    event LogSmartLendingDeployed(uint256 dexId, address smartLending);

    /// @dev Emitted when a SmartLending auth is updated
    /// @param smartLending address of SmartLending
    /// @param auth address of auth whose status is being updated
    /// @param allowed updated status of auth
    event LogAuthUpdated(address smartLending, address auth, bool allowed);

    /// @dev Emitted when a deployer is modified by owner
    /// @param deployer address of deployer
    /// @param allowed updated status of deployer
    event LogDeployerUpdated(address deployer, bool allowed);

    /// @dev Emitted when the smart lending creation code is modified by owner
    /// @param creationCodePointer address of the creation code pointer
    event LogSetCreationCode(address creationCodePointer);
}

contract FluidSmartLendingFactory is Constants, Variables, Events, Error {
    /// @dev Validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__ZeroAddress);
        }
        _;
    }

    constructor(
        address dexFactory_,
        address liquidity_,
        address owner_
    ) validAddress(dexFactory_) validAddress(liquidity_) validAddress(owner_) Variables(owner_) {
        LIQUIDITY = liquidity_;
        DEX_FACTORY = dexFactory_;
    }

    /// @dev Validates that msg.sender is deployer or owner
    modifier onlyDeployers() {
        if (!isDeployer(msg.sender)) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__Unauthorized);
        }
        _;
    }

    /// @notice List of all created tokens
    function allTokens() public view returns (address[] memory) {
        return createdTokens;
    }

    /// @notice Reads if a certain `auth_` address is an allowed auth for `smartLending_` or not. Owner is auth by default.
    function isSmartLendingAuth(address smartLending_, address auth_) public view returns (bool) {
        return auth_ == owner || _smartLendingAuths[smartLending_][auth_] == 1;
    }

    /// @notice Reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.
    function isDeployer(address deployer_) public view returns (bool) {
        return deployer_ == owner || _deployers[deployer_] == 1;
    }

    /// @dev Retrieves the creation code for the SmartLending contract
    function smartLendingCreationCode() public view returns (bytes memory) {
        return SSTORE2.read(_smartLendingCreationCodePointer);
    }

    /// @notice Sets an address as allowed deployer or not. Only callable by owner.
    /// @param deployer_ Address to set deployer value for
    /// @param allowed_ Bool flag for whether address is allowed as deployer or not
    function updateDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {
        _deployers[deployer_] = allowed_ ? 1 : 0;

        emit LogDeployerUpdated(deployer_, allowed_);
    }

    /// @notice Updates the authorization status of an address for a SmartLending contract. Only callable by owner.
    /// @param smartLending_ The address of the SmartLending contract
    /// @param auth_ The address to be updated
    /// @param allowed_ The new authorization status
    function updateSmartLendingAuth(
        address smartLending_,
        address auth_,
        bool allowed_
    ) external validAddress(smartLending_) validAddress(auth_) onlyOwner {
        _smartLendingAuths[smartLending_][auth_] = allowed_ ? 1 : 0;

        emit LogAuthUpdated(smartLending_, auth_, allowed_);
    }

    /// @notice Sets the `creationCode_` bytecode for new SmartLending contracts. Only callable by owner.
    /// @param creationCode_ New SmartLending contract creation code.
    function setSmartLendingCreationCode(bytes calldata creationCode_) external onlyOwner {
        if (creationCode_.length == 0) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__InvalidParams);
        }

        // write creation code to SSTORE2 pointer and set in mapping
        address creationCodePointer_ = SSTORE2.write(creationCode_);
        _smartLendingCreationCodePointer = creationCodePointer_;

        emit LogSetCreationCode(creationCodePointer_);
    }

    /// @notice Spell allows owner aka governance to do any arbitrary call on factory
    /// @param target_ Address to which the call needs to be delegated
    /// @param data_ Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @notice Deploys a new SmartLending contract. Only callable by deployers.
    /// @param dexId_ The ID of the DEX for which the smart lending wrapper is being deployed
    /// @return smartLending_ The newly deployed SmartLending contract
    function deploy(uint256 dexId_) public onlyDeployers returns (address smartLending_) {
        if (getSmartLendingAddress(dexId_).code.length != 0) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__AlreadyDeployed);
        }

        // Use CREATE3 for deterministic deployments. Unfortunately it has 55k gas overhead
        smartLending_ = CREATE3.deploy(
            _getSalt(dexId_),
            abi.encodePacked(
                SSTORE2.read(_smartLendingCreationCodePointer), // creation code
                abi.encode(dexId_, LIQUIDITY, DEX_FACTORY, address(this)) // constructor params
            ),
            0
        );

        createdTokens.push(smartLending_); // Add the created token to the allTokens array

        emit LogSmartLendingDeployed(dexId_, smartLending_);
    }

    /// @notice Computes the address of a SmartLending contract based on a given dexId.
    /// @param dexId_ The ID of the DEX for which the SmartLending contract address is being computed.
    /// @return The computed address of the SmartLending contract.
    function getSmartLendingAddress(uint256 dexId_) public view returns (address) {
        return CREATE3.getDeployed(_getSalt(dexId_));
    }

    /// @notice Returns the total number of SmartLending contracts deployed by the factory.
    /// @return The total number of SmartLending contracts deployed.
    function totalSmartLendings() external view returns (uint256) {
        return createdTokens.length;
    }

    /// @notice                         Checks if a given address (`smartLending_`) corresponds to a valid smart lending.
    /// @param smartLending_            The smart lending address to check.
    /// @return                         Returns `true` if the given address corresponds to a valid smart lending, otherwise `false`.
    function isSmartLending(address smartLending_) public view returns (bool) {
        if (smartLending_.code.length == 0) {
            return false;
        } else {
            // DEX() function signature is 0x80935aa9
            (bool success_, bytes memory data_) = smartLending_.staticcall(hex"80935aa9");
            address dex_ = abi.decode(data_, (address));
            // DEX_ID() function signature is 0xf4b9a3fb
            (success_, data_) = dex_.staticcall(hex"f4b9a3fb");
            return success_ && smartLending_ == getSmartLendingAddress(abi.decode(data_, (uint256)));
        }
    }

    /// @dev unique deployment salt for the smart lending
    function _getSalt(uint256 dexId_) internal pure returns (bytes32) {
        return keccak256(abi.encode(dexId_));
    }

    /// @dev Deploys a contract using the CREATE opcode with the provided bytecode (`bytecode_`).
    /// This is an internal function, meant to be used within the contract to facilitate the deployment of other contracts.
    /// @param bytecode_ The bytecode of the contract to be deployed.
    /// @return address_ Returns the address of the deployed contract.
    function _deploy(bytes memory bytecode_) internal returns (address address_) {
        if (bytecode_.length == 0) {
            revert FluidDexError(ErrorTypes.SmartLendingFactory__InvalidOperation);
        }
        /// @solidity memory-safe-assembly
        assembly {
            address_ := create(0, add(bytecode_, 0x20), mload(bytecode_))
        }
        if (address_ == address(0)) {
            revert FluidDexError(ErrorTypes.SmartLendingFactory__InvalidOperation);
        }
    }
}
main.sol 577 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

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

import { IFluidDexT1 } from "../interfaces/iDexT1.sol";
import { FluidDexFactory } from "../factory/main.sol";
import { FluidSmartLendingFactory } from "./factory/main.sol";
import { SafeTransfer } from "../../../libraries/safeTransfer.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error } from "../error.sol";
import { DexSlotsLink } from "../../../libraries/dexSlotsLink.sol";
import { DexCalcs } from "../../../libraries/dexCalcs.sol";

abstract contract Constants {
    /// @dev Ignoring leap years
    uint256 internal constant SECONDS_PER_YEAR = 365 days;

    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;

    FluidDexFactory public immutable DEX_FACTORY;

    FluidSmartLendingFactory public immutable SMART_LENDING_FACTORY;

    IFluidDexT1 public immutable DEX;

    address public immutable LIQUIDITY;

    address public immutable TOKEN0;

    address public immutable TOKEN1;

    bool public immutable IS_NATIVE_PAIR;
}

abstract contract Variables is ERC20, Constants {
    // ------------ storage variables from inherited contracts come before vars here --------
    // _________ ERC20 _______________
    // ----------------------- slot 0 ---------------------------
    // mapping(address => uint256) private _balances;

    // ----------------------- slot 1 ---------------------------
    // mapping(address => mapping(address => uint256)) private _allowances;

    // ----------------------- slot 2 ---------------------------
    // uint256 private _totalSupply;

    // ----------------------- slot 3 ---------------------------
    // string private _name;
    // ----------------------- slot 4 ---------------------------
    // string private _symbol;

    // ------------ storage variables ------------------------------------------------------

    // ----------------------- slot 5 ---------------------------
    uint40 public lastTimestamp;
    /// If positive then rewards, if negative then fee.
    /// 1e6 = 100%, 1e4 = 1%, minimum 0.0001% fee or reward.
    int32 public feeOrReward;
    // Starting from 1e18
    // If fees then reduce exchange price
    // If reward then increase exchange price
    uint184 public exchangePrice;

    // ----------------------- slot 6 ---------------------------
    address public rebalancer;

    // ----------------------- slot 7 ---------------------------
    address public dexFromAddress;

    /// @dev status for reentrancy guard
    uint8 internal _status;
}

abstract contract Events {
    /// @dev Emitted when the share to tokens ratio is rebalanced
    /// @param shares_ The number of shares rebalanced
    /// @param token0Amt_ The amount of token0 rebalanced
    /// @param token1Amt_ The amount of token1 rebalanced
    /// @param isWithdraw_ Whether the rebalance is a withdrawal or deposit
    event LogRebalance(uint256 shares_, uint256 token0Amt_, uint256 token1Amt_, bool isWithdraw_);

    /// @dev Emitted when the rebalancer is set
    /// @param rebalancer The new rebalancer
    event LogRebalancerSet(address rebalancer);

    /// @dev Emitted when the fee or reward is set
    /// @param feeOrReward The new fee or reward
    event LogFeeOrRewardSet(int256 feeOrReward);
}

/// @dev ReentrancyGuard based on OpenZeppelin implementation.
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol
abstract contract ReentrancyGuard is Variables, Error {
    uint8 internal constant REENTRANCY_NOT_ENTERED = 1;
    uint8 internal constant REENTRANCY_ENTERED = 2;

    constructor() {
        _status = REENTRANCY_NOT_ENTERED;
    }

    /// @dev Prevents a contract from calling itself, directly or indirectly.
    /// See OpenZeppelin implementation for more info
    modifier nonReentrant() {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status != REENTRANCY_NOT_ENTERED) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Reentrancy);
        }

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

        _;

        // storing original value triggers a refund (see https://eips.ethereum.org/EIPS/eip-2200)
        _status = REENTRANCY_NOT_ENTERED;
    }
}

contract FluidSmartLending is ERC20, Variables, Error, ReentrancyGuard, Events {
    /// @dev prefix for token name. constructor appends dex id, e.g. "Fluid Smart Lending 12"
    string private constant TOKEN_NAME_PREFIX = "Fluid Smart Lending ";
    /// @dev prefix for token symbol. constructor appends dex id, e.g. "fSL12"
    string private constant TOKEN_SYMBOL_PREFIX = "fSL";

    /// @dev Validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__ZeroAddress);
        }
        _;
    }

    constructor(
        uint256 dexId_,
        address liquidity_,
        address dexFactory_,
        address smartLendingFactory_
    )
        ERC20(
            string(abi.encodePacked(TOKEN_NAME_PREFIX, _toString(dexId_))),
            string(abi.encodePacked(TOKEN_SYMBOL_PREFIX, _toString(dexId_)))
        )
        validAddress(liquidity_)
        validAddress(dexFactory_)
        validAddress(smartLendingFactory_)
    {
        LIQUIDITY = liquidity_;
        DEX_FACTORY = FluidDexFactory(dexFactory_);
        SMART_LENDING_FACTORY = FluidSmartLendingFactory(smartLendingFactory_);
        DEX = IFluidDexT1(DEX_FACTORY.getDexAddress(dexId_));
        IFluidDexT1.ConstantViews memory constants_ = DEX.constantsView();
        TOKEN0 = constants_.token0;
        TOKEN1 = constants_.token1;
        IS_NATIVE_PAIR = (TOKEN0 == ETH_ADDRESS) || (TOKEN1 == ETH_ADDRESS);

        exchangePrice = uint184(1e18);
        feeOrReward = int32(0);
        lastTimestamp = uint40(block.timestamp);

        dexFromAddress = DEAD_ADDRESS;
    }

    modifier setDexFrom() {
        dexFromAddress = msg.sender;
        _;
        dexFromAddress = DEAD_ADDRESS;
    }

    modifier onlyAuth() {
        if (!SMART_LENDING_FACTORY.isSmartLendingAuth(address(this), msg.sender)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        _;
    }

    modifier onlyOwner() {
        if (msg.sender != SMART_LENDING_FACTORY.owner()) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        _;
    }

    modifier _updateExchangePrice() {
        bool rewardsOrFeeActive_;
        (exchangePrice, rewardsOrFeeActive_) = getUpdateExchangePrice();
        if (rewardsOrFeeActive_) {
            lastTimestamp = uint40(block.timestamp); // only write to storage if fee or reward is active.
        }
        _;
    }

    /// @notice gets updated exchange price
    function getUpdateExchangePrice() public view returns (uint184 exchangePrice_, bool rewardsOrFeeActive_) {
        int256 feeOrReward_ = feeOrReward;
        exchangePrice_ = exchangePrice;
        if (feeOrReward_ > 0) {
            exchangePrice_ =
                exchangePrice_ +
                uint184(
                    (exchangePrice_ * uint256(feeOrReward_) * (block.timestamp - uint256(lastTimestamp))) /
                        (1e6 * SECONDS_PER_YEAR)
                );
            rewardsOrFeeActive_ = true;
        } else if (feeOrReward_ < 0) {
            exchangePrice_ =
                exchangePrice_ -
                uint184(
                    (exchangePrice_ * uint256(-feeOrReward_) * (block.timestamp - uint256(lastTimestamp))) /
                        (1e6 * SECONDS_PER_YEAR)
                );
            rewardsOrFeeActive_ = true;
        }
    }

    /// @notice triggers updateExchangePrice
    function updateExchangePrice() public _updateExchangePrice {}

    /// @dev Set the fee or reward. Only callable by auths.
    /// @param feeOrReward_ The new fee or reward (1e6 = 100%, 1e4 = 1%, minimum 0.0001% fee or reward). 0 means no fee or reward
    function setFeeOrReward(int256 feeOrReward_) external onlyAuth _updateExchangePrice {
        if (feeOrReward_ > 1e6 || feeOrReward_ < -1e6) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__OutOfRange);
        }
        lastTimestamp = uint40(block.timestamp); // current fee or reward setting is applied until exactly now even if previously 0
        feeOrReward = int32(feeOrReward_);

        emit LogFeeOrRewardSet(feeOrReward_);
    }

    /// @dev Set the rebalancer. Only callable by auths.
    /// @param rebalancer_ The new rebalancer
    function setRebalancer(address rebalancer_) external onlyAuth validAddress(rebalancer_) {
        rebalancer = rebalancer_;

        emit LogRebalancerSet(rebalancer_);
    }

    /// @notice                         Spell allows auths (governance) to do any arbitrary call
    /// @param target_                  Address to which the call needs to be delegated
    /// @param data_                    Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param to_ Recipient of minted tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ Amount of tokens minted
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint256 shares_,
        uint256 maxToken0Deposit_,
        uint256 maxToken1Deposit_,
        address to_
    )
        external
        payable
        setDexFrom
        _updateExchangePrice
        nonReentrant
        returns (uint256 amount_, uint256 token0Amt_, uint256 token1Amt_)
    {
        if (!IS_NATIVE_PAIR) {
            if (msg.value > 0) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            (token0Amt_, token1Amt_) = DEX.depositPerfect(
                shares_ + 1, // + 1 rounding up but only minting shares
                maxToken0Deposit_,
                maxToken1Deposit_,
                false
            );
        } else {
            uint256 value_ = TOKEN0 == ETH_ADDRESS ? maxToken0Deposit_ : maxToken1Deposit_;
            if (value_ > msg.value) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            uint256 initialEthAmount_ = address(this).balance - msg.value;

            (token0Amt_, token1Amt_) = DEX.depositPerfect{ value: value_ }(
                shares_ + 1, // + 1 rounding up but only minting shares
                maxToken0Deposit_,
                maxToken1Deposit_,
                false
            );

            uint finalEth_ = payable(address(this)).balance;
            if (finalEth_ > initialEthAmount_) {
                unchecked {
                    SafeTransfer.safeTransferNative(msg.sender, finalEth_ - initialEthAmount_); // sending back excess ETH
                }
            }
        }

        to_ = to_ == address(0) ? msg.sender : to_;

        amount_ = (shares_ * 1e18) / exchangePrice;

        _mint(to_, amount_);
    }

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param to_ Recipient of minted tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ The amount of tokens minted for the deposit
    /// @return shares_ The number of dex pool shares deposited
    function deposit(
        uint256 token0Amt_,
        uint256 token1Amt_,
        uint256 minSharesAmt_,
        address to_
    ) external payable setDexFrom _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 shares_) {
        uint256 value_ = !IS_NATIVE_PAIR
            ? 0
            : (TOKEN0 == ETH_ADDRESS)
                ? token0Amt_
                : token1Amt_;

        if (value_ != msg.value) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
        }

        to_ = to_ == address(0) ? msg.sender : to_;

        shares_ = DEX.deposit{ value: value_ }(token0Amt_, token1Amt_, minSharesAmt_, false);

        amount_ = (shares_ * 1e18) / exchangePrice - 1;

        _mint(to_, amount_);
    }

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw. set to type(uint).max to withdraw maximum balance.
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ amount_ of shares actually burnt
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint256 shares_,
        uint256 minToken0Withdraw_,
        uint256 minToken1Withdraw_,
        address to_
    ) external _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 token0Amt_, uint256 token1Amt_) {
        if (shares_ == type(uint).max) {
            amount_ = balanceOf(msg.sender);
            shares_ = (amount_ * exchangePrice) / 1e18 - 1;
        } else {
            amount_ = (shares_ * 1e18) / exchangePrice + 1;
        }

        _burn(msg.sender, amount_);

        to_ = to_ == address(0) ? msg.sender : to_;

        if (minToken0Withdraw_ > 0 && minToken1Withdraw_ > 0) {
            (token0Amt_, token1Amt_) = DEX.withdrawPerfect(shares_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else if (minToken0Withdraw_ > 0 && minToken1Withdraw_ == 0) {
            // withdraw only in token0, token1Amt_ remains 0
            (token0Amt_) = DEX.withdrawPerfectInOneToken(shares_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else if (minToken0Withdraw_ == 0 && minToken1Withdraw_ > 0) {
            // withdraw only in token1, token0Amt_ remains 0
            (token1Amt_) = DEX.withdrawPerfectInOneToken(shares_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else {
            // meaning user sent both amounts as == 0
            revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidAmounts);
        }
    }

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return amount_ The number of tokens burned for the withdrawal
    /// @return shares_ The number of dex pool shares withdrawn
    function withdraw(
        uint256 token0Amt_,
        uint256 token1Amt_,
        uint256 maxSharesAmt_,
        address to_
    ) external _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 shares_) {
        to_ = to_ == address(0) ? msg.sender : to_;

        shares_ = DEX.withdraw(token0Amt_, token1Amt_, maxSharesAmt_, to_);

        amount_ = (shares_ * 1e18) / exchangePrice + 1;

        _burn(msg.sender, amount_);
    }

    /// @dev Rebalances the share to tokens ratio to balance out rewards and fees
    function rebalance(
        uint256 minOrMaxToken0_,
        uint256 minOrMaxToken1_
    )
        public
        payable
        _updateExchangePrice
        nonReentrant
        returns (uint256 shares_, uint256 token0Amt_, uint256 token1Amt_, bool isWithdraw_)
    {
        if (rebalancer != msg.sender) revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidRebalancer);

        int256 rebalanceDiff_ = rebalanceDiff();

        if (rebalanceDiff_ > 0) {
            // fees (withdraw)
            isWithdraw_ = true;
            if (msg.value > 0) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }
            shares_ = uint256(rebalanceDiff_);
            (token0Amt_, token1Amt_) = DEX.withdrawPerfect(shares_, minOrMaxToken0_, minOrMaxToken1_, msg.sender);
        } else if (rebalanceDiff_ < 0) {
            // rewards (deposit)
            isWithdraw_ = false;

            uint256 initialEthAmount_ = address(this).balance - msg.value;

            uint256 value_ = !IS_NATIVE_PAIR
                ? 0
                : (TOKEN0 == ETH_ADDRESS)
                    ? minOrMaxToken0_
                    : minOrMaxToken1_;

            if (value_ > msg.value) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            shares_ = uint256(-rebalanceDiff_);

            dexFromAddress = msg.sender;
            (token0Amt_, token1Amt_) = DEX.depositPerfect{ value: value_ }(
                shares_,
                minOrMaxToken0_,
                minOrMaxToken1_,
                false
            );
            dexFromAddress = DEAD_ADDRESS;

            uint finalEth_ = payable(address(this)).balance;
            if (finalEth_ > initialEthAmount_) {
                unchecked {
                    SafeTransfer.safeTransferNative(msg.sender, finalEth_ - initialEthAmount_); // sending back excess ETH
                }
            }
        }

        emit LogRebalance(shares_, token0Amt_, token1Amt_, isWithdraw_);
    }

    /// @dev Returns the difference between the total smart lending shares on the DEX and the total smart lending shares calculated.
    /// A positive value indicates fees to collect, while a negative value indicates rewards to be rebalanced.
    function rebalanceDiff() public view returns (int256) {
        uint256 totalSmartLendingSharesOnDex_ = DEX.readFromStorage(
            DexSlotsLink.calculateMappingStorageSlot(DexSlotsLink.DEX_USER_SUPPLY_MAPPING_SLOT, address(this))
        );
        totalSmartLendingSharesOnDex_ =
            (totalSmartLendingSharesOnDex_ >> DexSlotsLink.BITS_USER_SUPPLY_AMOUNT) &
            DexCalcs.X64;
        totalSmartLendingSharesOnDex_ =
            (totalSmartLendingSharesOnDex_ >> DexCalcs.DEFAULT_EXPONENT_SIZE) <<
            (totalSmartLendingSharesOnDex_ & DexCalcs.DEFAULT_EXPONENT_MASK);

        uint256 totalSmartLendingShares_ = (totalSupply() * exchangePrice) / 1e18;

        return int256(totalSmartLendingSharesOnDex_) - int256(totalSmartLendingShares_);
    }

    /// @notice   dex liquidity callback
    /// @param    token_ The token being transferred
    /// @param    amount_ The amount being transferred
    function dexCallback(address token_, uint256 amount_) external {
        if (msg.sender != address(DEX)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        SafeTransfer.safeTransferFrom(token_, dexFromAddress, LIQUIDITY, amount_);
    }

    /// @dev for excess eth being sent back from dex to here
    receive() external payable {
        if (msg.sender != address(DEX)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     * taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol
     */
    function _log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     * taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol
     */
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    function _toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = _log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }
}
Owned.sol 44 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}
Bytes32AddressLib.sol 14 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
    function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {
        return address(uint160(uint256(bytesValue)));
    }

    function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {
        return bytes32(bytes20(addressValue));
    }
}
CREATE3.sol 83 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/// @notice Deploy to deterministic addresses without an initcode factor.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol)
library CREATE3 {
    using Bytes32AddressLib for bytes32;

    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 0 size               //
    // 0x37       |  0x37                 | CALLDATACOPY     |                        //
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x34       |  0x34                 | CALLVALUE        | value 0 size           //
    // 0xf0       |  0xf0                 | CREATE           | newContract            //
    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x67       |  0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode   | bytecode               //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 bytecode             //
    // 0x52       |  0x52                 | MSTORE           |                        //
    // 0x60       |  0x6008               | PUSH1 08         | 8                      //
    // 0x60       |  0x6018               | PUSH1 18         | 24 8                   //
    // 0xf3       |  0xf3                 | RETURN           |                        //
    //--------------------------------------------------------------------------------//
    bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3";

    bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);

    function deploy(
        bytes32 salt,
        bytes memory creationCode,
        uint256 value
    ) internal returns (address deployed) {
        bytes memory proxyChildBytecode = PROXY_BYTECODE;

        address proxy;
        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with our pre-made bytecode via CREATE2.
            // We start 32 bytes into the code to avoid copying the byte length.
            proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt)
        }
        require(proxy != address(0), "DEPLOYMENT_FAILED");

        deployed = getDeployed(salt);
        (bool success, ) = proxy.call{value: value}(creationCode);
        require(success && deployed.code.length != 0, "INITIALIZATION_FAILED");
    }

    function getDeployed(bytes32 salt) internal view returns (address) {
        address proxy = keccak256(
            abi.encodePacked(
                // Prefix:
                bytes1(0xFF),
                // Creator:
                address(this),
                // Salt:
                salt,
                // Bytecode hash:
                PROXY_BYTECODE_HASH
            )
        ).fromLast20Bytes();

        return
            keccak256(
                abi.encodePacked(
                    // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01)
                    // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
                    hex"d6_94",
                    proxy,
                    hex"01" // Nonce of the proxy contract (1)
                )
            ).fromLast20Bytes();
    }
}
SSTORE2.sol 101 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}

Read Contract

DEX 0x80935aa9 → address
DEX_FACTORY 0x97b87b4a → address
IS_NATIVE_PAIR 0xddd59b9f → bool
LIQUIDITY 0x2861c7d1 → address
SMART_LENDING_FACTORY 0xf53eddcc → address
TOKEN0 0x443ec74d → address
TOKEN1 0x5ee04d78 → address
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
dexFromAddress 0xbd4dbda0 → address
exchangePrice 0x9e65741e → uint184
feeOrReward 0xe47a882d → int32
getUpdateExchangePrice 0xc89c74ea → uint184, bool
lastTimestamp 0x19d8ac61 → uint40
name 0x06fdde03 → string
rebalanceDiff 0x92eb1656 → int256
rebalancer 0x01d22ccd → address
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256

Write Contract 15 functions

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

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
decreaseAllowance 0xa457c2d7
address spender
uint256 subtractedValue
returns: bool
deposit 0xfad3cc4b
uint256 token0Amt_
uint256 token1Amt_
uint256 minSharesAmt_
address to_
returns: uint256, uint256
depositPerfect 0x00c3cdae
uint256 shares_
uint256 maxToken0Deposit_
uint256 maxToken1Deposit_
address to_
returns: uint256, uint256, uint256
dexCallback 0x9410ae88
address token_
uint256 amount_
increaseAllowance 0x39509351
address spender
uint256 addedValue
returns: bool
rebalance 0xdf8879b8
uint256 minOrMaxToken0_
uint256 minOrMaxToken1_
returns: uint256, uint256, uint256, bool
setFeeOrReward 0x4fa1aa8a
int256 feeOrReward_
setRebalancer 0x6cfd1553
address rebalancer_
spell 0xc7acb01f
address target_
bytes data_
returns: bytes
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
updateExchangePrice 0x3bfaa7e3
No parameters
withdraw 0xd331bef7
uint256 token0Amt_
uint256 token1Amt_
uint256 maxSharesAmt_
address to_
returns: uint256, uint256
withdrawPerfect 0x35f0df98
uint256 shares_
uint256 minToken0Withdraw_
uint256 minToken1Withdraw_
address to_
returns: uint256, uint256, uint256

Recent Transactions

No transactions found for this address