Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x109e4749F7c429DcC8ab84fE9fD587e2acf912aF
Balance 0 ETH
Nonce 1
Code Size 23503 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23503 bytes
0x608060405234801561001057600080fd5b50600436106103275760003560e01c806366258068116101b8578063a7c5b41311610104578063d8697598116100a2578063e5261b1d1161007c578063e5261b1d14610872578063eec47d6014610885578063f96d7b801461088f578063fb3ee5711461089857600080fd5b8063d86975981461082e578063e2481dbd14610855578063e449f3411461085f57600080fd5b8063be864d68116100de578063be864d68146107eb578063c72460ee146107fe578063d449300d14610808578063d4570c1c1461081b57600080fd5b8063a7c5b41314610743578063ac9650d8146107b8578063bc88d7e4146107d857600080fd5b80638456cb591161017157806391d148541161014b57806391d14854146106ea578063946d9204146106fd578063a488b71e14610710578063a694fc3a1461073057600080fd5b80638456cb59146106bc5780638b0bd55b146106c457806390672ad8146106d757600080fd5b806366258068146105cd57806367b4cdb8146105e25780636fa3fbda1461060c57806372f702f31461061f57806379502c5514610631578063817b1cd2146106b357600080fd5b80633d173136116102775780634b63885a11610230578063509b6c3f1161020a578063509b6c3f146105605780635c975abb1461058b5780635f3e849f1461059357806363727fc2146105a657600080fd5b80634b63885a146105275780634e6e056f1461053a5780634f814cc51461054d57600080fd5b80633d173136146104b35780633e677297146104bc5780633ee16bf7146104cf5780633f4ba83a146104e257806342623360146104ea57806349aeb46f1461051457600080fd5b8063248a9ca3116102e457806330139280116102be578063301392801461040f578063350b5af0146104365780633a22502c146104495780633cfe9fd11461049a57600080fd5b8063248a9ca3146103c25780632772ce9d146103d55780632a7a0c68146103e857600080fd5b806301ffc9a71461032c5780631b0875a6146103545780631c03e6cc1461036a5780631d8b4c491461037f5780631e26571f14610392578063227102a61461039b575b600080fd5b61033f61033a3660046150f2565b6108ab565b60405190151581526020015b60405180910390f35b61035c6108d6565b60405190815260200161034b565b61037d610378366004615141565b6108e8565b005b61037d61038d3660046151a9565b610a72565b6101945461035c565b61035c7ff28c151dcc22c5bfb460fbc387993d6c0d1c1245ca20aca5f3cbd33f33041eb181565b61035c6103d0366004615214565b610c94565b61033f6103e3366004615214565b610d86565b61035c7f000000000000000000000000000000000000000000000000ffffffffffffffff81565b61035c7f520767f2ee41b25d23e3166443601203d348640efb766878c95999973ecd38e181565b61037d61044436600461522d565b610e49565b61045c610457366004615266565b610fb2565b6040805182516001600160c01b03191681526020808401516001600160401b031690820152918101516001600160801b03169082015260600161034b565b6101d254600160c01b90046001600160401b031661035c565b6101ca5461035c565b61035c6104ca366004615141565b610fef565b61037d6104dd366004615214565b611048565b61037d61121a565b61035c6104f8366004615141565b6001600160a01b031660009081526101cf602052604090205490565b61037d6105223660046152d0565b6113a9565b61033f610535366004615141565b6114d6565b61035c610548366004615141565b6115e0565b61037d61055b366004615324565b61166d565b61057361056e366004615214565b611a44565b6040516001600160a01b03909116815260200161034b565b61033f611a52565b61037d6105a1366004615266565b611ae6565b6105737f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf81565b61035c600080516020615ba383398151915281565b61035c6105f0366004615141565b6001600160a01b031660009081526101ce602052604090205490565b61037d61061a3660046151a9565b611c57565b6101cb546001600160a01b0316610573565b6106a660408051608081018252600080825260208201819052918101829052606081019190915250604080516080810182526101cd546001600160401b038082168352600160401b820416602083015261ffff600160801b8204811693830193909352600160901b9004909116606082015290565b60405161034b9190615363565b6101c95461035c565b61037d611dea565b61037d6106d23660046153a9565b611f76565b61037d6106e53660046151a9565b61207c565b61033f6106f83660046153ea565b61228c565b61037d61070b366004615425565b612383565b61072361071e3660046153a9565b61263e565b60405161034b9190615539565b61037d61073e366004615214565b612814565b610756610751366004615214565b612922565b60405161034b9190815165ffffffffffff9081168252602080840151909116908201526040808301516001600160a01b0316908201526060808301516001600160801b0390811691830191909152608092830151169181019190915260a00190565b6107cb6107c63660046153a9565b6129ee565b60405161034b919061559c565b6107566107e6366004615214565b612ae2565b61035c6107f9366004615214565b612b9b565b61035c6101c85481565b61035c61081636600461522d565b612c92565b61035c61082936600461522d565b612cc9565b61035c7f0b8459b722d944f0580da74c767d2dcae280ce16527c19c825b302a24e208dfd81565b61035c6101c75481565b61037d61086d3660046153a9565b612d44565b61045c6108803660046155fe565b612e67565b61035c6101935481565b6101d75461035c565b61037d6108a6366004615141565b612e9f565b60006001600160e01b0319821663941be9cf60e01b14806108d057506108d082613184565b92915050565b60006108e36101d56131a9565b905090565b33610901600080516020615ba38339815191528261228c565b8061091457506109146101c7548261228c565b6109395760405162461bcd60e51b81526004016109309061562a565b60405180910390fd5b60026001540361095b5760405162461bcd60e51b815260040161093090615657565b600260015561096c6101d5836131b3565b6109b85760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e673a205245574152445f544f4b454e5f455849535453000000006044820152606401610930565b60406109c56101d56131a9565b1115610a045760405162461bcd60e51b815260206004820152600e60248201526d14dd185ada5b99ce88131253525560921b6044820152606401610930565b6001600160a01b0382163b610a2b5760405162461bcd60e51b81526004016109309061568e565b610a34826131c8565b60405133906001600160a01b038416907f3344e0a0f48738979c56a1b9f2cd3425597f76766d53e83439cab3fc30b067c790600090a3505060018055565b600260015403610a945760405162461bcd60e51b815260040161093090615657565b600260015533610aa3816114d6565b610abf5760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf6020526040902085610b1c5760405162461bcd60e51b815260206004820152601e60248201527f5374616b696e673a20494e56414c49445f4153534554535f4c454e47544800006044820152606401610930565b838614610b6b5760405162461bcd60e51b815260206004820152601e60248201527f5374616b696e673a20494e56414c49445f494e5055545f4c454e4754485300006044820152606401610930565b60005b86811015610c86576000868683818110610b8a57610b8a6156f5565b9050602002810190610b9c919061570b565b905090506000898984818110610bb457610bb46156f5565b9050602002016020810190610bc99190615141565b90506000808311610bec5760405162461bcd60e51b815260040161093090615754565b60005b83811015610c5257610c3e8a8a87818110610c0c57610c0c6156f5565b9050602002810190610c1e919061570b565b83818110610c2e57610c2e6156f5565b90506020020135848960016131d8565b90910190610c4b816157a1565b9050610bef565b506001600160a01b03909116600090815260018501602052604090208054909101905550610c7f816157a1565b9050610b6e565b505060018055505050505050565b60007f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b03166371907f176040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1891906157ba565b6001600160a01b031663248a9ca3836040518263ffffffff1660e01b8152600401610d4591815260200190565b602060405180830381865afa158015610d62573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d091906157d7565b6000610d96610193546106f83390565b80610dc65750610dc67ff28c151dcc22c5bfb460fbc387993d6c0d1c1245ca20aca5f3cbd33f33041eb13361228c565b610e385760405162461bcd60e51b815260206004820152603860248201527f477561726461626c653a20796f7520646f206e6f74206861766520746865207260448201527f6571756972656420726f6c657320746f20646f207468697300000000000000006064820152608401610930565b610e4182613239565b506001919050565b33610e62600080516020615ba38339815191528261228c565b80610e755750610e756101c7548261228c565b610e915760405162461bcd60e51b81526004016109309061562a565b600260015403610eb35760405162461bcd60e51b815260040161093090615657565b60026001556101c954610ec59061327e565b6001600160a01b03831660009081526101d0602052604090205480610f2c5760405162461bcd60e51b815260206004820152601f60248201527f5374616b696e673a20494e53554646494349454e545f434c41494d41424c45006044820152606401610930565b6001600160a01b03841660008181526101d06020526040812055610f519084836134c8565b336001600160a01b0316836001600160a01b0316856001600160a01b03167feb4184e6deaf4d9d0984cd22dcacac383ed3c67600403e9d48a877f32843260084604051610fa091815260200190565b60405180910390a45050600180555050565b6040805160608101825260008082526020820181905291810191909152610fd88361352b565b610fe58284866001613553565b90505b9392505050565b6101c9546000908181611003576000611018565b6110186101c95461101386613656565b613768565b6001600160a01b03851660009081526101ce60205260409020549091506110409082906157f0565b949350505050565b60026001540361106a5760405162461bcd60e51b815260040161093090615657565b600260015533611079816114d6565b6110955760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf602090815260409182902080546101c95484516080810186526101cd546001600160401b038082168352600160401b8204169582019590955261ffff600160801b8604811696820196909652600160901b9094049094166060840152909290918683101561111e5760405162461bcd60e51b815260040161093090615803565b6000871161113e5760405162461bcd60e51b81526004016109309061583a565b6111478261327e565b61115b84846111568a82615871565b61378a565b8682036101c955604081015160009061117990899061ffff16613867565b6101cb546001600160a01b031660008181526101d06020526040812080549394509192849291906111ab9084906157f0565b909155506111d09050876111bf848c615871565b6001600160a01b03841691906134c8565b604080518a81526020810184905233917f0caa32aefed7436cb5c128d4c61517c3b4fd8ca09590ecadc0545441a7954864910160405180910390a250506001805550505050505050565b6000339050806001600160a01b03167f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b031663ee97f7f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ab91906157ba565b6001600160a01b031614806113525750806001600160a01b03167f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b0316632d37ead56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611323573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134791906157ba565b6001600160a01b0316145b61139e5760405162461bcd60e51b815260206004820152601f60248201527f534f4d413a204d4153544552206f7220535542204d4153544552206f6e6c79006044820152606401610930565b6113a6613876565b50565b336113c2600080516020615ba38339815191528261228c565b806113d557506113d56101c7548261228c565b6113f15760405162461bcd60e51b81526004016109309061562a565b604080516080810182526001600160401b0387811682528616602082015261ffff8581168284015284166060820152905133907f9462329a02c37f74ff4c4fcc54801197381851fde6531472fd444620fbf0fd9d90611455906101cd908590615884565b60405180910390a280516101cd8054602084015160408501516060909501516001600160401b039485166001600160801b031990931692909217600160401b94909116939093029290921763ffffffff60801b1916600160801b61ffff9485160261ffff60901b191617600160901b93909216929092021790555050505050565b60007f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b0316637ceab3b16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611536573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155a91906157ba565b6001600160a01b031663253bd7b7836115736101945490565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156115bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d091906158f7565b6000806115ef6101d5846138c8565b9050808061160b57506101cb546001600160a01b038481169116145b6116275760405162461bcd60e51b81526004016109309061568e565b6101c9541580156116355750805b611640576000611649565b61164983613656565b6001600160a01b03841660009081526101d06020526040902054610fe891906157f0565b33611686600080516020615ba38339815191528261228c565b8061169957506116996101c7548261228c565b6116b55760405162461bcd60e51b81526004016109309061562a565b6002600154036116d75760405162461bcd60e51b815260040161093090615657565b60026001556001600160801b038211156117335760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e673a204d41585f5245574152445f414d4f554e540000000000006044820152606401610930565b4285116117825760405162461bcd60e51b815260206004820152601b60248201527f5374616b696e673a20494e56414c49445f53544152545f4441544500000000006044820152606401610930565b8385106117d15760405162461bcd60e51b815260206004820152601b60248201527f5374616b696e673a20494e56414c49445f444154455f4f5244455200000000006044820152606401610930565b65ffffffffffff8411156118275760405162461bcd60e51b815260206004820152601960248201527f5374616b696e673a20494e56414c49445f454e445f44415445000000000000006044820152606401610930565b6118308361352b565b61183c833330856138ea565b91506000821161185e5760405162461bcd60e51b81526004016109309061583a565b6101d754604081106118a35760405162461bcd60e51b815260206004820152600e60248201526d14dd185ada5b99ce88131253525560921b6044820152606401610930565b60006040518060a001604052808865ffffffffffff1681526020018765ffffffffffff168152602001866001600160a01b031681526020016118e4866139f1565b6001600160801b039081168252600060209283018190526101d780546001810182559152835160029091027f069f7b5e1776a2d4bf57d8efb3ff6de682dcba14407464b37d674a8bd8984923810180549486015160408701516001600160a01b0316600160601b026bffffffffffffffffffffffff65ffffffffffff928316600160301b026bffffffffffffffffffffffff1990981692909516919091179590951792909216939093179055606083015160808401518216600160801b029116177f069f7b5e1776a2d4bf57d8efb3ff6de682dcba14407464b37d674a8bd89849249091015590506119d96101d28389613a5e565b33606080830151604080516001600160801b039092168252602082018b905281018990526001600160a01b03928316928816917f4b43c543dc2da7e74df4d701157b31ed22b9fb6a39f9c5329fff7f250b65a872910160405180910390a35050600180555050505050565b60006108d06101d583614055565b60007f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ab2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad691906158f7565b806108e357505060655460ff1690565b7f0b8459b722d944f0580da74c767d2dcae280ce16527c19c825b302a24e208dfd611b11813361228c565b611b8d5760405162461bcd60e51b815260206004820152604160248201527f536f6d61416363657373436f6e74726f6c3a2063616c6c657220646f6573206e60448201527f6f7420686176652074686520617070726f70726961746520617574686f7269746064820152607960f81b608482015260a401610930565b611b9961015f856138c8565b15611be65760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e5265636f766572793a20494e56414c49445f544f4b454e000000006044820152606401610930565b611bfa6001600160a01b03851684846134c8565b336001600160a01b0316836001600160a01b0316856001600160a01b03167fca574176b1d34032f40f7fb0c519eae468b4535ebf7b27f986a7b6e2fa53d53885604051611c4991815260200190565b60405180910390a450505050565b600260015403611c795760405162461bcd60e51b815260040161093090615657565b600260015533611c88816114d6565b611ca45760405162461bcd60e51b8152600401610930906156be565b838214611cf35760405162461bcd60e51b815260206004820152601d60248201527f5374616b696e673a20494e434f4e53495354454e545f4c454e475448530000006044820152606401610930565b3360008181526101cf602090815260409182902082516080810184526101cd546001600160401b038082168352600160401b8204169382019390935261ffff600160801b8404811694820194909452600160901b909204909216606082015281546101c954611d619061327e565b611d6c83828361378a565b60005b88811015611dda57611dca84846060015161ffff16878d8d86818110611d9757611d976156f5565b9050602002016020810190611dac9190615141565b8c8c87818110611dbe57611dbe6156f5565b90506020020135614061565b611dd3816157a1565b9050611d6f565b5050600180555050505050505050565b6000339050806001600160a01b03167f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b031663ee97f7f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e7b91906157ba565b6001600160a01b03161480611f225750806001600160a01b03167f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b0316632d37ead56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1791906157ba565b6001600160a01b0316145b611f6e5760405162461bcd60e51b815260206004820152601f60248201527f534f4d413a204d4153544552206f7220535542204d4153544552206f6e6c79006044820152606401610930565b6113a66141fe565b600260015403611f985760405162461bcd60e51b815260040161093090615657565b600260015533611fa7816114d6565b611fc35760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf6020526040902080546101c95485611ff65760405162461bcd60e51b815260040161093090615754565b6101cb546001600160a01b03168260005b88811015612048576120348a8a83818110612024576120246156f5565b90506020020135848960006131d8565b90910190612041816157a1565b9050612007565b506120528361327e565b61205d85858361378a565b9182016101c955506101ca805491909103905550506001805550505050565b60026001540361209e5760405162461bcd60e51b815260040161093090615657565b6002600155336120ad816114d6565b6120c95760405162461bcd60e51b8152600401610930906156be565b6101cd543390600160401b90046001600160401b031683861461212e5760405162461bcd60e51b815260206004820152601e60248201527f5374616b696e673a20494e56414c49445f494e5055545f4c454e4754485300006044820152606401610930565b60005b86811015610c8657600086868381811061214d5761214d6156f5565b905060200281019061215f919061570b565b905090506000898984818110612177576121776156f5565b905060200201602081019061218c9190615141565b905060008083116121af5760405162461bcd60e51b815260040161093090615754565b60005b83811015612216576122028a8a878181106121cf576121cf6156f5565b90506020028101906121e1919061570b565b838181106121f1576121f16156f5565b90506020020135848989600161423b565b9091019061220f816157a1565b90506121b2565b5061222b6001600160a01b03831687836134c8565b856001600160a01b0316826001600160a01b03167f7e6632ca16a0ac6cf28448500b1a17d96c8b8163ad4c4a9b44ef5386cc02779e8360405161227091815260200190565b60405180910390a350505080612285906157a1565b9050612131565b60007f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b03166371907f176040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231091906157ba565b604051632474521560e21b8152600481018590526001600160a01b03848116602483015291909116906391d1485490604401602060405180830381865afa15801561235f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe891906158f7565b600054610100900460ff16158080156123a35750600054600160ff909116105b806123bd5750303b1580156123bd575060005460ff166001145b6124205760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610930565b6000805460ff191660011790558015612443576000805461ff0019166101001790555b30600080516020615ba3833981519152604051602001612464929190615919565b60408051601f198184030181529082905280516020918201206101c7556124af9130917f520767f2ee41b25d23e3166443601203d348640efb766878c95999973ecd38e19101615919565b60408051808303601f1901815291905280516020909101206101c8556001600160a01b0383163b6125225760405162461bcd60e51b815260206004820152601e60248201527f5374616b696e673a20494e56414c49445f5354414b494e475f544f4b454e00006044820152606401610930565b6101cb80546001600160a01b0319166001600160a01b03851690811790915561254a906131c8565b60005b82518110156125ca57600083828151811061256a5761256a6156f5565b60200260200101519050612587816001600160a01b03163b151590565b6125a35760405162461bcd60e51b81526004016109309061568e565b6125af6101d5826131b3565b506125b9816131c8565b506125c3816157a1565b905061254d565b506125d3614304565b6125db61433e565b6040805160008152602081019091526125f39061436b565b8015612639576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b60606002600154036126625760405162461bcd60e51b815260040161093090615657565b600260015533612671816114d6565b61268d5760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf602052604090208054856001600160401b038111156126b7576126b761540f565b6040519080825280602002602001820160405280156126e0578160200160208202803683370190505b5094506126ef6101c95461327e565b6126fa82828361378a565b60005b86811015612804576000888883818110612719576127196156f5565b905060200201602081019061272e9190615141565b6001600160a01b03811660009081526001860160205260409020549091506127558261352b565b6000811161279b5760405162461bcd60e51b81526020600482015260136024820152725374616b696e673a204e4f5f5245574152445360681b6044820152606401610930565b846001016000836001600160a01b03166001600160a01b03168152602001908152602001600020600090556127d386838360016143dd565b8884815181106127e5576127e56156f5565b6020026020010181815250505050806127fd906157a1565b90506126fd565b5050600180555091949350505050565b6002600154036128365760405162461bcd60e51b815260040161093090615657565b600260015533612845816114d6565b6128615760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf6020526040902080546101c9546101cb54612890906001600160a01b03168530896138ea565b9550600086116128b25760405162461bcd60e51b81526004016109309061583a565b6128bb8161327e565b6128ca838361115689826157f0565b6128d486826157f0565b6101c9556040518681526001600160a01b038516907f6e47dcdd359b6cd69456f0f97d394bd4540a2e7c4adc1b9da076859df53756c79060200160405180910390a250506001805550505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101829052906129596101d284614530565b5090506101d78160001c81548110612973576129736156f5565b60009182526020918290206040805160a081018252600293909302909101805465ffffffffffff8082168552600160301b82041694840194909452600160601b9093046001600160a01b0316908201526001909101546001600160801b038082166060840152600160801b9091041660808201529392505050565b6060816001600160401b03811115612a0857612a0861540f565b604051908082528060200260200182016040528015612a3b57816020015b6060815260200190600190039081612a265790505b50905060005b82811015612adb57612aab30858584818110612a5f57612a5f6156f5565b9050602002810190612a71919061593b565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061463192505050565b828281518110612abd57612abd6156f5565b60200260200101819052508080612ad3906157a1565b915050612a41565b5092915050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526101d78281548110612b2157612b216156f5565b60009182526020918290206040805160a081018252600293909302909101805465ffffffffffff8082168552600160301b82041694840194909452600160601b9093046001600160a01b0316908201526001909101546001600160801b038082166060840152600160801b90910416608082015292915050565b6000600260015403612bbf5760405162461bcd60e51b815260040161093090615657565b600260015533612bce816114d6565b612bea5760405162461bcd60e51b8152600401610930906156be565b3360008181526101cf6020526040902080546101c95486821015612c205760405162461bcd60e51b815260040161093090615803565b60008711612c405760405162461bcd60e51b81526004016109309061583a565b612c498161327e565b612c5883836111568a82615871565b8681036101c9556101ca8054880190556101cb54612c839085906001600160a01b03168960006143dd565b60018055979650505050505050565b6000612c9d8261352b565b6001600160a01b03831660009081526101cf6020526040902054610fe890612cc484610fef565b614725565b6001600160a01b03821660009081526101cf60205260408120612ceb8361352b565b6001600160a01b03831660009081526002820160205260409020548154612d1590612cc486610fef565b6001600160a01b0385166000908152600184016020526040902054612d3a91906157f0565b6110409190615871565b600260015403612d665760405162461bcd60e51b815260040161093090615657565b600260015533612d75816114d6565b612d915760405162461bcd60e51b8152600401610930906156be565b6101cb546101cd5433916001600160a01b0316906001600160401b03166000805b86811015612df457612de0888883818110612dcf57612dcf6156f5565b90506020020135858786600061423b565b90910190612ded816157a1565b9050612db2565b506101ca805482900390556101cb54612e17906001600160a01b031685836134c8565b836001600160a01b03167f81b0ac4b13aab1ab3b86d524dd62924e99a9d694e94e235f627bb41589717ff782604051612e5291815260200190565b60405180910390a25050600180555050505050565b60408051606081018252600080825260208201819052918101919091526101cb54610fe89083906001600160a01b0316856000613553565b33612eca7f520767f2ee41b25d23e3166443601203d348640efb766878c95999973ecd38e18261228c565b80612edd5750612edd6101c8548261228c565b612f295760405162461bcd60e51b815260206004820152601860248201527f5374616b696e673a205345495a455f524f4c455f4f4e4c5900000000000000006044820152606401610930565b60007f0000000000000000000000000f3dc00189dbcd1d0c574e48031270cae04c4adf6001600160a01b031663ff0d1a606040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fad91906157ba565b6101c9546001600160a01b03851660009081526101cf602052604090208054929350909180612fee5760405162461bcd60e51b815260040161093090615803565b612ff78361327e565b6130038282600061378a565b8083036101c95560006130176101d56131a9565b90506000816001600160401b038111156130335761303361540f565b60405190808252806020026020018201604052801561305c578160200160208202803683370190505b50905060005b828110156131095760006130786101d583614055565b6001600160a01b03811660009081526001880160209081526040808320546101d09092528220805493945090928392906130b39084906157f0565b92505081905550808484815181106130cd576130cd6156f5565b6020908102919091018101919091526001600160a01b03909216600090815260018801909252506040812055613102816157a1565b9050613062565b506101cb546001600160a01b03166131228188866134c8565b336001600160a01b0316876001600160a01b03168a6001600160a01b03167f70de378a6bcd3fde23ad1114eabf0a3d95dc5e57733b1d1c53d987128b4a76d38786604051613171929190615981565b60405180910390a4505050505050505050565b60006001600160e01b03198216630e46e23b60e31b14806108d057506108d08261473d565b60006108d0825490565b6000610fe8836001600160a01b038416614762565b6131d461015f826131b3565b5050565b60006131e685858585613553565b60409081015160008781526101d160205282812081905591516001600160801b03909116925086917fee243f878b7fc2f54e934ca33783d4395d42bc07612e2bd4b8e0e178639f7a2891a2949350505050565b61019454604080519182526020820183905233917f53f0b6b34c97fd1733ccb4b21aea7549d7ad3a83e6316864417b8cc1a9a9a807910160405180910390a261019455565b60006132936101d2546001600160401b031690565b9050600080806132a965ffffffffffff426159b0565b90505b83156134c1576132be6101d2856147b1565b6101d7805491955091935060009190849081106132dd576132dd6156f5565b60009182526020918290206040805160a081018252600293909302909101805465ffffffffffff808216808652600160301b8304821696860196909652600160601b9091046001600160a01b031692840192909252600101546001600160801b038082166060850152600160801b9091041660808301529092508316101561336557506134c1565b60006133718242614843565b90508160800151613381826139f1565b61338b91906159c4565b6101d78054869081106133a0576133a06156f5565b6000918252602090912060016002909202010180546001600160801b03928316600160801b029216919091179055861561345b5760006133e08883613768565b905080600003613422576040808401516001600160a01b031660009081526101d060205290812080548492906134179084906157f0565b909155506134559050565b6040808401516001600160a01b031660009081526101ce602052908120805483929061344f9084906157f0565b90915550505b5061348e565b6040808301516001600160a01b031660009081526101d060205290812080548392906134889084906157f0565b90915550505b816020015165ffffffffffff168365ffffffffffff16106134b7576134b56101d2876148dc565b505b84955050506132ac565b5050505050565b6040516001600160a01b03831660248201526044810182905261263990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526148e8565b6135376101d5826138c8565b6113a65760405162461bcd60e51b81526004016109309061568e565b604080516060810182526000808252602082018190529181019190915260008584868560405160200161358994939291906159e4565b60408051808303601f19018152828252805160209182012060008a81526101d183528390206060850184525460c081901b6001600160c01b0319908116808752600160401b83046001600160401b031694870194909452600160801b9091046001600160801b03169385019390935292945091925082161461364d5760405162461bcd60e51b815260206004820152601860248201527f5374616b696e673a20494e56414c49445f5245515545535400000000000000006044820152606401610930565b50949350505050565b600080600061366e6101d2546001600160401b031690565b90505b8015613761576136836101d2826147b1565b6101d78054929450909250600091849081106136a1576136a16156f5565b60009182526020918290206040805160a0810182526002909302909101805465ffffffffffff808216808652600160301b8304909116958501959095526001600160a01b03600160601b9091041691830191909152600101546001600160801b038082166060840152600160801b90910416608082015291504210156137275750613761565b846001600160a01b031681604001516001600160a01b03160361375b5761374e8142614843565b61375890856157f0565b93505b50613671565b5050919050565b6000826137806a084595161401484a00000084615a42565b610fe89190615a59565b818114613795578083555b60006137a26101d56131a9565b905060005b818110156134c15760006137bd6101d583614055565b6001600160a01b03811660009081526101ce602090815260408083205460028b0190925290912054919250906137f38783614725565b6137fd9190615871565b6001600160a01b0383166000908152600189016020526040812080549091906138279084906157f0565b9091555061383790508582614725565b6001600160a01b03909216600090815260028801602052604090209190915550613860816157a1565b90506137a7565b600061ffff6137808385615a42565b61387e6149ba565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6001600160a01b03811660009081526001830160205260408120541515610fe8565b6040516370a0823160e01b81526001600160a01b03838116600483015260009182918716906370a0823190602401602060405180830381865afa158015613935573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061395991906157d7565b90506139706001600160a01b038716868686614a05565b6040516370a0823160e01b81526001600160a01b0385811660048301528291908816906370a0823190602401602060405180830381865afa1580156139b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dd91906157d7565b6139e79190615871565b9695505050505050565b60006001600160801b03821115613a5a5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610930565b5090565b604080516080808201835285546001600160401b038082168452600160401b82048116602080860191909152600160801b83048216858701819052600160c01b909304821660608087019190915260019384018084166000908152948b01835293879020875195860188525460ff811615801587526101008204851693870193909352600160481b8104841697860197909752600160881b909604909116948301949094529192613b1e57613b1c86836001600160401b0316614a43565b505b6040805160c0810182526000918101828152606082018390526080820183905260a08201839052815260208101919091526040805160c0810182526000918101828152606082018390526080820183905260a082018390528152602081019190915284515b6001600160401b03811615613c1b576001600160401b0380821660009081526001808c016020908152604092839020835160c081018552815460ff81161515958201958652610100810487166060830152600160481b810487166080830152600160881b900490951660a0860152928452910154908201819052881015613c0b579250613c1b565b8051604001519092509050613b83565b604051806080016040528087600001516001600160401b03168560000151602001516001600160401b031614613c52578751613c54565b865b6001600160401b0316815260200187602001516001600160401b03168460000151602001516001600160401b031614613c91578760200151613c93565b865b6001600160401b03168152602001866001600160401b0316815260200187606001516001613cc19190615a6d565b6001600160401b03168152508960000160008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160106101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160186101000a8154816001600160401b0302191690836001600160401b0316021790555090505087896002016000876001600160401b031681526020019081526020016000208190555060405180604001604052806040518060800160405280600115158152602001886001600160401b031681526020018660000151602001516001600160401b031681526020018560000151602001516001600160401b0316815250815260200188815250896001016000876001600160401b0316815260200190815260200160002060008201518160000160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160401b0302191690836001600160401b0316021790555060408201518160000160096101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160000160116101000a8154816001600160401b0302191690836001600160401b0316021790555050506020820151816001015590505081600001516000015115613f9c5781516001600160401b038681166040928301528351602080820180518416600090815260018f01909252908490208251815492519584015160609094015168ffffffffffffffffff1990931690151568ffffffffffffffff001916176101009585169590950294909417600160481b600160c81b031916600160481b9284169290920267ffffffffffffffff60881b191691909117600160881b91909216021790555b8251511561404a5782516001600160401b038087166060928301528451602080820180518416600090815260018f019092526040918290208351815492519385015194909601518516600160881b0267ffffffffffffffff60881b19948616600160481b0294909416600160481b600160c81b0319939095166101000268ffffffffffffffff00199615159690961668ffffffffffffffffff19909216919091179490941716919091171790555b505050505050505050565b6000610fe88383614d6a565b61406a8261352b565b6001600160a01b0382166000908152600186016020526040902054816140a25760405162461bcd60e51b81526004016109309061583a565b600081116140e85760405162461bcd60e51b81526020600482015260136024820152725374616b696e673a204e4f5f5245574152445360681b6044820152606401610930565b808211156141385760405162461bcd60e51b815260206004820152601f60248201527f5374616b696e673a20494e53554646494349454e545f434c41494d41424c45006044820152606401610930565b60006141448387613867565b6001600160a01b03851660009081526101d060205260408120805492935083929091906141729084906157f0565b909155506141979050856141868386615871565b6001600160a01b03871691906134c8565b6001600160a01b03848116600081815260018a01602090815260409182902087870390558151878152908101859052928816927f4b7f0bdd3e71951c1c12390f2e6777e651743b9ba7acf3a13e9507c2f4dd5e31910160405180910390a350505050505050565b614206614d94565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586138ab3390565b60008061424a87878786613553565b90508381602001516001600160401b0316426142669190615871565b10156142b45760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e673a20494e53554646494349454e545f54494d450000000000006044820152606401610930565b60008781526101d160205260408082208290555188917f1cedb001a5114ea90393cd9f134224e9e1312545ee8a9b9533d780be6a9bf8b791a2604001516001600160801b03169695505050505050565b61430c614ddc565b614314614ddc565b61431c614e03565b614324614ddc565b61432c614ddc565b614334614ddc565b61433c614e84565b565b600054610100900460ff166143655760405162461bcd60e51b815260040161093090615a8d565b60018055565b600054610100900460ff166143925760405162461bcd60e51b815260040161093090615a8d565b60005b81518110156131d4576143cc8282815181106143b3576143b36156f5565b602002602001015161015f6131b390919063ffffffff16565b506143d6816157a1565b9050614395565b60008083116143fe5760405162461bcd60e51b81526004016109309061583a565b6101cc6000815461440e906157a1565b918290555060408051606081019091529091508061443283888887608086016159e4565b604051602081830303815290604052805190602001206001600160c01b031916815260200161446042614eb7565b6001600160401b03168152602001614477856139f1565b6001600160801b0390811690915260008381526101d160209081526040918290208451815492860151958401518516600160801b026001600160401b0396909616600160401b026001600160801b031990931660c09190911c1791909117909216929092179055516001600160a01b03808716919086169083907f1dc4096620302ec2c8b0fbec0db9683e195bddeddb5e9b48a150902b3676391c906145209088908890615ad8565b60405180910390a4949350505050565b6040805160808101825283546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b900490911660608201819052600091829184106145cb5760405162461bcd60e51b815260206004820152601960248201527f536f727465644c6973743a204f55545f4f465f424f554e4453000000000000006044820152606401610930565b80516001600160401b0316915060005b8481101561461657600092835260018601602052604090922054600160481b90046001600160401b03169161460f816157a1565b90506145db565b50506000818152600290940160205260409093205493915050565b60606001600160a01b0383163b6146995760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610930565b600080846001600160a01b0316846040516146b49190615b0a565b600060405180830381855af49150503d80600081146146ef576040519150601f19603f3d011682016040523d82523d6000602084013e6146f4565b606091505b509150915061471c8282604051806060016040528060278152602001615b7c60279139614f1f565b95945050505050565b60006a084595161401484a0000006137808385615a42565b60006001600160e01b03198216638757f10760e01b14806108d057506108d082614f58565b60008181526001830160205260408120546147a9575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556108d0565b5060006108d0565b6000818152600183016020526040812054819060ff1661480c5760405162461bcd60e51b815260206004820152601660248201527514dbdc9d1959131a5cdd0e881253959053125117d25160521b6044820152606401610930565b5050600090815260028201602090815260408083205460019094019091529020549091600160481b9091046001600160401b031690565b600080836020015165ffffffffffff168310156148b2578351602085015161486b9190615b26565b65ffffffffffff1684606001516001600160801b0316856000015165ffffffffffff16856148999190615871565b6148a39190615a42565b6148ad9190615a59565b6148c1565b83606001516001600160801b03165b905083608001516001600160801b0316816110409190615871565b6000610fe88383614a43565b600061493d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f7d9092919063ffffffff16565b805190915015612639578080602001905181019061495b91906158f7565b6126395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610930565b6149c2611a52565b61433c5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610930565b6040516001600160a01b0380851660248301528316604482015260648101829052614a3d9085906323b872dd60e01b906084016134f4565b50505050565b604080516080808201835284546001600160401b038082168452600160401b82048116602080860191909152600160801b8304821685870152600160c01b90920481166060808601918252600088815260018a018552878120885196870189525460ff8116151587526101008104851695870195909552600160481b8504841697860197909752600160881b909304821692840192909252905116614b235760405162461bcd60e51b8152602060048201526016602482015275536f727465644c6973743a204c4953545f454d50545960501b6044820152606401610930565b8051614b6a5760405162461bcd60e51b815260206004820152601660248201527514dbdc9d1959131a5cdd0e881253959053125117d25160521b6044820152606401610930565b600084815260028601602052604090205460608301519093506001600160401b0316600103614b9c5760008555614d31565b81600001516001600160401b0316846001600160401b031603614bca5760408101516001600160401b031682525b81602001516001600160401b0316846001600160401b031603614bfb5760608101516001600160401b031660208301525b60608101516001600160401b031615614c5b5760408181015160608301516001600160401b0390811660009081526001890160205292909220805470ffffffffffffffff0000000000000000001916600160481b93909216929092021790555b60408101516001600160401b031615614cb45760608101516040808301516001600160401b0390811660009081526001890160205291909120805491909216600160881b0267ffffffffffffffff60881b199091161790555b816060018051614cc390615b45565b6001600160401b03908116909152825186546020850151604086015160608701519385166001600160801b031990931692909217600160401b91851691909102176001600160801b0316600160801b918416919091026001600160c01b031617600160c01b91909216021785555b5050600091825260018084016020908152604080852080546001600160c81b031916815590920184905560029094019093529181205590565b6000826000018281548110614d8157614d816156f5565b9060005260206000200154905092915050565b614d9c611a52565b1561433c5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610930565b600054610100900460ff1661433c5760405162461bcd60e51b815260040161093090615a8d565b600054610100900460ff16614e2a5760405162461bcd60e51b815260040161093090615a8d565b307ff28c151dcc22c5bfb460fbc387993d6c0d1c1245ca20aca5f3cbd33f33041eb1604051602001614e5d929190615919565b60408051601f1981840301815291905280516020909101206101935561433c600019613239565b600054610100900460ff16614eab5760405162461bcd60e51b815260040161093090615a8d565b6065805460ff19169055565b60006001600160401b03821115613a5a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610930565b60608315614f2e575081610fe8565b825115614f3e5782518084602001fd5b8160405162461bcd60e51b81526004016109309190615b68565b60006001600160e01b0319821663b55bd4f760e01b14806108d057506108d082614f8c565b6060610fe58484600085614fc1565b60006001600160e01b0319821663d86f1ca160e01b14806108d057506301ffc9a760e01b6001600160e01b03198316146108d0565b6060824710156150225760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610930565b6001600160a01b0385163b6150795760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610930565b600080866001600160a01b031685876040516150959190615b0a565b60006040518083038185875af1925050503d80600081146150d2576040519150601f19603f3d011682016040523d82523d6000602084013e6150d7565b606091505b50915091506150e7828286614f1f565b979650505050505050565b60006020828403121561510457600080fd5b81356001600160e01b031981168114610fe857600080fd5b6001600160a01b03811681146113a657600080fd5b803561513c8161511c565b919050565b60006020828403121561515357600080fd5b8135610fe88161511c565b60008083601f84011261517057600080fd5b5081356001600160401b0381111561518757600080fd5b6020830191508360208260051b85010111156151a257600080fd5b9250929050565b600080600080604085870312156151bf57600080fd5b84356001600160401b03808211156151d657600080fd5b6151e28883890161515e565b909650945060208701359150808211156151fb57600080fd5b506152088782880161515e565b95989497509550505050565b60006020828403121561522657600080fd5b5035919050565b6000806040838503121561524057600080fd5b823561524b8161511c565b9150602083013561525b8161511c565b809150509250929050565b60008060006060848603121561527b57600080fd5b83356152868161511c565b925060208401356152968161511c565b929592945050506040919091013590565b80356001600160401b038116811461513c57600080fd5b803561ffff8116811461513c57600080fd5b600080600080608085870312156152e657600080fd5b6152ef856152a7565b93506152fd602086016152a7565b925061530b604086016152be565b9150615319606086016152be565b905092959194509250565b6000806000806080858703121561533a57600080fd5b843593506020850135925060408501356153538161511c565b9396929550929360600135925050565b608081016108d082846001600160401b0380825116835280602083015116602084015250604081015161ffff808216604085015280606084015116606085015250505050565b600080602083850312156153bc57600080fd5b82356001600160401b038111156153d257600080fd5b6153de8582860161515e565b90969095509350505050565b600080604083850312156153fd57600080fd5b82359150602083013561525b8161511c565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561543857600080fd5b82356154438161511c565b91506020838101356001600160401b038082111561546057600080fd5b818601915086601f83011261547457600080fd5b8135818111156154865761548661540f565b8060051b604051601f19603f830116810181811085821117156154ab576154ab61540f565b6040529182528482019250838101850191898311156154c957600080fd5b938501935b828510156154ee576154df85615131565b845293850193928501926154ce565b8096505050505050509250929050565b600081518084526020808501945080840160005b8381101561552e57815187529582019590820190600101615512565b509495945050505050565b602081526000610fe860208301846154fe565b60005b8381101561556757818101518382015260200161554f565b50506000910152565b6000815180845261558881602086016020860161554c565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156155f157603f198886030184526155df858351615570565b945092850192908501906001016155c3565b5092979650505050505050565b6000806040838503121561561157600080fd5b823561561c8161511c565b946020939093013593505050565b6020808252601390820152725374616b696e673a2041444d494e5f4f4e4c5960681b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526016908201527514dd185ada5b99ce881253959053125117d054d4d15560521b604082015260600190565b6020808252601b908201527f72657175697265642070726976696c65676573206e6f74206d65740000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261572257600080fd5b8301803591506001600160401b0382111561573c57600080fd5b6020019150600581901b36038213156151a257600080fd5b6020808252601b908201527f5374616b696e673a20494e56414c49445f4944535f4c454e4754480000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000600182016157b3576157b361578b565b5060010190565b6000602082840312156157cc57600080fd5b8151610fe88161511c565b6000602082840312156157e957600080fd5b5051919050565b808201808211156108d0576108d061578b565b6020808252601b908201527f5374616b696e673a20494e53554646494349454e545f5354414b450000000000604082015260600190565b60208082526017908201527f5374616b696e673a20494e56414c49445f414d4f554e54000000000000000000604082015260600190565b818103818111156108d0576108d061578b565b82546001600160401b038082168352604082811c8216602080860191909152608084811c61ffff9081168488015260909590951c851660608088019190915287518516918701919091529086015190921660a0850152840151821660c08401528301511660e08201526101008101610fe8565b60006020828403121561590957600080fd5b81518015158114610fe857600080fd5b60609290921b6bffffffffffffffffffffffff19168252601482015260340190565b6000808335601e1984360301811261595257600080fd5b8301803591506001600160401b0382111561596c57600080fd5b6020019150368190038213156151a257600080fd5b828152604060208201526000610fe560408301846154fe565b634e487b7160e01b600052601260045260246000fd5b6000826159bf576159bf61599a565b500690565b6001600160801b03818116838216019080821115612adb57612adb61578b565b84815260006bffffffffffffffffffffffff19808660601b166020840152808560601b1660348401525060028310615a2c57634e487b7160e01b600052602160045260246000fd5b5060f89190911b60488201526049019392505050565b80820281158282048414176108d0576108d061578b565b600082615a6857615a6861599a565b500490565b6001600160401b03818116838216019080821115612adb57612adb61578b565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b8281526040810160028310615afd57634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b60008251615b1c81846020870161554c565b9190910192915050565b65ffffffffffff828116828216039080821115612adb57612adb61578b565b60006001600160401b03821680615b5e57615b5e61578b565b6000190192915050565b602081526000610fe8602083018461557056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65647512bb95d4b127c0d01d6ada748d8b883dc98517e724044a57377f61d3efce7da164736f6c6343000812000a

Verified Source Code Partial Match

Compiler: v0.8.18+commit.87f61d96 EVM: london Optimization: Yes (200 runs)
SomaStaking.sol 798 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../utils/math/SortedList.sol";
import "../utils/math/Percent.sol";
import "../utils/DynamicArray.sol";

import "../SomaGuard/utils/GuardableUpgradeable.sol";
import "../SecurityTokens/ERC20/utils/SafeERC20Balance.sol";
import "../Lockdrop/extensions/TokenRecoveryUpgradeable.sol";

import "./SomaStakingLibrary.sol";
import "./ISomaStaking.sol";

/**
 * @notice Implementation of the {ISomaStaking} interface.
 */
contract SomaStaking is ISomaStaking, ReentrancyGuardUpgradeable, TokenRecoveryUpgradeable, GuardableUpgradeable {
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using SafeERC20Balance for IERC20;
    using SafeERC20 for IERC20;
    using SortedList for SortedList.AscendingList;
    using Percent for uint256;
    using SafeCastUpgradeable for uint256;

    /**
     * @inheritdoc ISomaStaking
     */
    bytes32 public constant override GLOBAL_ADMIN_ROLE = keccak256("Staking.GLOBAL_ADMIN_ROLE");

    /**
     * @inheritdoc ISomaStaking
     */
    bytes32 public constant override GLOBAL_SEIZE_ROLE = keccak256("Staking.GLOBAL_SEIZE_ROLE");

    /**
     * @inheritdoc ISomaStaking
     */
    bytes32 public override LOCAL_ADMIN_ROLE;

    /**
     * @inheritdoc ISomaStaking
     */
    bytes32 public override LOCAL_SEIZE_ROLE;

    /* Amount of staked tokens globally */
    uint256 private _totalStaked;
    uint256 private _totalPendingUnstake;
    address private _stakingToken;
    uint256 private _currentRequestId;
    StakingConfig private _config;

    mapping(address => uint256) private _tps;
    mapping(address => UserInfo) private _users;
    mapping(address => uint256) private _adminClaimable;
    mapping(uint256 => Request) private _requests;

    SortedList.AscendingList private _pendingStrategies;
    EnumerableSetUpgradeable.AddressSet private _rewardTokens;
    Strategy[] private _strategies;

    /**
     * @notice Modifier to restrict function calls to accounts that have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    modifier onlyAdmin() {
        address _sender = _msgSender();
        require(hasRole(GLOBAL_ADMIN_ROLE, _sender) || hasRole(LOCAL_ADMIN_ROLE, _sender), "Staking: ADMIN_ONLY"); // TODO errors should be Staking or SomaStaking
        _;
    }

    /**
     * @notice Modifier to restrict function calls to accounts that have the GLOBAL_SEIZE_ROLE or LOCAL_SEIZE_ROLE.
     */
    modifier onlySeizeRole() {
        address _sender = _msgSender();
        require(hasRole(GLOBAL_SEIZE_ROLE, _sender) || hasRole(LOCAL_SEIZE_ROLE, _sender), "Staking: SEIZE_ROLE_ONLY");
        _;
    }

    /**
     * @notice Checks if SomaStaking inherits a given contract interface.
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(GuardableUpgradeable, TokenRecoveryUpgradeable)
        returns (bool)
    {
        return interfaceId == type(ISomaStaking).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function config() external view override returns (StakingConfig memory) {
        return _config;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function totalStaked() external view override returns (uint256) {
        return _totalStaked;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function totalPendingUnstake() external view override returns (uint256) {
        return _totalPendingUnstake;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function strategy(uint256 id) external view override returns (Strategy memory) {
        return _strategies[id];
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function totalStrategies() external view override returns (uint256) {
        return _strategies.length;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function stakingToken() external view override returns (address) {
        return _stakingToken;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function rewardToken(uint256 index) external view override returns (address) {
        return _rewardTokens.at(index);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function totalRewardTokens() external view override returns (uint256) {
        return _rewardTokens.length();
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function pendingStrategy(uint256 index) external view override returns (Strategy memory) {
        (bytes32 id,) = _pendingStrategies.at(index);
        return _strategies[uint256(id)];
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function totalPendingStrategies() external view override returns (uint256) {
        return _pendingStrategies.length();
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function tps(address _asset) external view override returns (uint256) {
        return _tps[_asset];
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function adminClaimable(address _asset) external view override returns (uint256) {
        bool _isRewardAsset = _rewardTokens.contains(_asset);

        require(_isRewardAsset || _asset == _stakingToken, "Staking: INVALID_ASSET");

        // add the extra rewards if there are currently no stakers
        return _adminClaimable[_asset] + (_totalStaked == 0 && _isRewardAsset ? _rewardsUnlocked(_asset) : 0);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function debt(address _account, address _asset) external view override returns (uint256) {
        _checkRewardToken(_asset);
        return SomaStakingLibrary.stakeToRewards(_users[_account].stake, currentTPS(_asset));
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function claimable(address _account, address _asset) external view override returns (uint256) {
        UserInfo storage _user = _users[_account];
        _checkRewardToken(_asset);
        return _user.claimable[_asset] + SomaStakingLibrary.stakeToRewards(_user.stake, currentTPS(_asset))
            - _user.debt[_asset];
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function claimRequest(address _account, address _asset, uint256 _id)
        external
        view
        override
        returns (Request memory)
    {
        _checkRewardToken(_asset);
        return _getRequest(_id, _asset, _account, RequestType.CLAIM);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function stakeOf(address _account) external view override returns (uint256) {
        return _users[_account].stake;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function unstakeRequest(address _account, uint256 _id) external view override returns (Request memory) {
        return _getRequest(_id, _stakingToken, _account, RequestType.UNSTAKE);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function initialize(address stakingToken_, address[] memory rewardTokens_) external override initializer {
        LOCAL_ADMIN_ROLE = keccak256(abi.encodePacked(address(this), GLOBAL_ADMIN_ROLE));
        LOCAL_SEIZE_ROLE = keccak256(abi.encodePacked(address(this), GLOBAL_SEIZE_ROLE));

        require(Address.isContract(stakingToken_), "Staking: INVALID_STAKING_TOKEN");

        _stakingToken = stakingToken_;

        _disableTokenRecovery(_stakingToken);

        for (uint256 i = 0; i < rewardTokens_.length; ++i) {
            address _token = rewardTokens_[i];
            require(Address.isContract(_token), "Staking: INVALID_ASSET");
            _rewardTokens.add(_token);
            _disableTokenRecovery(_token);
        }

        __Guardable__init();
        __ReentrancyGuard_init_unchained();
        __TokenRecovery__init_unchained(new address[](0));
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function currentTPS(address token) public view override returns (uint256 tps_) {
        uint256 totalStaked_ = _totalStaked;
        uint256 extraTPS =
            (totalStaked_ > 0) ? SomaStakingLibrary.rewardsToTPS(_totalStaked, _rewardsUnlocked(token)) : 0;
        return _tps[token] + extraTPS;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function createUnstakeRequest(uint256 _amount)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
        returns (uint256 _id)
    {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        uint256 _userStake = _user.stake;
        uint256 totalStake_ = _totalStaked;

        require(_userStake >= _amount, "Staking: INSUFFICIENT_STAKE");
        require(_amount > 0, "Staking: INVALID_AMOUNT");

        _update(totalStake_);
        // remove this amount from the users stake amount, so that they no longer earn rewards on
        _syncUser(_user, _userStake, _userStake - _amount);

        // adjust the total stake balances
        unchecked {
            _totalStaked = totalStake_ - _amount;
            _totalPendingUnstake += _amount;
        }

        return _createRequest(_sender, _stakingToken, _amount, RequestType.UNSTAKE);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function cancelUnstakeRequests(uint256[] calldata _ids)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
    {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        uint256 _userStake = _user.stake;
        uint256 totalStake_ = _totalStaked;

        require(_ids.length > 0, "Staking: INVALID_IDS_LENGTH");

        address stakingToken_ = _stakingToken;
        uint256 _totalAmount = _userStake;
        for (uint256 i = 0; i < _ids.length; ++i) {
            unchecked {
                _totalAmount += _cancelRequest(_ids[i], stakingToken_, _sender, RequestType.UNSTAKE);
            }
        }

        _update(totalStake_);
        _syncUser(_user, _userStake, _totalAmount);

        unchecked {
            _totalStaked = totalStake_ + _totalAmount;
            _totalPendingUnstake -= _totalAmount;
        }
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function createClaimRequests(address[] calldata _assets)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
        returns (uint256[] memory _ids)
    {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        uint256 _userStake = _user.stake;

        _ids = new uint256[](_assets.length);

        _update(_totalStaked);
        _syncUser(_user, _userStake, _userStake);

        for (uint256 i = 0; i < _assets.length; ++i) {
            address _asset = _assets[i];
            uint256 _rewards = _user.claimable[_asset];

            _checkRewardToken(_asset);
            require(_rewards > 0, "Staking: NO_REWARDS");

            delete _user.claimable[_asset];

            _ids[i] = _createRequest(_sender, _asset, _rewards, RequestType.CLAIM);
        }
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function cancelClaimRequests(address[] calldata _assets, uint256[][] calldata _ids)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
    {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];

        require(_assets.length > 0, "Staking: INVALID_ASSETS_LENGTH");
        require(_ids.length == _assets.length, "Staking: INVALID_INPUT_LENGTHS");

        for (uint256 i = 0; i < _assets.length; ++i) {
            uint256 _idsLength = _ids[i].length;
            address _asset = _assets[i];
            uint256 _totalAmount;

            require(_idsLength > 0, "Staking: INVALID_IDS_LENGTH");
            for (uint256 j = 0; j < _idsLength; ++j) {
                unchecked {
                    _totalAmount += _cancelRequest(_ids[i][j], _asset, _sender, RequestType.CLAIM);
                }
            }

            unchecked {
                _user.claimable[_asset] += _totalAmount;
            }
        }
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function claim(address[] calldata _assets, uint256[][] calldata _ids)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
    {
        address _sender = _msgSender();
        uint256 _claimDuration = _config.claimDuration;

        require(_assets.length == _ids.length, "Staking: INVALID_INPUT_LENGTHS");

        for (uint256 i = 0; i < _assets.length; ++i) {
            uint256 _idsLength = _ids[i].length;
            address _asset = _assets[i];
            uint256 _totalAmount;

            require(_idsLength > 0, "Staking: INVALID_IDS_LENGTH");
            for (uint256 j = 0; j < _idsLength; ++j) {
                unchecked {
                    _totalAmount += _useRequest(_ids[i][j], _asset, _sender, _claimDuration, RequestType.CLAIM);
                }
            }

            IERC20(_asset).safeTransfer(_sender, _totalAmount);

            emit Claimed(_asset, _totalAmount, _sender);
        }
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function claimImmediate(address[] calldata _assets, uint256[] calldata _amounts)
        external
        override
        nonReentrant
        onlyApprovedPrivileges(_msgSender())
    {
        require(_assets.length == _amounts.length, "Staking: INCONSISTENT_LENGTHS");

        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        StakingConfig memory config_ = _config;

        uint256 _userStake = _user.stake;
        _update(_totalStaked);
        _syncUser(_user, _userStake, _userStake);

        for (uint256 i = 0; i < _assets.length; ++i) {
            _claimImmediate(_user, config_.earlyClaimFee, _sender, _assets[i], _amounts[i]);
        }
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function stake(uint256 _amount) external override nonReentrant onlyApprovedPrivileges(_msgSender()) {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        uint256 _userStake = _user.stake;
        uint256 totalStake_ = _totalStaked;

        // slither-disable-next-line reentrancy-no-eth
        _amount = SafeERC20Balance.safeTransferFrom(IERC20(_stakingToken), _sender, address(this), _amount);

        require(_amount > 0, "Staking: INVALID_AMOUNT");

        _update(totalStake_);
        _syncUser(_user, _userStake, _userStake + _amount);
        _totalStaked = totalStake_ + _amount;

        emit Staked(_amount, _sender);
    }

    // TODO should this have an input amount?
    /**
     * @inheritdoc ISomaStaking
     */
    function adminClaim(address _asset, address _to) external override onlyAdmin nonReentrant {
        _update(_totalStaked);
        uint256 _claimable = _adminClaimable[_asset];
        require(_claimable > 0, "Staking: INSUFFICIENT_CLAIMABLE");
        delete _adminClaimable[_asset];
        IERC20(_asset).safeTransfer(_to, _claimable);
        emit AdminClaimed(_asset, _claimable, _to, _msgSender());
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function unstake(uint256[] calldata _ids) external override nonReentrant onlyApprovedPrivileges(_msgSender()) {
        address _sender = _msgSender();
        address stakingToken_ = _stakingToken;
        uint256 _unstakeDuration = _config.unstakeDuration;

        uint256 _totalAmount;
        for (uint256 i = 0; i < _ids.length; ++i) {
            unchecked {
                _totalAmount += _useRequest(_ids[i], stakingToken_, _sender, _unstakeDuration, RequestType.UNSTAKE);
            }
        }

        unchecked {
            _totalPendingUnstake -= _totalAmount;
        }

        IERC20(_stakingToken).safeTransfer(_sender, _totalAmount);

        emit Unstaked(_totalAmount, _sender);
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function unstakeImmediate(uint256 _amount) external override nonReentrant onlyApprovedPrivileges(_msgSender()) {
        address _sender = _msgSender();
        UserInfo storage _user = _users[_sender];
        uint256 _userStake = _user.stake;
        uint256 totalStake_ = _totalStaked;

        StakingConfig memory config_ = _config;

        require(_userStake >= _amount, "Staking: INSUFFICIENT_STAKE");
        require(_amount > 0, "Staking: INVALID_AMOUNT");

        _update(totalStake_);
        _syncUser(_user, _userStake, _userStake - _amount);

        unchecked {
            _totalStaked = totalStake_ - _amount;
        }
        uint256 _adminFee = _amount.applyPercent(config_.earlyUnstakeFee);

        address _asset = _stakingToken;
        _adminClaimable[_asset] += _adminFee;
        IERC20(_asset).safeTransfer(_sender, _amount - _adminFee);

        emit UnstakedImmediate(_amount, _adminFee, _msgSender());
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function addRewardToken(address _asset) external override onlyAdmin nonReentrant {
        require(_rewardTokens.add(_asset), "Staking: REWARD_TOKEN_EXISTS");
        require(_rewardTokens.length() <= 64, "Staking: LIMIT");
        require(Address.isContract(_asset), "Staking: INVALID_ASSET");

        _disableTokenRecovery(_asset);
        emit RewardTokenAdded(_asset, _msgSender());
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function createStrategy(uint256 _startDate, uint256 _endDate, address _rewardToken, uint256 _rewardAmount)
        external
        override
        onlyAdmin
        nonReentrant
    {
        require(_rewardAmount <= type(uint128).max, "Staking: MAX_REWARD_AMOUNT");
        require(_startDate > block.timestamp, "Staking: INVALID_START_DATE");
        require(_startDate < _endDate, "Staking: INVALID_DATE_ORDER");
        require(_endDate <= type(uint48).max, "Staking: INVALID_END_DATE");
        _checkRewardToken(_rewardToken);

        // slither-disable-next-line reentrancy-no-eth
        _rewardAmount =
            SafeERC20Balance.safeTransferFrom(IERC20(_rewardToken), _msgSender(), address(this), _rewardAmount);

        require(_rewardAmount > 0, "Staking: INVALID_AMOUNT");

        uint256 strategyId = _strategies.length;
        require(strategyId < 64, "Staking: LIMIT");
        Strategy memory _strategy = Strategy({
            startDate: uint48(_startDate),
            endDate: uint48(_endDate),
            rewardsLocked: _rewardAmount.toUint128(),
            rewardToken: _rewardToken,
            rewardsUnlocked: 0
        });

        _strategies.push(_strategy);

        _pendingStrategies.add(bytes32(strategyId), _startDate);

        emit StrategyCreated(_rewardToken, _strategy.rewardsLocked, _startDate, _endDate, _msgSender());
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function updateConfig(
        uint64 _unstakeDuration,
        uint64 _claimDuration,
        uint16 _earlyUnstakeFee,
        uint16 _earlyClaimFee
    ) external override onlyAdmin {
        StakingConfig memory config_ = StakingConfig({
            unstakeDuration: _unstakeDuration,
            claimDuration: _claimDuration,
            earlyUnstakeFee: _earlyUnstakeFee,
            earlyClaimFee: _earlyClaimFee
        });
        emit StakingConfigUpdated(_config, config_, _msgSender());
        _config = config_;
    }

    /**
     * @inheritdoc ISomaStaking
     */
    function seize(address from) external override onlySeizeRole {
        address _seizeTo = SOMA.seizeTo();
        uint256 totalStake_ = _totalStaked;

        UserInfo storage _user = _users[from];
        uint256 _userStake = _user.stake;

        require(_userStake > 0, "Staking: INSUFFICIENT_STAKE");

        _update(totalStake_);
        _syncUser(_user, _userStake, 0);

        unchecked {
            _totalStaked = totalStake_ - _userStake;
        }

        uint256 _totalTokens = _rewardTokens.length();
        uint256[] memory _seizedRewards = new uint256[](_totalTokens);

        for (uint256 i = 0; i < _totalTokens; ++i) {
            address _rewardToken = _rewardTokens.at(i);
            uint256 _claimable = _user.claimable[_rewardToken];

            _adminClaimable[_rewardToken] += _claimable;
            _seizedRewards[i] = _claimable;

            delete _user.claimable[_rewardToken];
        }

        address _asset = _stakingToken;
        IERC20(_asset).safeTransfer(_seizeTo, _userStake);

        emit Seized(from, _seizeTo, _userStake, _seizedRewards, _msgSender());
    }

    function _checkRewardToken(address _asset) private view {
        require(_rewardTokens.contains(_asset), "Staking: INVALID_ASSET");
    }

    function _update(uint256 totalStake_) private {
        uint256 curId = _pendingStrategies.head();
        uint256 nextId;
        bytes32 key;

        // slither-disable-next-line weak-prng
        uint48 curTimestamp = uint48(block.timestamp % type(uint48).max);

        while (curId != 0) {
            (key, nextId) = _pendingStrategies.get(curId);
            Strategy memory _strategy = _strategies[uint256(key)];
            if (curTimestamp < _strategy.startDate) {
                break;
            }

            uint256 rewardsUnlocked = SomaStakingLibrary.rewardsUnlocked(_strategy, block.timestamp);

            // increment how many rewards have been released
            _strategies[uint256(key)].rewardsUnlocked = rewardsUnlocked.toUint128() + _strategy.rewardsUnlocked;
            // if there is nobody staking, lets go ahead and return the rewards earned to the admin
            if (totalStake_ > 0) {
                uint256 tps_ = SomaStakingLibrary.rewardsToTPS(totalStake_, rewardsUnlocked);
                if (tps_ == 0) {
                    _adminClaimable[_strategy.rewardToken] += rewardsUnlocked;
                } else {
                    _tps[_strategy.rewardToken] += tps_;
                }
            } else {
                _adminClaimable[_strategy.rewardToken] += rewardsUnlocked;
            }

            // if this strategy has been completed then let us remove it from the pending list
            if (curTimestamp >= _strategy.endDate) {
                _pendingStrategies.remove(curId);
            }

            // progress to the next item in the list
            curId = nextId;
        }
    }

    function _syncUser(UserInfo storage _user, uint256 _userStake, uint256 _newUserStake) private {
        if (_newUserStake != _userStake) _user.stake = _newUserStake;

        // sync the claimable and debt values
        uint256 _totalTokens = _rewardTokens.length();
        for (uint256 i = 0; i < _totalTokens; ++i) {
            address _rewardToken = _rewardTokens.at(i);
            uint256 tps_ = _tps[_rewardToken];

            _user.claimable[_rewardToken] +=
                SomaStakingLibrary.stakeToRewards(_userStake, tps_) - _user.debt[_rewardToken];
            _user.debt[_rewardToken] = SomaStakingLibrary.stakeToRewards(_newUserStake, tps_);
        }
    }

    function _claimImmediate(
        UserInfo storage _user,
        uint256 _earlyClaimFee,
        address _sender,
        address _asset,
        uint256 _amount
    ) private {
        _checkRewardToken(_asset);

        uint256 _claimable = _user.claimable[_asset];

        require(_amount > 0, "Staking: INVALID_AMOUNT");
        require(_claimable > 0, "Staking: NO_REWARDS");
        require(_amount <= _claimable, "Staking: INSUFFICIENT_CLAIMABLE");

        uint256 _adminFee = _amount.applyPercent(_earlyClaimFee);

        _adminClaimable[_asset] += _adminFee;
        IERC20(_asset).safeTransfer(_sender, _amount - _adminFee);

        unchecked {
            _user.claimable[_asset] = _claimable - _amount;
        }

        emit ClaimedImmediate(_asset, _amount, _adminFee, _sender);
    }

    function _rewardsUnlocked(address token) private view returns (uint256 _totalUnlocked) {
        bytes32 key;
        uint256 curId = _pendingStrategies.head();

        while (curId != 0) {
            (key, curId) = _pendingStrategies.get(curId);
            Strategy memory _strategy = _strategies[uint256(key)];
            if (block.timestamp < _strategy.startDate) break;
            if (_strategy.rewardToken == token) {
                _totalUnlocked += SomaStakingLibrary.rewardsUnlocked(_strategy, block.timestamp);
            }
        }
    }

    function _createRequest(address _sender, address _asset, uint256 _amount, RequestType _type)
        private
        returns (uint256 _id)
    {
        require(_amount > 0, "Staking: INVALID_AMOUNT");

        _id = ++_currentRequestId;
        _requests[_id] = Request({
            hash: bytes8(keccak256(abi.encodePacked(_id, _sender, _asset, _type))),
            timestamp: block.timestamp.toUint64(),
            amount: _amount.toUint128()
        });

        emit RequestCreated(_id, _asset, _amount, _sender, _type);
    }

    function _getRequest(uint256 _id, address _asset, address _sender, RequestType _type)
        private
        view
        returns (Request memory _request)
    {
        bytes8 _hash = bytes8(keccak256(abi.encodePacked(_id, _sender, _asset, _type)));
        _request = _requests[_id];
        require(_request.hash == _hash, "Staking: INVALID_REQUEST");
    }

    function _useRequest(uint256 _id, address _asset, address _sender, uint256 _requiredDuration, RequestType _type)
        private
        returns (uint256 _amount)
    {
        Request memory _request = _getRequest(_id, _asset, _sender, _type);

        require(block.timestamp - _request.timestamp >= _requiredDuration, "Staking: INSUFFICIENT_TIME");

        delete _requests[_id];
        emit RequestFulfilled(_id);

        return _request.amount;
    }

    function _cancelRequest(uint256 _id, address _asset, address _sender, RequestType _type)
        private
        returns (uint256 _amount)
    {
        _amount = _getRequest(_id, _asset, _sender, _type).amount;
        delete _requests[_id];
        emit RequestCancelled(_id);
    }
}
ReentrancyGuardUpgradeable.sol 75 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

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

    uint256 private _status;

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

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
SafeCastUpgradeable.sol 1135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248) {
        require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
        return int248(value);
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240) {
        require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
        return int240(value);
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232) {
        require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
        return int232(value);
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224) {
        require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
        return int224(value);
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216) {
        require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
        return int216(value);
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208) {
        require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
        return int208(value);
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200) {
        require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
        return int200(value);
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192) {
        require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
        return int192(value);
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184) {
        require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
        return int184(value);
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176) {
        require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
        return int176(value);
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168) {
        require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
        return int168(value);
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160) {
        require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
        return int160(value);
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152) {
        require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
        return int152(value);
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144) {
        require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
        return int144(value);
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136) {
        require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
        return int136(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120) {
        require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
        return int120(value);
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112) {
        require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
        return int112(value);
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104) {
        require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
        return int104(value);
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96) {
        require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
        return int96(value);
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88) {
        require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
        return int88(value);
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80) {
        require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
        return int80(value);
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72) {
        require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
        return int72(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56) {
        require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
        return int56(value);
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48) {
        require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
        return int48(value);
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40) {
        require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
        return int40(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24) {
        require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
        return int24(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}
EnumerableSetUpgradeable.sol 367 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
SafeERC20.sol 116 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
SortedList.sol 155 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

library SortedList {
    struct ListItemDetails {
        bool exists;
        uint64 id;
        uint64 nextId;
        uint64 prevId;
    }

    struct ListItem {
        ListItemDetails details;
        uint256 value;
    }

    struct ListDetails {
        uint64 head;
        uint64 tail;
        uint64 current;
        uint64 length;
    }

    struct AscendingList {
        ListDetails __details;
        mapping(uint256 => ListItem) __items;
        mapping(uint256 => bytes32) __keys;
    }

    function head(AscendingList storage list) internal view returns (uint256 id) {
        return list.__details.head;
    }

    function tail(AscendingList storage list) internal view returns (uint256 id) {
        return list.__details.tail;
    }

    function get(AscendingList storage list, uint256 id) internal view returns (bytes32 key, uint256 nextId) {
        require(list.__items[id].details.exists, "SortedList: INVALID_ID");
        key = list.__keys[id];
        nextId = list.__items[id].details.nextId;
    }

    function exists(AscendingList storage list, uint256 id) internal view returns (bool) {
        return list.__items[id].details.exists;
    }

    function length(AscendingList storage list) internal view returns (uint256) {
        return list.__details.length;
    }

    function at(AscendingList storage list, uint256 index) internal view returns (bytes32 key, uint256 curId) {
        ListDetails memory details = list.__details;
        require(index < details.length, "SortedList: OUT_OF_BOUNDS");

        curId = details.head;
        for (uint256 i = 0; i < index; ++i) {
            curId = list.__items[curId].details.nextId;
        }
        key = list.__keys[curId];
    }

    function add(AscendingList storage list, bytes32 key, uint256 value) internal {
        ListDetails memory details = list.__details;

        // when it hits the end loop back around to the beginning
        uint64 id;
        unchecked {
            id = details.current + 1;
        }

        ListItemDetails memory itemDetails = list.__items[id].details;

        // we want to override the previous values if they exist
        if (itemDetails.exists) _remove(list, id);

        ListItem memory nextItem;
        ListItem memory prevItem;
        uint64 curId = details.head;
        while (curId != 0) {
            ListItem memory curListItem = list.__items[curId];
            // sort until we find first value that is larger.
            // then this item should go on the right. so it is ascending
            if (curListItem.value > value) {
                nextItem = curListItem;
                break;
            }

            prevItem = curListItem;
            curId = curListItem.details.nextId;
        }

        list.__details = ListDetails({
            length: details.length + 1,
            head: nextItem.details.id == details.head ? id : details.head,
            tail: prevItem.details.id == details.tail ? id : details.tail,
            current: id
        });
        list.__keys[id] = key;
        list.__items[id] = ListItem({
            value: value,
            details: ListItemDetails({exists: true, id: id, prevId: prevItem.details.id, nextId: nextItem.details.id})
        });

        if (prevItem.details.exists) {
            prevItem.details.nextId = id;
            list.__items[prevItem.details.id].details = prevItem.details;
        }
        if (nextItem.details.exists) {
            nextItem.details.prevId = id;
            list.__items[nextItem.details.id].details = nextItem.details;
        }
    }

    function remove(AscendingList storage list, uint256 id) internal returns (bytes32 key) {
        return _remove(list, id);
    }

    function _remove(AscendingList storage list, uint256 id) private returns (bytes32 key) {
        ListDetails memory listDetails = list.__details;
        ListItemDetails memory itemDetails = list.__items[id].details;

        require(listDetails.length > 0, "SortedList: LIST_EMPTY");
        require(itemDetails.exists, "SortedList: INVALID_ID");

        key = list.__keys[id];

        if (listDetails.length == 1) {
            delete list.__details;
        } else {
            if (uint64(id) == listDetails.head) {
                listDetails.head = itemDetails.nextId;
            }
            if (uint64(id) == listDetails.tail) {
                listDetails.tail = itemDetails.prevId;
            }
            if (itemDetails.prevId != 0) {
                list.__items[itemDetails.prevId].details.nextId = itemDetails.nextId;
            }
            if (itemDetails.nextId != 0) {
                list.__items[itemDetails.nextId].details.prevId = itemDetails.prevId;
            }

            --listDetails.length;
            list.__details = listDetails;
        }

        // TODO confirm the gas of this -- does this clear all the structs?
        delete list.__items[id];
        delete list.__keys[id];
    }
}
Percent.sol 31 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

library Percent {
    uint256 internal constant BASE_PERCENT = type(uint16).max;

    function isValidPercent(uint256 nb) internal pure returns (bool) {
        return nb <= BASE_PERCENT;
    }

    function validatePercent(uint256 nb) internal pure {
        require(isValidPercent(nb), "Percent: INVALID_NUMBER");
    }

    function applyPercent(uint256 nb, uint256 _percent) internal pure returns (uint256) {
        return (nb * _percent) / BASE_PERCENT;
    }

    function inverseApplyPercent(uint256 nb, uint256 _percent) internal pure returns (uint256) {
        return asPercent(nb) / (_percent);
    }

    function percentValueOf(uint256 value, uint256 total) internal pure returns (uint256) {
        return asPercent(value) / total;
    }

    function asPercent(uint256 value) internal pure returns (uint256) {
        return value * BASE_PERCENT;
    }
}
DynamicArray.sol 97 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

library DynamicArray {
    struct Bytes32Array {
        uint256 length;
        bytes __data;
    }

    function push(Bytes32Array memory array, bytes32 item) internal pure {
        ++array.length;
        array.__data = abi.encodePacked(array.__data, item);
    }

    function toArray(Bytes32Array memory array) internal pure returns (bytes32[] memory) {
        return abi.decode(abi.encodePacked(array.length, array.__data), (bytes32[]));
    }

    function includes(Bytes32Array memory array, bytes32 item) internal pure returns (bool) {
        return indexOf(array, item) != type(uint256).max;
    }

    function indexOf(Bytes32Array memory array, bytes32 item) internal pure returns (uint256) {
        return _indexOf(array, item);
    }

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

    struct Uint256Array {
        uint256 length;
        bytes __data;
    }

    function push(Uint256Array memory array, uint256 item) internal pure {
        ++array.length;
        array.__data = abi.encodePacked(array.__data, item);
    }

    function toArray(Uint256Array memory array) internal pure returns (uint256[] memory) {
        return abi.decode(abi.encodePacked(array.length, array.__data), (uint256[]));
    }

    function includes(Uint256Array memory array, uint256 item) internal pure returns (bool) {
        return indexOf(array, item) != type(uint256).max;
    }

    function indexOf(Uint256Array memory array, uint256 item) internal pure returns (uint256) {
        return _indexOf(Bytes32Array(array.length, array.__data), bytes32(item));
    }

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

    struct AddressArray {
        uint256 length;
        bytes __data;
    }

    function push(AddressArray memory array, address item) internal pure {
        ++array.length;
        array.__data = abi.encodePacked(array.__data, abi.encode(item));
    }

    function toArray(AddressArray memory array) internal pure returns (address[] memory) {
        return abi.decode(abi.encodePacked(array.length, array.__data), (address[]));
    }

    function includes(AddressArray memory array, address item) internal pure returns (bool) {
        return indexOf(array, item) != type(uint256).max;
    }

    function indexOf(AddressArray memory array, address item) internal pure returns (uint256) {
        return _indexOf(Bytes32Array(array.length, array.__data), bytes32(uint256(uint160(item)) << 96));
    }

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

    function _indexOf(Bytes32Array memory array, bytes32 item) private pure returns (uint256) {
        bytes memory data = array.__data;
        for (uint256 i = 0; i < array.length; ++i) {
            bytes32 piece;
            uint256 pos;

            unchecked {
                pos = i * 32;
            }
            assembly {
                piece := mload(add(data, pos))
            }

            if (piece == item) {
                return i;
            }
        }
        return type(uint256).max;
    }
}
GuardableUpgradeable.sol 90 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "../../SomaAccessControl/utils/AccessibleUpgradeable.sol";

import "../ISomaGuard.sol";

import "./GuardHelper.sol";
import "./IGuardable.sol";

/**
 * @notice Implementation of the {IGuardable} interface.
 */
abstract contract GuardableUpgradeable is IGuardable, AccessibleUpgradeable {
    /**
     * @notice Initializer for extended contracts.
     */
    function __Guardable__init() internal {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __Guardable__init_unchained();
        __SomaContract_init_unchained();
        __Accessible_init_unchained();
        __Multicall_init_unchained();
        __Pausable_init_unchained();
    }

    /**
     * @notice Unchained initializer for extended contracts.
     */
    function __Guardable__init_unchained() internal onlyInitializing {
        LOCAL_UPDATE_PRIVILEGES_ROLE = keccak256(abi.encodePacked(address(this), GLOBAL_UPDATE_PRIVILEGES_ROLE));
        _updateRequiredPrivileges(bytes32(type(uint256).max));
    }

    bytes32 public immutable DEFAULT_PRIVILEGES = GuardHelper.DEFAULT_PRIVILEGES;
    bytes32 public constant GLOBAL_UPDATE_PRIVILEGES_ROLE = keccak256("Guardable.GLOBAL_UPDATE_PRIVILEGES_ROLE");

    bytes32 public LOCAL_UPDATE_PRIVILEGES_ROLE;
    bytes32 private _requiredPrivileges;

    /**
     * @notice Modifier restricting a function call to accounts that have the required privileges.
     */
    modifier onlyApprovedPrivileges(address sender) {
        require(hasPrivileges(sender), "required privileges not met");
        _;
    }

    /**
     * @inheritdoc IGuardable
     */
    function hasPrivileges(address account) public view virtual override returns (bool) {
        return ISomaGuard(SOMA.guard()).check(account, requiredPrivileges());
    }

    /**
     * @inheritdoc IGuardable
     */
    function requiredPrivileges() public view virtual override returns (bytes32) {
        return _requiredPrivileges;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IGuardable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc IGuardable
     */
    function updateRequiredPrivileges(bytes32 newRequiredPrivileges) external virtual override returns (bool) {
        require(
            hasRole(LOCAL_UPDATE_PRIVILEGES_ROLE, _msgSender()) || hasRole(GLOBAL_UPDATE_PRIVILEGES_ROLE, _msgSender()),
            "Guardable: you do not have the required roles to do this"
        );
        _updateRequiredPrivileges(newRequiredPrivileges);
        return true;
    }

    function _updateRequiredPrivileges(bytes32 newRequiredPrivileges) internal {
        emit RequiredPrivilegesUpdated(_requiredPrivileges, newRequiredPrivileges, _msgSender());
        _requiredPrivileges = newRequiredPrivileges;
    }

    uint256[50] private __gap;
}
SafeERC20Balance.sol 18 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library SafeERC20Balance {
    using SafeERC20 for IERC20;

    function safeTransferFrom(IERC20 token, address sender, address receiver, uint256 amount)
        internal
        returns (uint256)
    {
        uint256 prevBalance = token.balanceOf(receiver);
        token.safeTransferFrom(sender, receiver, amount);
        return token.balanceOf(receiver) - prevBalance;
    }
}
TokenRecoveryUpgradeable.sol 82 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

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

import "../../SomaAccessControl/utils/AccessibleUpgradeable.sol";

import "./ITokenRecoveryUpgradeable.sol";

/**
 * @notice Implementation of the {ITokenRecoveryUpgradeable} interface.
 */
abstract contract TokenRecoveryUpgradeable is ITokenRecoveryUpgradeable, AccessibleUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;
    using SafeERC20 for IERC20;

    /**
     * @notice Initializer for the extended contracts.
     */
    function __TokenRecovery__init() internal onlyInitializing {
        __TokenRecovery__init(new address[](0));
    }

    /**
     * @notice Initializer for the extended contracts.
     */
    function __TokenRecovery__init(address[] memory disabledTokens) internal onlyInitializing {
        __ERC165_init_unchained();
        __Context_init_unchained();
        __SomaContract_init_unchained();
        __Multicall_init_unchained();
        __Pausable_init_unchained();
        __Accessible_init_unchained();
        __TokenRecovery__init_unchained(disabledTokens);
    }

    /**
     * @notice Unchained initializer.
     */
    function __TokenRecovery__init_unchained(address[] memory disabledTokens) internal onlyInitializing {
        for (uint256 i; i < disabledTokens.length; ++i) {
            _disabledTokens.add(disabledTokens[i]);
        }
    }

    /**
     * @inheritdoc ITokenRecoveryUpgradeable
     */
    bytes32 public constant override TOKEN_RECOVERY_ROLE = keccak256("TokenRecovery.TOKEN_RECOVERY_ROLE");

    EnumerableSet.AddressSet private _disabledTokens;

    /**
     * @notice Checks if TokenRecoveryUpgradeable inherits a given contract interface.
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(ITokenRecoveryUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc ITokenRecoveryUpgradeable
     */
    function recoverTokens(address token, address to, uint256 amount) external override onlyRole(TOKEN_RECOVERY_ROLE) {
        require(!_disabledTokens.contains(token), "TokenRecovery: INVALID_TOKEN");
        IERC20(token).safeTransfer(to, amount);
        emit TokensRecovered(token, to, amount, _msgSender());
    }

    function _disableTokenRecovery(address token) internal {
        _disabledTokens.add(token);
    }

    function _enableTokenRecovery(address token) internal {
        _disabledTokens.remove(token);
    }

    uint256[50] private __gap;
}
SomaStakingLibrary.sol 28 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "./ISomaStaking.sol";

library SomaStakingLibrary {
    uint256 internal constant PRECISION_FACTOR = 10 ** 25;

    function rewardsUnlocked(ISomaStaking.Strategy memory _strategy, uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        uint256 locked = (timestamp >= _strategy.endDate)
            ? _strategy.rewardsLocked
            : ((timestamp - _strategy.startDate) * _strategy.rewardsLocked) / (_strategy.endDate - _strategy.startDate);
        return locked - _strategy.rewardsUnlocked;
    }

    function rewardsToTPS(uint256 totalStake, uint256 _rewardsUnlocked) internal pure returns (uint256 tps) {
        return (_rewardsUnlocked * PRECISION_FACTOR) / totalStake;
    }

    function stakeToRewards(uint256 stake, uint256 tps) internal pure returns (uint256) {
        return (stake * tps) / PRECISION_FACTOR;
    }
}
ISomaStaking.sol 495 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Staking Contract.
 * @author SOMA.finance.
 * @notice A staking contract that supports multiple reward tokens and strategies.
 */
interface ISomaStaking {
    /**
     * Events ***********************************
     */

    /**
     * @notice Emitted when a user Stakes.
     * @param amount The amount staked, denominated in the staking token.
     * @param sender The address of the message sender.
     */
    event Staked(uint256 amount, address indexed sender);

    /**
     * @notice Emitted when a user Unstakes.
     * @param amount The amount unstaked, denominated in the staking token.
     * @param sender The address of the message sender.
     */
    event Unstaked(uint256 amount, address indexed sender);

    /**
     * @notice Emitted when a user Unstakes immediately.
     * @param amount The amount of the staking token unstaked.
     * @param fee The early unstake fee charged.
     * @param sender The address of the message sender.
     */
    event UnstakedImmediate(uint256 amount, uint256 fee, address indexed sender);

    /**
     * @notice Emitted when a user Claims.
     * @param asset The address of the reward token claimed.
     * @param amount The amount of the reward token claimed.
     * @param sender The address of the message sender.
     */
    event Claimed(address indexed asset, uint256 amount, address indexed sender);

    /**
     * @notice Emitted when a user Claims immediately.
     * @param asset The address of the reward token claimed.
     * @param amount The amount of the reward token claimed.
     * @param fee The early claim fee.
     * @param sender The address of the message sender.
     */
    event ClaimedImmediate(address indexed asset, uint256 amount, uint256 fee, address indexed sender);

    /**
     * @notice Emitted when a Strategy is created.
     * @param rewardToken The address of the reward token.
     * @param amount The amount of the reward tokens.
     * @param startDate The timestamp marking the start of the strategy.
     * @param endDate The timestamp marking the end of the strategy.
     * @param sender The address of the message sender.
     */
    event StrategyCreated(
        address indexed rewardToken, uint256 amount, uint256 startDate, uint256 endDate, address indexed sender
    );

    /**
     * @notice Emitted when a Admin claims.
     * @param asset The address of the asset claimed.
     * @param amount The amount of the asset claimed.
     * @param to The address that the claimed tokens is sent to.
     * @param sender The address of the message sender.
     */
    event AdminClaimed(address indexed asset, uint256 amount, address indexed to, address indexed sender);

    /**
     * @notice Emitted when the Staking Config is updated.
     * @param prevConfig The previous staking configuration.
     * @param newConfig The new staking configuration.
     * @param sender The address of the message sender.
     */
    event StakingConfigUpdated(StakingConfig prevConfig, StakingConfig newConfig, address indexed sender);

    /**
     * @notice Emitted when an Unstake or Claim Request is created.
     * @param id The ID of the request.
     * @param asset The asset of the request.
     * @param amount The amount of the requested asset.
     * @param sender The address of the message sender.
     * @param requestType The type of request.
     */
    event RequestCreated(
        uint256 indexed id, address indexed asset, uint256 amount, address indexed sender, RequestType requestType
    );

    /**
     * @notice Emitted when an Unstake or Claim Request is cancelled.
     * @param id The ID of the request.
     */
    event RequestCancelled(uint256 indexed id);

    /**
     * @notice Emitted when an Unstake or Claim Request is fulfilled.
     * @param id The ID of the request.
     */
    event RequestFulfilled(uint256 indexed id);

    /**
     * @notice Emitted when a Reward Token is added.
     * @param token The address of the reward token.
     * @param sender The address of the message sender.
     */
    event RewardTokenAdded(address indexed token, address indexed sender);

    /**
     * @notice Emitted when a Staking Token is seized.
     * @param from The address that is having their tokens seized.
     * @param to The address to receive the seized tokens.
     * @param amount The amount of tokens seized.
     * @param seizedRewards The array of seized reward token amounts.
     * @param sender The address of the message sender.
     */
    event Seized(
        address indexed from, address indexed to, uint256 amount, uint256[] seizedRewards, address indexed sender
    );

    /**
     * Enum ***********************************
     */

    /**
     * @notice Request type.
     */
    enum RequestType {
        UNSTAKE,
        CLAIM
    }

    /**
     * Structs ***********************************
     */

    /**
     * @notice Staking Configuration structure. Defines the fees and unstake duration.
     * @param unstakeDuration The amount of seconds that must pass before an unstake request is fulfilled.
     * @param claimDuration The amount of seconds that must pass before a claim request is fulfilled.
     * @param earlyUnstakeFee The fee charged for early unstakes.
     * @param earlyClaimFee The fee charged for early claims.
     */
    struct StakingConfig {
        uint64 unstakeDuration;
        uint64 claimDuration;
        uint16 earlyUnstakeFee;
        uint16 earlyClaimFee;
    }

    /**
     * @notice Request Configuration structure.
     * @param hash The ID of the request.
     * @param timestamp The timestamp that the request was created at.
     * @param amount The amount of the reward token requested.
     */
    struct Request {
        bytes8 hash;
        uint64 timestamp;
        uint128 amount;
    }

    /**
     * @notice User Information structure.
     * @param stake The amount of tokens the user has staked.
     * @param claimable The mapping of reward token to the claimable rewards amount for the user.
     * @param debt The mapping of reward token to rewards amount for the user.
     */
    struct UserInfo {
        uint256 stake; // How many tokens the user has staked
        mapping(address => uint256) claimable;
        mapping(address => uint256) debt;
    }

    /**
     * @notice Strategy structure. Defines the rewards that are released over a particular amount of time for
     * an asset.
     * @param startDate The timestamp marking the start of the strategy.
     * @param endDate The timestamp marking the end of the strategy.
     * @param rewardToken The address of the reward token.
     * @param rewardsLocked The amount of locked reward tokens.
     * @param rewardsUnlocked The amount of unlocked reward tokens.
     */
    struct Strategy {
        uint48 startDate;
        uint48 endDate;
        address rewardToken;
        uint128 rewardsLocked;
        uint128 rewardsUnlocked;
    }

    /**
     * @notice Returns the Staking Global Admin Role.
     * @dev Equivalent to keccak256('Staking.GLOBAL_ADMIN_ROLE').
     */
    function GLOBAL_ADMIN_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns the Staking Local Admin Role.
     */
    function LOCAL_ADMIN_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the Staking Global Seize Role.
     * @dev Equivalent to keccak256('Staking.GLOBAL_SEIZE_ROLE').
     */
    function GLOBAL_SEIZE_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns the Staking Local Seize Role.
     */
    function LOCAL_SEIZE_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the Staking Configuration.
     */
    function config() external view returns (StakingConfig memory);

    /**
     * @notice Returns the strategy, given a strategy ID.
     * @param id The ID of the strategy.
     * @return The strategy with the matching ID.
     */
    function strategy(uint256 id) external view returns (Strategy memory);

    /**
     * @notice Returns the pending strategy, given a strategy ID.
     * @param index The ID of the pending strategy.
     * @return The pending strategy with the matching ID.
     */
    function pendingStrategy(uint256 index) external view returns (Strategy memory);

    /**
     * @notice Returns the total number of strategies created.
     */
    function totalStrategies() external view returns (uint256);

    /**
     * @notice Returns the total number of pending strategies.
     */
    function totalPendingStrategies() external view returns (uint256);

    /**
     * @notice Returns the total number of staked tokens.
     */
    function totalStaked() external view returns (uint256);

    /**
     * @notice Returns the total number of staking tokens pending unstake.
     */
    function totalPendingUnstake() external view returns (uint256);

    /**
     * @notice Returns the last recorded tokens per share, given a token.
     * @param _asset The token to return the tokens per share of.
     * @return The tokens per share of `_asset`.
     */
    function tps(address _asset) external view returns (uint256);

    /**
     * @notice Returns the current tokens per share, given a token.
     * @param token The token to return the current tokens per share of.
     * @param tps_ The current tokens per share of `token`.
     */
    function currentTPS(address token) external view returns (uint256 tps_);

    /**
     * @notice Returns the number of reward tokens.
     */
    function totalRewardTokens() external view returns (uint256);

    /**
     * @notice Returns the address of a reward token, given an index.
     * @param index The index of the reward token.
     */
    function rewardToken(uint256 index) external view returns (address);

    /**
     * @notice Returns the address of the staking token.
     */
    function stakingToken() external view returns (address);

    /**
     * @notice Returns the staked balance of an account.
     * @param _account The account to return the staked balance of.
     * @return The staked balance of the account, denominated in the staking token.
     */
    function stakeOf(address _account) external view returns (uint256);

    /**
     * @notice Returns an Unstake Request, given an account and Request ID.
     * @param _account The account to return the Request of.
     * @param _id The ID of the request.
     * @return The Request with the matching ID.
     */
    function unstakeRequest(address _account, uint256 _id) external view returns (Request memory);

    /**
     * @notice Returns the debt of an account, given an asset.
     * @param _account The account to return the debt of.
     * @param _asset The asset to return the account's debt of.
     * @return The debt of the account, denominated in the asset.
     */
    function debt(address _account, address _asset) external view returns (uint256);

    /**
     * @notice Returns the amount of claimable tokens of an account, given an asset.
     * @param _account The account to return the claimable tokens of.
     * @param _asset The asset to return the claimable amount of.
     * @return The number of claimable tokens.
     */
    function claimable(address _account, address _asset) external view returns (uint256);

    /**
     * @notice Returns the Claim Request of an account, given an asset and Request ID.
     * @param _account The account to return the Claim Request of.
     * @param _asset The asset to return the Claim Request of.
     * @param _id The ID of the Claim Request.
     * @return The Claim Request.
     */
    function claimRequest(address _account, address _asset, uint256 _id) external view returns (Request memory);

    /**
     * @notice Initializer for the Staking Contract.
     * @param stakingToken_ The address of the staking token.
     * @param rewardTokens_ An array of addresses of the reward tokens.
     * @custom:emits Initialized
     * @custom:requirement `stakingToken_` must be a contract.
     * @custom:requirement Each token in `rewardTokens_` must be a contract.
     */
    function initialize(address stakingToken_, address[] memory rewardTokens_) external;

    /**
     * @notice Stakes an amount of staking tokens.
     * @param _amount The amount of tokens to stake.
     * @custom:emits Staked
     * @custom:requirement `_amount` must be greater than zero.
     */
    function stake(uint256 _amount) external;

    /**
     * @notice Creates an Unstake Request. This is required before performing an unstake. Creating an
     * unstake request will stop users from earning rewards on the amount that they are unstaking for.
     * @param _amount The amount of tokens requested to unstake.
     * @custom:emits RequestCreated
     * @custom:requirement The stake of the message sender must be greater than or equal to `_amount`.
     * @custom:requirement `_amount` must be greater than zero.
     * @return _id The ID of the created Unstake Request.
     */
    function createUnstakeRequest(uint256 _amount) external returns (uint256 _id);

    /**
     * @notice Cancels multiple Unstake Requests. Cancelling an unstake will return the staking tokens to
     * the contract allowing the user to earn rewards on these tokens again.
     * @param _ids An array of Request IDs to cancel the unstake requests for.
     * @custom:emits RequestCancelled
     * @custom:requirement The length of `_ids` must be greater than zero.
     */
    function cancelUnstakeRequests(uint256[] calldata _ids) external;

    /**
     * @notice Unstakes the input requests, given that the required unstake duration has passed. This unstake has no fees
     * associated with the transaction.
     * @param _ids An array of Request IDs.
     * @custom:emits Unstaked
     * @custom:emits RequestFulfilled
     * @custom:requirement The difference between the timestamp of the function call and the timestamp of the request creation
     * must be greater than or equal to the Unstake required duration.
     */
    function unstake(uint256[] calldata _ids) external;

    /**
     * @notice Unstakes tokens immediately. This incurs a fee to bypass the unstake duration required to unstake.
     * @param _amount The number of staking tokens to unstake immediately.
     * @custom:emits UnstakedImmediate
     * @custom:requirement The staked balance of the message caller must be greater than or equal to `_amount`.
     * @custom:requirement `_amount` must be greater than or equal to zero.
     */
    function unstakeImmediate(uint256 _amount) external;

    /**
     * @notice Creates Claim Requests for multiple rewards tokens. This is required before performing a claim.
     * @param _assets The array of rewards tokens to create Claim Requests for.
     * @custom:emits RequestCreated
     * @custom:requirement Each asset in `_assets` must be a valid reward token.
     * @custom:requirement The claimable amount for the message sender, for each asset in `_assets` must be greater than zero.
     * @return _ids The array of created Claim Requests IDs.
     */
    function createClaimRequests(address[] calldata _assets) external returns (uint256[] memory _ids);

    /**
     * @notice Cancels multiple Claim Requests.
     * @param _assets The array of rewards tokens to cancel Claim Requests for.
     * @param _ids The 2D array of Request IDs to cancel requests of.
     * @custom:emits RequestCancelled
     * @custom:requirement The length of `_assets` must be greater than zero.
     * @custom:requirement The length of `_ids` must be equal to the length of `_assets`.
     */
    function cancelClaimRequests(address[] calldata _assets, uint256[][] calldata _ids) external;

    /**
     * @notice Claims an amount of reward tokens, provided that each request is ready to be claimed.
     * @param _assets The array of rewards tokens to fulfill Claim Requests for.
     * @param _ids The 2D array of Request IDs to fulfill requests of.
     * @custom:emits Claimed
     * @custom:emits RequestFulfilled
     * @custom:requirement The length of `_ids` must be equal to the length of `_asset`.
     */
    function claim(address[] calldata _assets, uint256[][] calldata _ids) external;

    /**
     * @notice Claims an amount of reward tokens immediately, bypassing the claim duration in exchange for a fee.
     * @param _assets The array of rewards tokens to claim immediately.
     * @param _amounts The array of amounts for each reward token to claim immediately.
     * @custom:emits ClaimedImmediate
     * @custom:requirement The length of `_assets` must be equal to the length of `_amounts`.
     * @custom:requirement Each asset in `_assets` must be a valid reward token.
     */
    function claimImmediate(address[] calldata _assets, uint256[] calldata _amounts) external;

    // ********************************** ADMIN **********************************

    /**
     * @notice Returns the amount of admin claimable tokens originating from fees.
     * @param _asset The asset to return the admin claimable balance of.
     * @custom:requirement `_asset` must be a valid reward token.
     * @return The amount of admin claimable tokens, denominated in `_asset`.
     */
    function adminClaimable(address _asset) external view returns (uint256);

    /**
     * @notice Adds a reward token to contract. Once a reward token has been added, it cannot be removed.
     * @param _asset The asset to add as a reward token.
     * @custom:emits RewardTokenAdded
     * @custom:requirement `_asset` must not be an existing reward token.
     * @custom:requirement The message sender must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function addRewardToken(address _asset) external;

    /**
     * @notice Claims the admin claimable amount for a reward token.
     * @param _asset The reward token to claim as an admin.
     * @param _to The address to send the claimed tokens to.
     * @custom:emits AdminClaimed
     * @custom:requirement `_asset` must not be an existing reward token.
     * @custom:requirement The claimable amount of `_asset` must be greater than zero.
     * @custom:requirement The message sender must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function adminClaim(address _asset, address _to) external;

    /**
     * @notice Creates a Strategy.
     * @param _startDate The timestamp marking the start of the strategy.
     * @param _endDate The timestamp marking the end of the strategy.
     * @param _rewardToken The address of the reward token.
     * @param _rewardAmount The amount of the reward token.
     * @custom:emits StrategyCreated
     * @custom:requirement `_startDate` must be greater than the timestamp of the function call.
     * @custom:requirement `_startDate` must be less than `_endDate`.
     * @custom:requirement `_rewardToken` must be a valid existing reward token.
     * @custom:requirement `_endDate` must be a valid uint48.
     * @custom:requirement The message sender must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function createStrategy(uint256 _startDate, uint256 _endDate, address _rewardToken, uint256 _rewardAmount)
        external;

    /**
     * @notice Updates the Staking Configuration.
     * @param _unstakeDuration The new unstake duration.
     * @param _claimDuration The new claim duration.
     * @param _earlyUnstakeFee The new early unstake fee.
     * @param _earlyClaimFee The new early claim fee.
     * @custom:emits StakingConfigUpdated
     * @custom:requirement The message sender must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function updateConfig(
        uint64 _unstakeDuration,
        uint64 _claimDuration,
        uint16 _earlyUnstakeFee,
        uint16 _earlyClaimFee
    ) external;

    /**
     * @notice Seizes staking tokens from `from`.
     * @param from The address to have their tokens seized.
     * @custom:emits Seized
     * @custom:requirement The user's staked balance must be greater than zero.
     */
    function seize(address from) external;
}
Initializable.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

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

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }
}
AddressUpgradeable.sol 195 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
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);
}
draft-IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
EnumerableSet.sol 367 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
AccessibleUpgradeable.sol 69 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts/access/IAccessControl.sol";

import "../../utils/security/IPausable.sol";
import "../../utils/SomaContractUpgradeable.sol";

import "../ISomaAccessControl.sol";
import "./IAccessible.sol";

/**
 * @title SOMA Accessible Upgradeable Contract.
 * @author SOMA.finance
 */
abstract contract AccessibleUpgradeable is IAccessible, SomaContractUpgradeable {
    /**
     * @notice Initializer for extended contracts.
     */
    function __Accessible_init() internal onlyInitializing {
        __ERC165_init_unchained();
        __Context_init_unchained();
        __SomaContract_init_unchained();
        __Multicall_init_unchained();
        __Pausable_init_unchained();
        __Accessible_init_unchained();
    }

    /**
     * @notice Unchained initializer for extended contracts.
     */
    function __Accessible_init_unchained() internal onlyInitializing {}

    /**
     * @notice The modifier that restricts a function caller to accounts that have been granted `role`.
     * @param role The role that an account must have to execute a function.
     */
    modifier onlyRole(bytes32 role) {
        require(hasRole(role, _msgSender()), "SomaAccessControl: caller does not have the appropriate authority");
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessible).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc IAccessible
     */
    // slither-disable-next-line external-function
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return IAccessControlUpgradeable(SOMA.access()).getRoleAdmin(role);
    }

    /**
     * @inheritdoc IAccessible
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return IAccessControlUpgradeable(SOMA.access()).hasRole(role, account);
    }

    uint256[50] private __gap;
}
ISomaGuard.sol 89 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title SOMA Guard Contract.
 * @author SOMA.finance
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 * @notice A contract to batch update account privileges.
 */
interface ISomaGuard {
    /**
     * @notice Emitted when privileges for a 2D array of accounts are updated.
     * @param accounts The 2D array of addresses.
     * @param privileges The array of privileges.
     * @param sender The address of the message sender.
     */
    event BatchUpdate(address[][] accounts, bytes32[] privileges, address indexed sender);

    /**
     * @notice Emitted when privileges for an array of accounts are updated.
     * @param accounts The array of addresses.
     * @param access The array of privileges.
     * @param sender The address of the message sender.
     */
    event BatchUpdateSingle(address[] accounts, bytes32[] access, address indexed sender);

    /**
     * @notice Returns the default privileges of the SomaGuard contract.
     * @dev Returns bytes32(uint256(2 ** 64 - 1)).
     */
    function DEFAULT_PRIVILEGES() external view returns (bytes32);

    /**
     * @notice Returns the operator role of the SomaGuard contract.
     * @dev Returns keccak256('SomaGuard.OPERATOR_ROLE').
     */
    function OPERATOR_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the privilege of an account.
     * @param account The account to return the privilege of.
     */
    function privileges(address account) external view returns (bytes32);

    /**
     * @notice Returns True if an account passes a query, where query is the desired privileges.
     * @param account The account to check the privileges of.
     * @param query The desired privileges to check for.
     */
    function check(address account, bytes32 query) external view returns (bool);

    /**
     * @notice Returns the privileges for each account.
     * @param accounts_ The array of accounts return the privileges of.
     * @return privileges_ The array of privileges.
     */
    function batchFetch(address[] calldata accounts_) external view returns (bytes32[] memory privileges_);

    /**
     * @notice Updates the privileges of an array of accounts.
     * @param accounts_ The array of addresses to accumulate privileges of.
     * @param privileges_ The array of privileges to update the array of accounts with.
     * @custom:emits BatchUpdateSingle
     * @custom:requirement The length of `accounts_` must be equal to the length of `privileges_`.
     * @custom:requirement The length of `accounts_` must be greater than zero.
     * @custom:requirement The function caller must have the OPERATOR_ROLE.
     * @return True if the batch update was successful.
     */
    function batchUpdate(address[] calldata accounts_, bytes32[] calldata privileges_) external returns (bool);

    /**
     * @notice Updates the privileges of a 2D array of accounts, where the child array of accounts are all assigned to the
     * same privileges.
     * @param accounts_ The array of addresses to accumulate privileges of.
     * @param privileges_ The array of privileges to update the 2D array of accounts with.
     * @custom:emits BatchUpdate
     * @custom:requirement The length of `accounts_` must be equal to the length of `privileges_`.
     * @custom:requirement The length of `accounts_` must be greater than zero.
     * @custom:requirement The function caller must have the OPERATOR_ROLE.
     * @return True if the batch update was successful.
     */
    function batchUpdate(address[][] calldata accounts_, bytes32[] calldata privileges_) external returns (bool);
}
GuardHelper.sol 50 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "./IGuardable.sol";

library GuardHelper {
    // 00000000(192 0's repeated)111(64times)
    // (64 default on, 192 default off)
    bytes32 internal constant DEFAULT_PRIVILEGES = bytes32(uint256(2 ** 64 - 1));

    function requiredPrivileges(address account) internal view returns (bytes32 privileges) {
        // call to account with no code returns true
        // call to account with code and no function returns false
        // call to account with code and function returns true
        (bool success, bytes memory returndata) =
            account.staticcall(abi.encodePacked(IGuardable.requiredPrivileges.selector));
        privileges = (success && (account.code.length > 0)) ? bytes32(returndata) : DEFAULT_PRIVILEGES;
    }

    function check(bytes32 privileges, bytes32 query) internal pure returns (bool) {
        return privileges & query == query;
    }

    function mergePrivileges(bytes32 privileges1, bytes32 privileges2) internal pure returns (bytes32) {
        return privileges1 | privileges2;
    }

    function mergePrivileges(bytes32 privileges1, bytes32 privileges2, bytes32 privileges3)
        internal
        pure
        returns (bytes32)
    {
        return privileges1 | privileges2 | privileges3;
    }

    function switchOn(uint256[] memory ids, bytes32 base) internal pure returns (bytes32 result) {
        result = base;
        for (uint256 i; i < ids.length; ++i) {
            result = result | bytes32(2 ** ids[i]);
        }
    }

    function switchOff(uint256[] memory ids, bytes32 base) internal pure returns (bytes32 result) {
        result = base;
        for (uint256 i; i < ids.length; ++i) {
            result = result & bytes32(type(uint256).max - 2 ** ids[i]);
        }
    }
}
IGuardable.sol 40 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Guardable Upgradeable Contract.
 * @author SOMA.finance
 * @notice Interface of the {GuardableUpgradeable} contract.
 */
interface IGuardable {
    /**
     * @notice Emitted when the required privileges are updated.
     * @param prevPrivileges The previous required privileges.
     * @param newPrivileges The new required privileges.
     * @param sender The message sender that triggered the event.
     */
    event RequiredPrivilegesUpdated(bytes32 prevPrivileges, bytes32 newPrivileges, address indexed sender);

    /**
     * @notice Returns a boolean indicating if `account` has the required privileges.
     * @param account The account to check against the required privileges.
     * @return True if `account` has the required privileges, False otherwise.
     */
    function hasPrivileges(address account) external view returns (bool);

    /**
     * @notice Returns the required privileges.
     */
    function requiredPrivileges() external view returns (bytes32);

    /**
     * @notice Updates the required privileges.
     * @param newRequiredPrivileges The new required privileges.
     * @custom:emits RequiredPrivilegesUpdated
     * @custom:requirement The message sender must have either the LOCAL_UPDATE_PRIVILEGES_ROLE or the
     * GLOBAL_UPDATE_PRIVILEGES_ROLE role.
     * @return True if the update was successful.
     */
    function updateRequiredPrivileges(bytes32 newRequiredPrivileges) external returns (bool);
}
IAccessControlUpgradeable.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
ContextUpgradeable.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
IAccessControl.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
IPausable.sol 11 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

interface IPausable {
    function paused() external view returns (bool);

    function pause() external;

    function unpause() external;
}
SomaContractUpgradeable.sol 60 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";

import "../ISOMA.sol";
import "../SOMAlib.sol";

import "./ISomaContract.sol";

contract SomaContractUpgradeable is ISomaContract, PausableUpgradeable, ERC165Upgradeable, MulticallUpgradeable {
    function __SomaContract_init() internal onlyInitializing {
        __ERC165_init_unchained();
        __Context_init_unchained();
        __SomaContract_init_unchained();
        __Multicall_init_unchained();
        __Pausable_init_unchained();
    }

    function __SomaContract_init_unchained() internal onlyInitializing {}

    ISOMA public immutable override SOMA = SOMAlib.SOMA;

    modifier onlyMaster() {
        address sender = _msgSender();
        require(SOMA.master() == sender, "SOMA: MASTER");
        _;
    }

    modifier onlyMasterOrSubMaster() {
        address sender = _msgSender();
        require(SOMA.master() == sender || SOMA.subMaster() == sender, "SOMA: MASTER or SUB MASTER only");
        _;
    }

    function pause() external virtual override onlyMasterOrSubMaster {
        _pause();
    }

    function unpause() external virtual override onlyMasterOrSubMaster {
        _unpause();
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(ISomaContract).interfaceId || super.supportsInterface(interfaceId);
    }

    function paused() public view virtual override returns (bool) {
        return PausableUpgradeable(address(SOMA)).paused() || super.paused();
    }

    uint256[50] private __gap;
}
ISomaAccessControl.sol 21 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Access Control Contract.
 * @author SOMA.finance.
 * @notice An access control contract that establishes a hierarchy of accounts and controls
 * function call permissions.
 */
interface ISomaAccessControl {
    /**
     * @notice Sets the admin of a role.
     * @dev Sets the admin for the `role` role.
     * @param role The role to set the admin role of.
     * @param adminRole The admin of `role`.
     * @custom:emits RoleAdminChanged
     * @custom:requirement The function caller must have the DEFAULT_ADMIN_ROLE.
     */
    function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
}
IAccessible.sol 25 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Accessible Contract.
 * @author SOMA.finance
 * @notice Interface of the {Accessible} contract.
 */
interface IAccessible {
    /**
     * @notice Returns the role admin, given a role.
     * @param role The role to return the admin of.
     * @return The admin of the role.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @notice Returns a boolean indicating if `account` has been granted `role`.
     * @param role The role to check against `account`.
     * @param account The account to check against `role`.
     * @return True if `account` has been granted `role`, False otherwise.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);
}
ERC165Upgradeable.sol 42 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
PausableUpgradeable.sol 117 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
MulticallUpgradeable.sol 51 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)

pragma solidity ^0.8.0;

import "./AddressUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
abstract contract MulticallUpgradeable is Initializable {
    function __Multicall_init() internal onlyInitializing {
    }

    function __Multicall_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = _functionDelegateCall(address(this), data[i]);
        }
        return results;
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
        require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
ISOMA.sol 170 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "./SomaAccessControl/ISomaAccessControl.sol";
import "./SomaSwap/periphery/ISomaSwapRouter.sol";
import "./SomaSwap/core/interfaces/ISomaSwapFactory.sol";
import "./SomaGuard/ISomaGuard.sol";
import "./TemplateFactory/ITemplateFactory.sol";
import "./Lockdrop/ILockdropFactory.sol";

/**
 * @title SOMA Contract.
 * @author SOMA.finance
 * @notice Interface of the SOMA contract.
 */
interface ISOMA {
    /**
     * @notice Emitted when the SOMA snapshot is updated.
     * @param version The version of the new snapshot.
     * @param hash The hash of the new snapshot.
     * @param snapshot The new snapshot.
     */
    event SOMAUpgraded(bytes32 indexed version, bytes32 indexed hash, bytes snapshot);

    /**
     * @notice Emitted when the `seizeTo` address is updated.
     * @param prevSeizeTo The address of the previous `seizeTo`.
     * @param newSeizeTo The address of the new `seizeTo`.
     * @param sender The address of the message sender.
     */
    event SeizeToUpdated(address indexed prevSeizeTo, address indexed newSeizeTo, address indexed sender);

    /**
     * @notice Emitted when the `mintTo` address is updated.
     * @param prevMintTo The address of the previous `mintTo`.
     * @param newMintTo The address of the new `mintTo`.
     * @param sender The address of the message sender.
     */
    event MintToUpdated(address indexed prevMintTo, address indexed newMintTo, address indexed sender);

    /**
     * @notice Snapshot of the SOMA contracts.
     * @param master The master address.
     * @param subMaster The subMaster address.
     * @param access The ISomaAccessControl contract.
     * @param guard The ISomaGuard contract.
     * @param factory The ITemplateFactory contract.
     * @param token The IERC20 contract.
     */
    struct Snapshot {
        address master;
        address subMaster;
        address access;
        address guard;
        address factory;
        address token;
    }

    /**
     * @notice Returns the address that has been assigned the master role.
     */
    function master() external view returns (address);

    /**
     * @notice Returns the address that has been assigned the subMaster role.
     */
    function subMaster() external view returns (address);

    /**
     * @notice Returns the address of the {ISomaAccessControl} contract.
     */
    function access() external view returns (address);

    /**
     * @notice Returns the address of the {ISomaGuard} contract.
     */
    function guard() external view returns (address);

    /**
     * @notice Returns the address of the {ITemplateFactory} contract.
     */
    function factory() external view returns (address);

    /**
     * @notice Returns the address of the {IERC20} contract.
     */
    function token() external view returns (address);

    /**
     * @notice Returns the hash of the latest snapshot.
     */
    function snapshotHash() external view returns (bytes32);

    /**
     * @notice Returns the latest snapshot version.
     */
    function snapshotVersion() external view returns (bytes32);

    /**
     * @notice Returns the snapshot, given a snapshot hash.
     * @param hash The snapshot hash.
     * @return _snapshot The snapshot matching the `hash`.
     */
    function snapshots(bytes32 hash) external view returns (bytes memory _snapshot);

    /**
     * @notice Returns the hash when given a version, returns a version when given a hash.
     * @param versionOrHash The version or hash.
     * @return hashOrVersion The hash or version based on the input.
     */
    function versions(bytes32 versionOrHash) external view returns (bytes32 hashOrVersion);

    /**
     * @notice Returns the address that receives all minted tokens.
     */
    function mintTo() external view returns (address);

    /**
     * @notice Returns the address that receives all seized tokens.
     */
    function seizeTo() external view returns (address);

    /**
     * @notice Updates the current SOMA snapshot and is called after the proxy has been upgraded.
     * @param version The version to upgrade to.
     * @custom:emits SOMAUpgraded
     * @custom:requirement The incoming snapshot hash cannot be equal to the contract's existing snapshot hash.
     */
    function __upgrade(bytes32 version) external;

    /**
     * @notice Triggers the SOMA paused state. Pauses all the SOMA contracts.
     * @custom:emits Paused
     * @custom:requirement SOMA must be already unpaused.
     * @custom:requirement The caller must be the master or subMaster.
     */
    function pause() external;

    /**
     * @notice Triggers the SOMA unpaused state. Unpauses all the SOMA contracts.
     * @custom:emits Unpaused
     * @custom:requirement SOMA must be already paused.
     * @custom:requirement The caller must be the master or subMaster.
     */
    function unpause() external;

    /**
     * @notice Sets the `mintTo` address to `_mintTo`.
     * @param _mintTo The address to be set as the `mintTo` address.
     * @custom:emits MintToUpdated
     * @custom:requirement The caller must be the master.
     */
    function setMintTo(address _mintTo) external;

    /**
     * @notice Sets the `seizeTo` address to `_seizeTo`.
     * @param _seizeTo The address to be set as the `seizeTo` address.
     * @custom:emits SeizeToUpdated
     * @custom:requirement The caller must be the master.
     */
    function setSeizeTo(address _seizeTo) external;

    /**
     * @notice Returns the current snapshot of the SOMA contracts.
     */
    function snapshot() external view returns (Snapshot memory _snapshot);
}
SOMAlib.sol 12 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity =0.8.18;

import "./ISOMA.sol";

library SOMAlib {
    /**
     * @notice The fixed address where the SOMA contract will be located (this is a proxy).
     */
    ISOMA public constant SOMA = ISOMA(0x0F3dC00189dbCD1D0c574e48031270cAe04C4ADF);
}
ISomaContract.sol 12 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "../ISOMA.sol";

interface ISomaContract {
    function pause() external;
    function unpause() external;

    function SOMA() external view returns (ISOMA);
}
IERC165Upgradeable.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
ISomaSwapRouter.sol 483 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2;

/**
 * @title SOMA Swap Router Contract.
 * @author SOMA.finance
 * @notice Interface for the {SomaSwapRouter} contract.
 */
interface ISomaSwapRouter {
    /**
     * @notice Returns the address of the factory contract.
     */
    function factory() external view returns (address);

    /**
     * @notice Returns the address of the WETH token.
     */
    function WETH() external view returns (address);

    /**
     * @notice Adds liquidity to the pool.
     * @param tokenA The token0 of the pair to add liquidity to.
     * @param tokenB The token1 of the pair to add liquidity to.
     * @param amountADesired The amount of token0 to add as liquidity.
     * @param amountBDesired The amount of token1 to add as liquidity.
     * @param amountAMin The bound of the tokenB / tokenA price can go up
     * before transaction reverts.
     * @param amountBMin The bound of the tokenA / tokenB price can go up
     * before transaction reverts.
     * @param to The address to receive the liquidity tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement `tokenA` and `tokenB` pair must already exist.
     * @custom:requirement the router's expiration deadline must be greater than the timestamp of the
     * function call
     * @return amountA The amount of tokenA added as liquidity.
     * @return amountB The amount of tokenB added as liquidity.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    /**
     * @notice Adds liquidity to the pool with ETH.
     * @param token The pool token.
     * @param amountTokenDesired The amount of token to add as liquidity if WETH/token price
     * is less or equal to the value of msg.value/amountTokenDesired (token depreciates).
     * @param amountTokenMin The bound that WETH/token price can go up before the transactions
     * reverts.
     * @param amountETHMin The bound that token/WETH price can go up before the transaction reverts.
     * @param to The recipient of the liquidity tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement `tokenA` and `tokenB` pair must already exist.
     * @custom:requirement the router's expiration deadline must be greater than the timestamp of the
     * function call
     * @return amountToken The amount of token sent to the pool.
     * @return amountETH The amount of ETH converted to WETH and sent to the pool.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);

    /**
     * @notice Removes liquidity from the pool.
     * @param tokenA The pool token.
     * @param tokenB The pool token.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param amountAMin The minimum amount of tokenA that must be received
     * for the transaction not to revert.
     * @param amountBMin The minimum amount of tokenB that must be received
     * for the transaction not to revert.
     * @param to The recipient of the underlying asset.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement `amountA` must be greater than or equal to `amountAMin`.
     * @custom:requirement `amountB` must be greater than or equal to `amountBMin`.
     * @return amountA The amount of tokenA received.
     * @return amountB The amount of tokenB received.
     */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    /**
     * @notice Removes liquidity from the pool and the caller receives ETH.
     * @param token The pool token.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param amountTokenMin The minimum amount of tokens that must be received
     * for the transaction not to revert.
     * @param amountETHMin The minimum amount of ETH that must be received for the
     * transaction not to revert.
     * @param to The recipient of the underlying assets.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement `amountA` must be greater than or equal to `amountAMin`.
     * @custom:requirement `amountB` must be greater than or equal to `amountBMin`.
     * @return amountToken The amount of token received.
     * @return amountETH The amount of ETH received.
     */
    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    /**
     * @notice Removes liquidity from the pool without pre-approval.
     * @param tokenA The pool token0.
     * @param tokenB The pool token1.
     * @param liquidity The amount of liquidity to remove.
     * @param amountAMin The minimum amount of tokenA that must be received for the
     * transaction not to revert.
     * @param amountBMin The minimum amount of tokenB that must be received for the
     * transaction not to revert.
     * @param to The recipient of the underlying asset.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @param approveMax Boolean value indicating if the approval amount in the signature
     * is for liquidity or uint(-1).
     * @param v The v component of the permit signature.
     * @param r The r component of the permit signature.
     * @param s The s component of the permit signature.
     * @custom:requirement `amountA` must be greater than or equal to `amountAMin`.
     * @custom:requirement `amountB` must be greater than or equal to `amountBMin`.
     * @return amountA The amount of tokenA received.
     * @return amountB The amount of tokenB received.
     */
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    /**
     * @notice Removes liquidity from the pool and the caller receives ETH without pre-approval.
     * @param token The pool token.
     * @param liquidity The amount of liquidity to remove.
     * @param amountTokenMin The minimum amount of token that must be received for the transaction
     * not to revert.
     * @param amountETHMin The minimum amount of ETH that must be received for the transaction not
     * to revert.
     * @param to The recipient of the underlying asset.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @param approveMax Boolean value indicating if the approval amount in the signature
     * is for liquidity or uint(-1).
     * @param v The v component of the permit signature.
     * @param r The r component of the permit signature.
     * @param s The s component of the permit signature.
     * @return amountToken The amount fo token received.
     * @return amountETH The amount of ETH received.
     */
    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountETH);

    /**
     * @notice Swaps an exact amount of input tokens for as many output tokens as possible, along
     * with the route determined by the path.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction
     * not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The value at the last index of `amounts` (from `SomaSwapLibrary.getAmountsOut()`) must be greater than or equal to `amountOutMin`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Caller receives an exact amount of output tokens for as few input input tokens as possible, along
     * with the route determined by the path.
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The value of the first index of `amounts` (from `SomaSwapLibrary.getAmountsIn()`) must be less than or equal to `amountInMax`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Swaps an exact amount of ETH for as many output tokens as possible, along with the route
     * determined by the path.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The first element of `path` must be equal to the WETH address.
     * @custom:requirement The last element of `amounts` (from `SomaSwapLibrary.getAmountsOut()`) must be greater than or equal to `amount0Min`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)
        external
        payable
        returns (uint256[] memory amounts);

    /**
     * @notice Caller receives an exact amount of ETH for as few input tokens as possible, along with the route
     * determined by the path.
     * @param amountOut The amount of ETH to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The last element of `path` must be equal to the WETH address.
     * @custom:requirement The first element of `amounts` (from `SomaSwapLibrary.getAmountsIn()`) must be less than or equal to `amountInMax`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Swaps an exact amount of tokens for as much ETH as possible, along with the route determined
     * by the path.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The last element of `path` must be equal to the WETH address.
     * @custom:requirement The last element of `amounts` (from `SomaSwapLibrary.getAmountsOut()`) must be greater than or
     * equal to `amountOutMin`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Caller receives an exact amount of tokens for as little ETH as possible, along with the route determined
     * by the path.
     * @param amountOut The amount of tokens to receive.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The first element of `path` must be equal to the WETH address.
     * @custom:requirement The first element of `amounts` (from `SomaSwapLibrary.getAmountIn()`) must be less than or equal
     * to the `msg.value`.
     * @return amounts The input token amount and all subsequent output token amounts.
     */
    function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline)
        external
        payable
        returns (uint256[] memory amounts);

    /**
     * @notice Given some asset amount and reserves, returns the amount of the other asset representing equivalent value.
     * @param amountA The amount of token0.
     * @param reserveA The reserves of token0.
     * @param reserveB The reserves of token1.
     * @custom:requirement `amountA` must be greater than zero.
     * @custom:requirement `reserveA` must be greater than zero.
     * @custom:requirement `reserveB` must be greater than zero.
     * @return amountB The amount of token1.
     */
    function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB);

    /**
     * @notice Given some asset amount and reserves, returns the maximum output amount of the other asset (accounting for fees).
     * @param amountIn The amount of the input token.
     * @param reserveIn The reserves of the input token.
     * @param reserveOut The reserves of the output token.
     * @custom:requirement `amountIn` must be greater than zero.
     * @custom:requirement `reserveIn` must be greater than zero.
     * @custom:requirement `reserveOut` must be greater than zero.
     * @return amountOut The amount of the output token.
     */
    function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        external
        pure
        returns (uint256 amountOut);

    /**
     * @notice Returns the minimum input asset amount required to buy the given output asset amount (accounting for fees).
     * @param amountOut The amount of the output token.
     * @param reserveIn The reserves of the input token.
     * @param reserveOut The reserves of the output token.
     * @custom:requirement `amountOut` must be greater than zero.
     * @custom:requirement `reserveIn` must be greater than zero.
     * @custom:requirement `reserveOut` must be greater than zero.
     * @return amountIn The required input amount of the input asset.
     */
    function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
        external
        pure
        returns (uint256 amountIn);

    /**
     * @notice Given an input asset amount and an array of token addresses, calculates all subsequent maximum output token amounts
     * calling `getReserves()` for each pair of token addresses in the path in turn, and using these to call `getAmountOut()`.
     * @param amountIn The amount of the input token.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @custom:requirement `path` length must be greater than or equal to 2.
     * @return amounts The maximum output amounts.
     */
    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    /**
     * @notice Given an output asset amount and an array of token addresses, calculates all preceding minimum input token amounts
     * by calling `getReserves()` for each pair of token addresses in the path in turn, and using these to call `getAmountIn()`.
     * @param amountOut The amount of the output token.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @custom:requirement `path` length must be greater than or equal to 2.
     * @return amounts The required input amounts.
     */
    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    /**
     * @notice See {ISomaSwapRouter-removeLiquidityETH} - Identical but succeeds for tokens that take a fee on transfer.
     * @param token The pool token.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param amountTokenMin The minimum amount of token that must be received for the transaction not to revert.
     * @param amountETHMin The minimum amount of ETH that must be received for the transaction not to revert.
     * @param to Recipient of the underlying assets.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @custom:requirement There must be enough liquidity for both token amounts to be removed.
     * @return amountETH The amount of ETH received.
     */
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

    /**
     * @notice See {ISomaSwapRouter-removeLiquidityETHWithPermit} - Identical but succeeds for tokens that take a fee on transfer.
     * @param token The pool token.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param amountTokenMin The minimum amount of token that must be received for the transaction not to revert.
     * @param amountETHMin The minimum amount of ETH that must be received for the transaction not to revert.
     * @param to The recipient of the underlying assets.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @param approveMax Whether or not the approval amount in the signature is for liquidity or uint(-1).
     * @param v The v component of the permit signature.
     * @param r The r component of the permit signature.
     * @param s The s component of the permit signature.
     * @custom:requirement There must be enough liquidity for both token amounts to be removed.
     * @return amountETH The amount of ETH received.
     */
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountETH);

    /**
     * @notice See {ISomaSwapRouter-swapExactTokensForTokens} - Identical but succeeds for tokens that take a fee on transfer.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the underlying assets.
     * @param deadline The unix timestamp after which the transaction will revert.
     */
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    /**
     * @notice See {ISomaSwapRouter-swapExactETHForTokens} - Identical but succeeds for tokens that take a fee on transfer.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The first element of `path` must be equal to the WETH address.
     * @custom:requirement The increase in balance of the last element of `path` for the `to` address must be greater than
     * or equal to `amountOutMin`.
     */
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    /**
     * @notice See {ISomaSwapRouter-swapExactTokensForETH} - Identical but succeeds for tokens that take a fee on transfer.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The array of token addresses, where pools for each pair of addresses must exist and
     * have liquidity.
     * @param to The recipient of the output tokens.
     * @param deadline The unix timestamp after which the transaction will revert.
     * @custom:requirement The last element of `path` must be equal to the WETH address.
     * @custom:requirement The WETH balance of the router must be greater than or equal to `amountOutMin`.
     */
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}
ISomaSwapFactory.sol 131 lines
// SPDX-License-Identifier: MIT

pragma solidity =0.8.18;

/**
 * @title SOMA Swap Factory Contract.
 * @author SOMA.finance
 * @notice Interface for the {SomaSwapFactory} contract.
 */
interface ISomaSwapFactory {
    /**
     * @notice Emitted when a pair is created via `createPair()`.
     * @param token0 The address of token0.
     * @param token1 The address of token1.
     * @param pair The address of the created pair.
     */
    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);

    /**
     * @notice Emitted when the `feeTo` address is updated from `prevFeeTo` to `newFeeTo` by `sender`.
     * @param prevFeeTo The address of the previous fee to.
     * @param prevFeeTo The address of the new fee to.
     * @param sender The address of the message sender.
     */
    event FeeToUpdated(address indexed prevFeeTo, address indexed newFeeTo, address indexed sender);

    /**
     * @notice Emitted when a router is added by `sender`.
     * @param router The address of the router added.
     * @param sender The address of the message sender.
     */
    event RouterAdded(address indexed router, address indexed sender);

    /**
     * @notice Emitted when a router is removed by `sender`.
     * @param router The address of the router removed.
     * @param sender The address of the message sender.
     */
    event RouterRemoved(address indexed router, address indexed sender);

    /**
     * @notice Returns SOMA Swap Factory Create Pair Role.
     * @dev Returns `keccak256('SomaSwapFactory.CREATE_PAIR_ROLE')`.
     */
    function CREATE_PAIR_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns SOMA Swap Factory Fee Setter Role.
     * @dev Returns `keccak256('SomaSwapFactory.FEE_SETTER_ROLE')`.
     */
    function FEE_SETTER_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns SOMA Swap Factory Manage Router Role.
     * @dev Returns `keccak256('SomaSwapFactory.MANAGE_ROUTER_ROLE')`.
     */
    function MANAGE_ROUTER_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns the address where fees from the exchange get transferred to.
     */
    function feeTo() external view returns (address);

    /**
     * @notice Returns the address of the pair contract for tokenA and tokenB if it exists, else returns address(0).
     * @dev Returns the address of the pair for `tokenA` and `tokenB` if it exists, else returns `address(0)`.
     * @param tokenA The token0 of the pair.
     * @param tokenB The token1 of the pair.
     * @return pair The address of the pair.
     */
    function getPair(address tokenA, address tokenB) external view returns (address pair);

    /**
     * @notice Returns the nth pair created through the factory, or address(0).
     * @dev Returns the `n-th` pair (0 indexed) created through the factory, or `address(0)`.
     * @return pair The address of the pair.
     */
    function allPairs(uint256) external view returns (address pair);

    /**
     * @notice Returns the total number of pairs created through the factory so far.
     */
    function allPairsLength() external view returns (uint256);

    /**
     * @notice Returns True if an address is an existing router, else returns False.
     * @param target The address to return true if it an existing router, or false if it is not.
     * @return Boolean value indicating if the address is an existing router.
     */
    function isRouter(address target) external view returns (bool);

    /**
     * @notice Adds an address as a new router. A router is able to tell a pair who is swapping.
     * @param router The address to add as a new router.
     * @custom:emits RouterAdded
     * @custom:requirement The function caller must have the MANAGE_ROUTER_ROLE.
     */
    function addRouter(address router) external;

    /**
     * @notice Removes an address from the list of routers. A router is able to tell a pair who is swapping.
     * @param router The address to remove from the list of routers.
     * @custom:emits RouterRemoved
     * @custom:requirement The function caller must have the MANAGE_ROUTER_ROLE.
     */
    function removeRouter(address router) external;

    /**
     * @notice Creates a new pair.
     * @dev Creates a pair for `tokenA` and `tokenB` if one does not exist already.
     * @param tokenA The address of token0 of the pair.
     * @param tokenB The address of token1 of the pair.
     * @custom:emits PairCreated
     * @custom:requirement The function caller must have the CREATE_PAIR_ROLE.
     * @custom:requirement `tokenA` must not be equal to `tokenB`.
     * @custom:requirement `tokenA` must not be equal to `address(0)`.
     * @custom:requirement `tokenA` and `tokenB` must not be an existing pair.
     * @custom:requirement The system must not be paused.
     * @return pair The address of the pair created.
     */
    function createPair(address tokenA, address tokenB) external returns (address pair);

    /**
     * @notice Sets a new `feeTo` address.
     * @param _feeTo The new address to receive the protocol fees.
     * @custom:emits FeeToUpdated
     * @custom:requirement The function caller must have the FEE_SETTER_ROLE.
     * @custom:requirement The system must not be paused.
     */
    function setFeeTo(address _feeTo) external;
}
ITemplateFactory.sol 357 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/**
 * @title SOMA Template Factory Contract.
 * @author SOMA.finance.
 * @notice Interface of the {TemplateFactory} contract.
 */
interface ITemplateFactory {
    /**
     * @notice Emitted when a template version is created.
     * @param templateId The ID of the template added.
     * @param version The version of the template.
     * @param implementation The address of the implementation of the template.
     * @param sender The address of the message sender.
     */
    event TemplateVersionCreated(
        bytes32 indexed templateId, uint256 indexed version, address implementation, address indexed sender
    );

    /**
     * @notice Emitted when a deploy role is updated.
     * @param templateId The ID of the template with the updated deploy role.
     * @param prevRole The previous role.
     * @param newRole The new role.
     * @param sender The address of the message sender.
     */
    event DeployRoleUpdated(bytes32 indexed templateId, bytes32 prevRole, bytes32 newRole, address indexed sender);

    /**
     * @notice Emitted when a template is enabled.
     * @param templateId The ID of the template.
     * @param sender The address of the message sender.
     */
    event TemplateEnabled(bytes32 indexed templateId, address indexed sender);

    /**
     * @notice Emitted when a template is disabled.
     * @param templateId The ID of the template.
     * @param sender The address of the message sender.
     */
    event TemplateDisabled(bytes32 indexed templateId, address indexed sender);

    /**
     * @notice Emitted when a template version is deprecated.
     * @param templateId The ID of the template.
     * @param version The version of the template deprecated.
     * @param sender The address of the message sender.
     */
    event TemplateVersionDeprecated(bytes32 indexed templateId, uint256 indexed version, address indexed sender);

    /**
     * @notice Emitted when a template version is undeprecated.
     * @param templateId The ID of the template.
     * @param version The version of the template undeprecated.
     * @param sender The address of the message sender.
     */
    event TemplateVersionUndeprecated(bytes32 indexed templateId, uint256 indexed version, address indexed sender);

    /**
     * @notice Emitted when a template is deployed.
     * @param instance The instance of the deployed template.
     * @param templateId The ID of the template.
     * @param version The version of the template.
     * @param args The abi-encoded constructor arguments.
     * @param functionCalls The abi-encoded function calls.
     * @param sender The address of the message sender.
     */
    event TemplateDeployed(
        address indexed instance,
        bytes32 indexed templateId,
        uint256 version,
        bytes args,
        bytes[] functionCalls,
        address indexed sender
    );

    /**
     * @notice Emitted when a template is cloned.
     * @param instance The instance of the deployed template.
     * @param templateId The ID of the template.
     * @param version The version of the template.
     * @param functionCalls The abi-encoded function calls.
     * @param sender The address of the message sender.
     */
    event TemplateCloned(
        address indexed instance,
        bytes32 indexed templateId,
        uint256 version,
        bytes[] functionCalls,
        address indexed sender
    );

    /**
     * @notice Emitted when a template function is called.
     * @param target The address of the target contract.
     * @param data The abi-encoded data.
     * @param result The abi-encoded result.
     * @param sender The address of the message sender.
     */
    event FunctionCalled(address indexed target, bytes data, bytes result, address indexed sender);

    /**
     * @notice Structure of a template version.
     * @param exists True if the version exists, False if it does not.
     * @param deprecated True if the version is deprecated, False if it is not.
     * @param implementation The address of the version's implementation.
     * @param creationCode The abi-encoded creation code.
     * @param totalParts The total number of parts of the version.
     * @param partsUploaded The number of parts uploaded.
     * @param instances The array of instances.
     */
    struct Version {
        bool deprecated;
        address implementation;
        bytes creationCode;
        uint256 totalParts;
        uint256 partsUploaded;
        address[] instances;
    }

    /**
     * @notice Structure of a template.
     * @param disabled Boolean value indicating if the template is enabled.
     * @param latestVersion The latest version of the template.
     * @param deployRole The deployer role of the template.
     * @param version The versions of the template.
     * @param instances The instances of the template.
     */
    struct Template {
        bool disabled;
        bytes32 deployRole;
        Version[] versions;
        address[] instances;
    }

    /**
     * @notice Structure of deployment information.
     * @param exists Boolean value indicating if the deployment information exists.
     * @param templateId The id of the template.
     * @param version The version of the template.
     * @param args The abi-encoded arguments.
     * @param functionCalls The abi-encoded function calls.
     * @param cloned Boolean indicating if the deployment information is cloned.
     */
    struct DeploymentInfo {
        bool exists;
        uint64 block;
        uint64 timestamp;
        address sender;
        bytes32 templateId;
        uint256 version;
        bytes args;
        bytes[] functionCalls;
        bool cloned;
    }

    /**
     * @notice Returns a version of a template.
     * @param templateId The id of the template to return the version of.
     * @param _version The version of the template to be returned.
     * @return The version of the template.
     */
    function version(bytes32 templateId, uint256 _version) external view returns (Version memory);

    /**
     * @notice Returns the latest version of a template.
     * @param templateId The id of the template to return the latest version of.
     * @return The latest version of the template.
     */
    function latestVersion(bytes32 templateId) external view returns (uint256);

    /**
     * @notice Returns the instances of a template.
     * @param templateId The id of the template to return the latest instance of.
     * @return The instances of the template.
     */
    function templateInstances(bytes32 templateId) external view returns (address[] memory);

    /**
     * @notice Returns the deployment information of an instance.
     * @param instance The instance of the template to return deployment information of.
     * @return The deployment information of the template.
     */
    function deploymentInfo(address instance) external view returns (DeploymentInfo memory);

    /**
     * @notice Returns the deploy role of a template.
     * @param templateId The id of the template to return the deploy role of.
     * @return The deploy role of the template.
     */
    function deployRole(bytes32 templateId) external view returns (bytes32);

    /**
     * @notice Returns True if an instance has been deployed by the template factory, else returns False.
     * @dev Returns `true` if `instance` has been deployed by the template factory, else returns `false`.
     * @param instance The instance of the template to return True for, if it has been deployed by the factory, else False.
     * @return Boolean value indicating if the instance has been deployed by the template factory.
     */
    function deployedByFactory(address instance) external view returns (bool);

    /**
     * @notice Uploads a new template and returns True.
     * @param templateId The id of the template to upload.
     * @param initialPart The initial part to upload.
     * @param totalParts The number of total parts of the template.
     * @param implementation The address of the implementation of the template.
     * @custom:emits TemplateVersionCreated
     * @custom:requirement `templateId` must not be equal to bytes32(0).
     * @custom:requirement The length of `initialPart` must be greater than zero.
     * @custom:requirement `totalParts` must be greater than zero.
     */
    function uploadTemplate(bytes32 templateId, bytes memory initialPart, uint256 totalParts, address implementation)
        external
        returns (bool);

    /**
     * @notice Uploads a part of a template.
     * @param templateId The id of the template to upload a part to.
     * @param version The version of the template to upload a part to.
     * @param part The part to upload to the template.
     * @custom:requirement The length of part must be greater than zero.
     * @custom:requirement The version of the template must already exist.
     * @custom:requirement The version's number of parts uploaded must be less than the version's total number of parts.
     * @return Boolean value indicating if the operation was successful.
     */
    function uploadTemplatePart(bytes32 templateId, uint256 version, bytes memory part) external returns (bool);

    /**
     * @notice Updates the deploy role of a template.
     * @param templateId The id of the template to update the deploy role for.
     * @param _deployRole The deploy role to update to.
     * @custom:emits DeployRoleUpdated
     * @custom:requirement The template's existing deploy role cannot be equal to `deployRole`.
     * @return Boolean value indicating if the operation was successful.
     */
    function updateDeployRole(bytes32 templateId, bytes32 _deployRole) external returns (bool);

    /**
     * @notice Disables a template and returns True.
     * @dev Disables a template and returns `true`.
     * @param templateId The id of the template to disable.
     * @custom:emits TemplateDisabled
     * @custom:requirement The template must be enabled when the function call is made.
     * @return Boolean value indicating if the operation was successful.
     */
    function disableTemplate(bytes32 templateId) external returns (bool);

    /**
     * @notice Enables a template and returns True.
     * @dev Enables a template and returns `true`.
     * @param templateId The id of the template to enable.
     * @custom:emits TemplateEnabled
     * @custom:requirement The template must be disabled when the function call is made.
     * @return Boolean value indicating if the operation was successful.
     */
    function enableTemplate(bytes32 templateId) external returns (bool);

    /**
     * @notice Deprecates a version of a template. A deprecated template version cannot be deployed.
     * @param templateId The id of the template to deprecate the version for.
     * @param _version The version of the template to deprecate.
     * @custom:emits TemplateVersionDeprecated
     * @custom:requirement The version must already exist.
     * @custom:requirement The version must not be deprecated already.
     * @return Boolean value indicating if the operation was successful.
     */
    function deprecateVersion(bytes32 templateId, uint256 _version) external returns (bool);

    /**
     * @notice Undeprecates a version of a template and returns True.
     * @param templateId The id of the template to undeprecate a version for.
     * @param _version The version of a template to undeprecate.
     * @custom:emits TemplateVersionUndeprecated
     * @custom:requirement The version must be deprecated already.
     * @return Boolean value indicating if the operation was successful.
     */
    function undeprecateVersion(bytes32 templateId, uint256 _version) external returns (bool);

    /**
     * @notice Returns the Init Code Hash.
     * @dev Returns the keccak256 hash of `templateId`, `version` and `args`.
     * @param templateId The id of the template to return the init code hash of.
     * @param _version The version of the template to return the init code hash of.
     * @param args The abi-encoded constructor arguments.
     * @return The abi-encoded init code hash.
     */
    function initCodeHash(bytes32 templateId, uint256 _version, bytes memory args) external view returns (bytes32);

    /**
     * @notice Overloaded predictDeployAddress function.
     * @dev See {ITemplateFactory-predictDeployAddress}.
     * @param templateId The id of the template to predict the deploy address for.
     * @param _version The version of the template to predict the deploy address for.
     * @param args The abi-encoded constructor arguments.
     * @param salt The unique hash ot identify the contract.
     */
    function predictDeployAddress(bytes32 templateId, uint256 _version, bytes memory args, bytes32 salt)
        external
        view
        returns (address);

    /**
     * @notice Predict the clone address.
     * @param templateId The id of the template to predict the clone address for.
     * @param _version The version of the template to predict the clone address for.
     * @param salt The unique hash ot identify the contract.
     * @return The predicted clone address.
     */
    function predictCloneAddress(bytes32 templateId, uint256 _version, bytes32 salt) external view returns (address);

    /**
     * @notice Deploys a version of a template.
     * @param templateId The id of the template to deploy.
     * @param _version The version of the template to deploy.
     * @param args The abi-encoded constructor arguments.
     * @param functionCalls The abi-encoded function calls.
     * @param salt The unique hash to identify the contract.
     * @custom:emits TemplateDeployed
     * @custom:requirement The version's number of parts must be equal to the version's number of parts uploaded.
     * @custom:requirement The length of the version's creation code must be greater than zero.
     * @return instance The instance of the deployed template.
     */
    function deployTemplate(
        bytes32 templateId,
        uint256 _version,
        bytes memory args,
        bytes[] memory functionCalls,
        bytes32 salt
    ) external returns (address instance);

    /**
     * @notice Clones a version of a template.
     * @param templateId The id of the template to clone.
     * @param _version The version of the template to clone.
     * @param functionCalls The abi-encoded function calls.
     * @param salt The unique hash to identify the contract.
     * @custom:emits TemplateCloned
     * @custom:requirement The version's implementation must not equal `address(0)`.
     * @return instance The address of the cloned template instance.
     */
    function cloneTemplate(bytes32 templateId, uint256 _version, bytes[] memory functionCalls, bytes32 salt)
        external
        returns (address instance);

    /**
     * @notice Calls a function on the target contract.
     * @param target The target address of the function call.
     * @param data Miscalaneous data associated with the transfer.
     * @custom:emits FunctionCalled
     * @return result The result of the function call.
     */
    function functionCall(address target, bytes memory data) external returns (bytes memory result);
}
ILockdropFactory.sol 38 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "./ILockdrop.sol";

/**
 * @title SOMA Lockdrop Factory Contract.
 * @author SOMA.finance.
 * @notice A factory that produces Lockdrop contracts.
 */
interface ILockdropFactory {
    /**
     * @notice Emitted when a Lockdrop is created.
     * @param id The ID of the Lockdrop.
     * @param asset The delegation asset of the Lockdrop.
     * @param instance The address of the created Lockdrop.
     */
    event LockdropCreated(uint256 id, address asset, address instance);

    /**
     * @notice The Lockdrop's CREATE_ROLE.
     * @dev Returns keccak256('Lockdrop.CREATE_ROLE').
     */
    function CREATE_ROLE() external pure returns (bytes32);

    /**
     * @notice Creates a Lockdrop instance.
     * @param asset The address of the delegation asset.
     * @param withdrawTo The address that delegated assets will be withdrawn to.
     * @param dateConfig The date configuration of the Lockdrop.
     * @custom:emits LockdropCreated
     * @custom:requirement `asset` must not be equal to address zero.
     * @custom:requirement `withdrawTo` must not be equal to address zero.
     * @custom:requirement The function caller must have the CREATE_ROLE.
     */
    function create(address asset, address withdrawTo, ILockdrop.DateConfig calldata dateConfig) external;
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
ILockdrop.sol 271 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Lockdrop Contract.
 * @author SOMA.finance
 * @notice A fund raising contract for bootstrapping DEX liquidity pools.
 */
interface ILockdrop {
    /**
     * @notice Emitted when the {DelegationConfig} is updated.
     * @param poolId The pool ID.
     * @param prevLockDuration The previous lock duration.
     * @param newLockDuration The new lock duration.
     * @param sender The message sender that triggered the event.
     */
    event LockDurationUpdated(
        bytes32 poolId, uint256 prevLockDuration, uint256 newLockDuration, address indexed sender
    );

    /**
     * @notice Emitted when the {withdrawTo} address is updated.
     * @param prevTo The previous withdraw to address.
     * @param newTo The new withdraw to address.
     * @param sender The message sender that triggered the event.
     */
    event WithdrawToUpdated(address prevTo, address newTo, address indexed sender);

    /**
     * @notice Emitted when a delegation is added to a pool.
     * @param poolId The pool ID.
     * @param amount The delegation amount denominated in the delegation asset.
     * @param lockDuration The lock duration.
     * @param sender The message sender that triggered the event.
     */
    event DelegationAdded(bytes32 indexed poolId, uint256 amount, uint256 lockDuration, address indexed sender);

    /**
     * @notice Emitted when someone calls {moveDelegation}, transferring their delegation to a different pool.
     * @param fromPoolId The pool ID of the source pool.
     * @param toPoolId The pool ID of the destination pool.
     * @param amount The amount of the delegation asset to move.
     * @param sender TThe message sender that triggered the event.
     */
    event DelegationMoved(bytes32 indexed fromPoolId, bytes32 indexed toPoolId, uint256 amount, address indexed sender);

    /**
     * @notice Emitted when the {DateConfig} is updated.
     * @param prevDateConfig The previous date configuration.
     * @param newDateConfig The new date configuration.
     * @param sender The message sender that triggered the event.
     */
    event DatesUpdated(DateConfig prevDateConfig, DateConfig newDateConfig, address indexed sender);

    /**
     * @notice Emitted when the {Pool} is updated.
     * @param poolId The pool ID.
     * @param requiredPrivileges The new required privileges.
     * @param enabled Boolean indicating if the pool is enabled.
     * @param sender The message sender that triggered the event.
     */
    event PoolUpdated(bytes32 indexed poolId, bytes32 requiredPrivileges, bool enabled, address indexed sender);

    /**
     * @notice Emitted when a delegation is removed from a pool.
     * @param poolId The pool ID.
     * @param amount The removed delegation amount denominated in the delegation asset.
     * @param sender The message sender that triggered the event.
     */
    event DelegationRemoved(bytes32 indexed poolId, uint256 amount, address indexed sender);

    /**
     * @notice Date Configuration structure.
     * @param startDate The unix timestamp marking the start of the lockdrop.
     * @param removeDelegationEnd The unix timestamp marking the end of delegation removals.
     * @param endDate The unix timestamp marking the end of the lockdrop.
     */
    struct DateConfig {
        uint48 startDate;
        uint48 removeDelegationEnd;
        uint48 endDate;
    }

    /**
     * @notice Pool structure. Each pool will bootstrap liquidity for an upcoming DEX pair.
     * E.g: sTSLA/USDC
     * @param enabled Boolean indicating if the pool is enabled.
     * @param requiredPrivileges The required privileges of the pool.
     * @param balances The mapping of user addresses to delegation balances.
     */
    struct Pool {
        bool enabled;
        bytes32 requiredPrivileges;
        mapping(address => uint256) balances;
    }

    /**
     * @notice Returns the Lockdrop Global Admin Role.
     * @dev Equivalent to `keccak256('Lockdrop.GLOBAL_ADMIN_ROLE')`.
     */
    function GLOBAL_ADMIN_ROLE() external pure returns (bytes32);

    /**
     * @notice Returns the Lockdrop Local Admin Role.
     * @dev Equivalent to `keccak256(abi.encodePacked(address(this), GLOBAL_ADMIN_ROLE))`.
     */
    function LOCAL_ADMIN_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the ID of the Lockdrop.
     */
    function id() external view returns (uint256);

    /**
     * @notice The address of the Lockdrop's delegation asset.
     */
    function asset() external view returns (address);

    /**
     * @notice The date configuration of the Lockdrop.
     */
    function dateConfig() external view returns (DateConfig memory);

    /**
     * @notice The address where the delegated funds will be withdrawn to.
     */
    function withdrawTo() external view returns (address);

    /**
     * @notice Initialize function for the Lockdrop contract.
     * @param _id The ID of the Lockdrop.
     * @param _asset The address of the delegation asset.
     * @param _withdrawTo The address that receives withdrawn assets.
     * @param _initDateConfig The initial date configuration.
     */
    function initialize(uint256 _id, address _asset, address _withdrawTo, DateConfig calldata _initDateConfig)
        external;

    /**
     * @notice Updates the Lockdrop's date configuration.
     * @param newConfig The updated date configuration.
     * @custom:emits DatesUpdated
     * @custom:requirement The function caller must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function updateDateConfig(DateConfig calldata newConfig) external;

    /**
     * @notice Sets the `withdrawTo` address.
     * @param account The updated address to receive withdrawn funds.
     * @custom:emits WithdrawToUpdated
     * @custom:requirement The function caller must be the master.
     * @custom:requirement `account` must not be equal to address zero.
     */
    function setWithdrawTo(address account) external;

    /**
     * @notice Returns the delegation balance of an account, given a pool ID.
     * @param poolId The pool ID to return the account's balance of.
     * @param account The account to return the balance of.
     * @return The delegation balance of `account` for the `poolId` pool.
     */
    function balanceOf(bytes32 poolId, address account) external view returns (uint256);

    /**
     * @notice Returns the lock duration of an account for a pool.
     * @param poolId The poolId to return the user lock duration of.
     * @param account The account to return the pool lock duration of.
     * @return The lock duration.
     */
    function lockDuration(bytes32 poolId, address account) external view returns (uint256);

    /**
     * @notice Returns a boolean indicating if a pool is enabled.
     * @param poolId The pool ID to check the enabled status of.
     * @return True if the pool is enabled, False if the pool is disabled.
     */
    function enabled(bytes32 poolId) external view returns (bool);

    /**
     * @notice Returns the required privileges of the pool. These privileges are required in order to
     * delegate.
     * @param poolId The pool ID to check the enabled status of.
     * @custom:requirement The pool must be enabled.
     * @return The required privileges of the pool.
     */
    function requiredPrivileges(bytes32 poolId) external view returns (bytes32);

    /**
     * @notice Updates the lockdrop pool parameters.
     * @param _poolId The ID of the pool to update.
     * @param _requiredPrivileges The updated required privileges of the pool.
     * @param _enabled The updated enabled or disabled state of the pool.
     * @custom:emits PoolUpdated
     * @custom:requirement The function caller must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function updatePool(bytes32 _poolId, bytes32 _requiredPrivileges, bool _enabled) external;

    /**
     * @notice Updates the delegation configuration of an account.
     * @param poolId The pool ID.
     * @param newLockDuration The new lock duration.
     * @custom:emits LockDurationUpdated
     * @custom:requirement The `poolId` pool must be enabled.
     * @custom:requirement The lockdrop must have started already.
     * @custom:requirement The lockdrop must not have ended yet.
     * @custom:requirement The contracts must not be paused.
     */
    function updateLockDuration(bytes32 poolId, uint256 newLockDuration) external;

    /**
     * @notice Withdraws tokens from the Lockdrop contract to the `withdrawTo` address.
     * @param amount The amount of tokens to be withdrawn.
     * @custom:requirement The function caller must have the GLOBAL_ADMIN_ROLE or LOCAL_ADMIN_ROLE.
     */
    function withdraw(uint256 amount) external;

    /**
     * @notice Moves the accounts' delegated tokens from one pool to another, and sets the `toPoolId` lock duration.
     * @param fromPoolId The ID of the pool that the delegation will be moved from.
     * @param toPoolId The ID of the pool that the delegation will be moved to.
     * @param amount The amount of tokens to be moved.
     * @param toPoolLockDuration The lock duration of the user for `toPoolId`.
     * @custom:emits DelegationMoved
     * @custom:requirement `fromPoolId` must not be equal to `toPoolId`.
     * @custom:requirement The lockdrop must have started already.
     * @custom:requirement The lockdrop must not have ended yet.
     * @custom:requirement `amount` must be greater than zero.
     * @custom:requirement The `fromPoolId` pool must be enabled.
     * @custom:requirement The `toPoolId` pool must be enabled.
     * @custom:requirement The delegation balance of the caller for the `fromPoolId` pool must be greater than
     * or equal to `amount`.
     * @custom:requirement The function caller must have the required privileges of the `fromPoolId` pool.
     * @custom:requirement The function caller must have the required privileges of the `toPoolId` pool.
     * @custom:requirement The contracts must not be paused.
     */
    function moveDelegation(bytes32 fromPoolId, bytes32 toPoolId, uint256 amount, uint256 toPoolLockDuration)
        external;

    /**
     * @notice Removes the accounts' delegated tokens from the pool.
     * @param poolId The ID of the pool that the delegation will be removed from.
     * @param amount The amount of tokens to be removed.
     * @custom:emits DelegationRemoved
     * @custom:requirement The lockdrop must have started already.
     * @custom:requirement The block timestamp must be less than or equal to the remove delegation end timestamp.
     * @custom:requirement `amount` must be greater than zero.
     * @custom:requirement `amount` must be less than the balance of the asset.
     * @custom:requirement The `poolId` pool must be enabled.
     * @custom:requirement The delegation balance of the caller for the `poolId` pool must be greater than
     * or equal to `amount`.
     * @custom:requirement The function caller must have the required privileges of the `poolId` pool.
     * @custom:requirement The contracts must not be paused.
     */
    function removeDelegation(bytes32 poolId, uint256 amount) external;

    /**
     * @notice Delegates tokens to the a specific pool.
     * @param poolId The ID of the pool to receive the delegation.
     * @param amount The amount of tokens to be delegated.
     * @param lockDuration_ The lock duration.
     * @custom:emits DelegationAdded
     * @custom:requirement `amount` must be greater than zero.
     * @custom:requirement The `poolId` pool must be enabled.
     * @custom:requirement The lockdrop must have started already.
     * @custom:requirement The lockdrop must not have ended yet.
     * @custom:requirement The function caller must have the `poolId` pool's required privileges.
     * @custom:requirement The contracts must not be paused.
     */
    function delegate(bytes32 poolId, uint256 amount, uint256 lockDuration_) external;
}
ITokenRecoveryUpgradeable.sol 36 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/**
 * @title SOMA Token Rewards Upgradeable Contract.
 * @author SOMA.finance
 * @notice Interface for the {TokenRecoveryUpgradeable} contract.
 */
interface ITokenRecoveryUpgradeable {
    /**
     * @notice Emitted when tokens are recovered.
     * @param token The address of the recovered tokens.
     * @param to The address that the tokens are being sent to.
     * @param amount The amount of tokens recovered.
     * @param sender The address of the message sender.
     */
    event TokensRecovered(address indexed token, address indexed to, uint256 amount, address indexed sender);

    /**
     * @notice Returns the Token Recovery Upgradeable TOKEN_RECOVERY_ROLE.
     * @dev Returns keccak256('TokenRecovery.TOKEN_RECOVERY_ROLE').
     */
    function TOKEN_RECOVERY_ROLE() external pure returns (bytes32);

    /**
     * @notice Recovers tokens and transfers these tokens to `to`.
     * @param token The address of the recovered tokens.
     * @param to The address that the tokens are being sent to.
     * @param amount The amount of tokens recovered.
     * @custom:emits TokensRecovered
     * @custom:requirement The function caller must have the TOKEN_RECOVERY_ROLE.
     * @custom:requirement `token` must not be a disabled token.
     */
    function recoverTokens(address token, address to, uint256 amount) external;
}

Read Contract

DEFAULT_PRIVILEGES 0x2a7a0c68 → bytes32
GLOBAL_ADMIN_ROLE 0x66258068 → bytes32
GLOBAL_SEIZE_ROLE 0x30139280 → bytes32
GLOBAL_UPDATE_PRIVILEGES_ROLE 0x227102a6 → bytes32
LOCAL_ADMIN_ROLE 0xe2481dbd → bytes32
LOCAL_SEIZE_ROLE 0xc72460ee → bytes32
LOCAL_UPDATE_PRIVILEGES_ROLE 0xeec47d60 → bytes32
SOMA 0x63727fc2 → address
TOKEN_RECOVERY_ROLE 0xd8697598 → bytes32
adminClaimable 0x4e6e056f → uint256
claimRequest 0x3a22502c → tuple
claimable 0xd4570c1c → uint256
config 0x79502c55 → tuple
currentTPS 0x3e677297 → uint256
debt 0xd449300d → uint256
getRoleAdmin 0x248a9ca3 → bytes32
hasPrivileges 0x4b63885a → bool
hasRole 0x91d14854 → bool
paused 0x5c975abb → bool
pendingStrategy 0xa7c5b413 → tuple
requiredPrivileges 0x1e26571f → bytes32
rewardToken 0x509b6c3f → address
stakeOf 0x42623360 → uint256
stakingToken 0x72f702f3 → address
strategy 0xbc88d7e4 → tuple
supportsInterface 0x01ffc9a7 → bool
totalPendingStrategies 0x3cfe9fd1 → uint256
totalPendingUnstake 0x3d173136 → uint256
totalRewardTokens 0x1b0875a6 → uint256
totalStaked 0x817b1cd2 → uint256
totalStrategies 0xf96d7b80 → uint256
tps 0x67b4cdb8 → uint256
unstakeRequest 0xe5261b1d → tuple

Write Contract 20 functions

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

addRewardToken 0x1c03e6cc
address _asset
adminClaim 0x350b5af0
address _asset
address _to
cancelClaimRequests 0x1d8b4c49
address[] _assets
uint256[][] _ids
cancelUnstakeRequests 0x8b0bd55b
uint256[] _ids
claim 0x90672ad8
address[] _assets
uint256[][] _ids
claimImmediate 0x6fa3fbda
address[] _assets
uint256[] _amounts
createClaimRequests 0xa488b71e
address[] _assets
returns: uint256[]
createStrategy 0x4f814cc5
uint256 _startDate
uint256 _endDate
address _rewardToken
uint256 _rewardAmount
createUnstakeRequest 0xbe864d68
uint256 _amount
returns: uint256
initialize 0x946d9204
address stakingToken_
address[] rewardTokens_
multicall 0xac9650d8
bytes[] data
returns: bytes[]
pause 0x8456cb59
No parameters
recoverTokens 0x5f3e849f
address token
address to
uint256 amount
seize 0xfb3ee571
address from
stake 0xa694fc3a
uint256 _amount
unpause 0x3f4ba83a
No parameters
unstake 0xe449f341
uint256[] _ids
unstakeImmediate 0x3ee16bf7
uint256 _amount
updateConfig 0x49aeb46f
uint64 _unstakeDuration
uint64 _claimDuration
uint16 _earlyUnstakeFee
uint16 _earlyClaimFee
updateRequiredPrivileges 0x2772ce9d
bytes32 newRequiredPrivileges
returns: bool

Recent Transactions

No transactions found for this address