Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xDba68f07d1b7Ca219f78ae8582C213d975c25cAf
Balance 0 ETH
Nonce 1
Code Size 15648 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

15648 bytes
0x6080604052600436106102665760003560e01c806397988dce11610144578063cf0d5af3116100b6578063e04ab1391161007a578063e04ab13914610732578063e091dd1a14610752578063e52c4b7a14610767578063f19451d814610787578063f2fde38b1461079c578063fc0633d0146107bc57610266565b8063cf0d5af314610668578063cf6dde4a1461069c578063d060e175146106bc578063d323cdbf146106dc578063d68f4dd1146106fc57610266565b8063b2fb30cb11610108578063b2fb30cb146105b3578063b4540fa7146105d3578063bb5ee001146105f3578063c368803a14610613578063c8b0cf6814610633578063ca5cc0c21461064857610266565b806397988dce1461052b5780639ecd74721461054b578063a34df14f14610560578063a6dcb8de14610573578063b1d7655c1461059357610266565b80635a5b8d9e116101dd578063783451e8116101a1578063783451e81461046f57806386de2fcb146104845780638b7b23ee146104a45780638ba74f17146104c95780638da5cb5b146104e9578063903df8061461050b57610266565b80635a5b8d9e146103da5780636588fc03146103fa578063675187a31461041a578063715018a61461043a578063741af17e1461044f57610266565b80631d7065db1161022f5780631d7065db1461030d57806323cf31181461033a5780633717dee71461035a5780633e54bacb1461037a578063441a3e701461039a5780635a04fb69146103ba57610266565b8062623ae31461026b57806303e1cdf4146102965780630cdebc9e146102ab57806313ef2b1b146102cb5780631c30ffb1146102ed575b600080fd5b34801561027757600080fd5b506102806107dc565b60405161028d9190613b69565b60405180910390f35b3480156102a257600080fd5b506102806107ed565b3480156102b757600080fd5b506102806102c63660046134ba565b6107f9565b3480156102d757600080fd5b506102eb6102e636600461340a565b6108ab565b005b3480156102f957600080fd5b506102806103083660046133ca565b610f2d565b34801561031957600080fd5b5061032d610328366004613376565b610f8d565b60405161028d9190613778565b34801561034657600080fd5b506102eb610355366004613376565b610f9a565b34801561036657600080fd5b506102eb6103753660046135df565b610ffb565b34801561038657600080fd5b506102eb6103953660046135df565b6112fc565b3480156103a657600080fd5b506102eb6103b53660046135df565b6115f1565b3480156103c657600080fd5b506102eb6103d53660046135bb565b61189e565b3480156103e657600080fd5b506102eb6103f536600461358b565b611c27565b34801561040657600080fd5b506102eb6104153660046135df565b611cb8565b34801561042657600080fd5b5061032d610435366004613376565b611fab565b34801561044657600080fd5b506102eb61201e565b34801561045b57600080fd5b506102eb61046a366004613376565b6120a7565b34801561047b57600080fd5b50610280612108565b34801561049057600080fd5b506102eb61049f36600461348d565b612114565b3480156104b057600080fd5b506104b961217b565b60405161028d9493929190613c07565b3480156104d557600080fd5b506102806104e43660046134ba565b612197565b3480156104f557600080fd5b506104fe6121e2565b60405161028d9190613682565b34801561051757600080fd5b506104fe6105263660046134ba565b6121f1565b34801561053757600080fd5b506104fe61054636600461358b565b612213565b34801561055757600080fd5b506104fe612220565b6102eb61056e366004613376565b61222f565b34801561057f57600080fd5b5061028061058e366004613376565b6122f8565b34801561059f57600080fd5b506104fe6105ae36600461358b565b612319565b3480156105bf57600080fd5b506102eb6105ce3660046135df565b612326565b3480156105df57600080fd5b506102eb6105ee36600461348d565b61259a565b3480156105ff57600080fd5b5061028061060e366004613392565b612663565b34801561061f57600080fd5b5061028061062e366004613376565b612692565b34801561063f57600080fd5b506104fe6126a4565b34801561065457600080fd5b506102806106633660046134ba565b6126b3565b34801561067457600080fd5b5061068861068336600461358b565b612759565b60405161028d9897969594939291906136d3565b3480156106a857600080fd5b5061032d6106b7366004613376565b6127ab565b3480156106c857600080fd5b506102806106d7366004613376565b6127b8565b3480156106e857600080fd5b506102806106f736600461358b565b6127d3565b34801561070857600080fd5b5061071c61071736600461358b565b6128ca565b60405161028d9a99989796959493929190613ba6565b34801561073e57600080fd5b5061028061074d36600461358b565b612a83565b34801561075e57600080fd5b50610280612b2a565b34801561077357600080fd5b506104fe61078236600461358b565b612b30565b34801561079357600080fd5b50610280612b3d565b3480156107a857600080fd5b506102eb6107b7366004613376565b612b43565b3480156107c857600080fd5b506102eb6107d7366004613600565b612c03565b60006107e8600c612c7c565b905090565b60006107e8600a612c7c565b600080836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016108289190613682565b60206040518083038186803b15801561084057600080fd5b505afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906135a3565b6001600160a01b0385166000908152600960205260409020549091506108a19084908390612c87565b9150505b92915050565b600260015414156108d75760405162461bcd60e51b81526004016108ce90613b32565b60405180910390fd5b6002600155806108f95760405162461bcd60e51b81526004016108ce906139ba565b6014546001600160a01b03161561096b57601454604051633c6202c960e21b81526001600160a01b039091169063f1880b249061093a908690600401613682565b60006040518083038186803b15801561095257600080fd5b505afa158015610966573d6000803e3d6000fd5b505050505b6000805b828110156109c05783838281811061099757634e487b7160e01b600052603260045260246000fd5b905060a0020160200135826109ac9190613c44565b9150806109b881613c93565b91505061096f565b506040516370a0823160e01b81526000906001600160a01b038616906370a08231906109f0903090600401613682565b60206040518083038186803b158015610a0857600080fd5b505afa158015610a1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4091906135a3565b9050610a4e85333085612d36565b600081866001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610a7d9190613682565b60206040518083038186803b158015610a9557600080fd5b505afa158015610aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610acd91906135a3565b610ad79190613c7c565b9050610ae4600a87612e26565b610b25576000610afc82600e60000154612710612c87565b601054909150610b179088906001600160a01b031683612e3b565b610b218183613c7c565b9150505b6000805b85811015610f1f576000878783818110610b5357634e487b7160e01b600052603260045260246000fd5b905060a00201803603810190610b699190613501565b90508060600151816040015110610b925760405162461bcd60e51b81526004016108ce9061399a565b6402540be400816060015110610bba5760405162461bcd60e51b81526004016108ce906137c5565b60065481602001511015610be05760405162461bcd60e51b81526004016108ce90613940565b6000610bf182602001518689612c87565b6001600160a01b038b16600090815260096020526040902054909150610c1957809350610c4f565b6001600160a01b038a16600090815260096020526040902054610c4c9082908815610c445788610c47565b60015b612c87565b93505b60008411610c6f5760405162461bcd60e51b81526004016108ce90613a87565b6001600160a01b038a1660009081526009602052604081208054869290610c97908490613c44565b90915550610ca790508187613c44565b9550610cb1613316565b6001600160a01b03808c16825260208201869052604084015160608084019190915284015160808084019190915260055460a08401528451821660c08401528401511615610d835782608001516001600160a01b031663f968f4936040518163ffffffff1660e01b815260040160206040518083038186803b158015610d3657600080fd5b505afa158015610d4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6e91906134e5565b5060808301516001600160a01b031660e08201525b60058054600090815260046020818152604092839020855181546001600160a01b039182166001600160a01b0319918216178355928701516001830155938601516002808301919091556060870151600383015560808701519382019390935560a08601519481019490945560c085015160068501805491851691831691909117905560e085015160079094018054949093169316929092179055610e28908c612f28565b506001600160a01b03808c166000908152600760209081526040808320600554815460018101835591855283852090910155865190931682526008905220610e70818d612f28565b506001600160a01b038c1660009081526002820160209081526040822060058054825460018101845592855292842090910191909155805491610eb283613c93565b91905055507f4d0f9887048089b093c92bec885ab529000723bce1a79f4e81a5990619910b968260a001518d8460c001518686606001518760800151604051610f0096959493929190613b72565b60405180910390a1505050508080610f1790613c93565b915050610b29565b505060018055505050505050565b6001600160a01b038084166000908152600860209081526040808320938616835260029093019052908120805483908110610f7857634e487b7160e01b600052603260045260246000fd5b906000526020600020015490505b9392505050565b60006108a5600a83612e26565b610fa2612f3d565b6001600160a01b0316610fb36121e2565b6001600160a01b031614610fd95760405162461bcd60e51b81526004016108ce90613965565b601380546001600160a01b0319166001600160a01b0392909216919091179055565b6002600154141561101e5760405162461bcd60e51b81526004016108ce90613b32565b600260015560008281526004602052604090206006548210156110535760405162461bcd60e51b81526004016108ce90613940565b80546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611083903090600401613682565b60206040518083038186803b15801561109b57600080fd5b505afa1580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d391906135a3565b82549091506110ed906001600160a01b0316333086612d36565b81546040516370a0823160e01b815260009183916001600160a01b03909116906370a0823190611121903090600401613682565b60206040518083038186803b15801561113957600080fd5b505afa15801561114d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117191906135a3565b61117b9190613c7c565b835490915061119590600a906001600160a01b0316612e26565b6111da5760006111ad82600e60000154612710612c87565b84546010549192506111cc916001600160a01b03918216911683612e3b565b6111d68183613c7c565b9150505b82546001600160a01b03166000908152600960205260408120546111ff575080611228565b83546001600160a01b031660009081526009602052604090205461122590839085612c87565b90505b600081116112485760405162461bcd60e51b81526004016108ce90613a87565b83546001600160a01b031660009081526009602052604081208054839290611271908490613c44565b925050819055508084600101600082825461128c9190613c44565b9091555050600584015484546006860154600387015460048801546040517f4d0f9887048089b093c92bec885ab529000723bce1a79f4e81a5990619910b96956112e89590946001600160a01b03918216949116928992613b72565b60405180910390a150506001805550505050565b6002600154141561131f5760405162461bcd60e51b81526004016108ce90613b32565b60026001556013546001600160a01b031661134c5760405162461bcd60e51b81526004016108ce906138e2565b600082815260046020526040902060068101546001600160a01b031633146113865760405162461bcd60e51b81526004016108ce90613903565b60008160020154826001015461139c9190613c7c565b9050600081116113be5760405162461bcd60e51b81526004016108ce90613827565b81546040516370a0823160e01b81526000916001600160a01b0316906370a08231906113ee903090600401613682565b60206040518083038186803b15801561140657600080fd5b505afa15801561141a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143e91906135a3565b83546001600160a01b0316600090815260096020526040812054919250906114699084908490612c87565b8454601354919250611488916001600160a01b03918216911683612f41565b601360009054906101000a90046001600160a01b03166001600160a01b03166322a3d1a48560000160009054906101000a90046001600160a01b031686600101548760020154886003015489600401548a600501548b60060160009054906101000a90046001600160a01b03168c60070160009054906101000a90046001600160a01b03168a8f6040518b63ffffffff1660e01b81526004016115349a9998979695949392919061371b565b602060405180830381600087803b15801561154e57600080fd5b505af1158015611562573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158691906134e5565b506001840154600285015583546001600160a01b0316600090815260096020526040812080548592906115ba908490613c7c565b90915550506040517f8b1497abd425402b9139208bcb7f83d61a7545712a632d73a1ae68039cd593ab906112e89088908490613bf9565b600260015414156116145760405162461bcd60e51b81526004016108ce90613b32565b6002600155600082815260046020526040902060068101546001600160a01b031633146116535760405162461bcd60e51b81526004016108ce90613903565b80546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611683903090600401613682565b60206040518083038186803b15801561169b57600080fd5b505afa1580156116af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d391906135a3565b82546001600160a01b0316600090815260096020526040812054919250906116fc908584612c87565b90508015801561170c5750600084115b1561171f578061171b81613c93565b9150505b6000811161173f5760405162461bcd60e51b81526004016108ce90613aa7565b600061174e8460050154612a83565b90508061175c836001613c44565b14156117a25783546001600160a01b03166000908152600960205260409020546117909061178a8186613c5c565b85612c87565b6117a2578161179e81613c93565b9250505b818110156117c25760405162461bcd60e51b81526004016108ce90613827565b818460020160008282546117d69190613c44565b909155505083546001600160a01b03166000908152600960205260408120546118029084908690612c87565b85546001600160a01b0316600090815260096020526040812080549293508592909190611830908490613c7c565b9091555050845461184b906001600160a01b03163383612e3b565b84546040517fccad973dcd043c7d680389db4378bd6b9775db7124092e9e0422c9e46d7985dc91611889916001600160a01b039091169084906136ba565b60405180910390a15050600180555050505050565b600260015414156118c15760405162461bcd60e51b81526004016108ce90613b32565b6002600155336001600160a01b03821614156118ef5760405162461bcd60e51b81526004016108ce90613a44565b600082815260046020526040902060068101546001600160a01b031633146119295760405162461bcd60e51b81526004016108ce90613903565b611931613316565b8160000160009054906101000a90046001600160a01b031681600001906001600160a01b031690816001600160a01b03168152505081600101548160200181815250508160020154816040018181525050816003015481606001818152505081600401548160800181815250506005548160a0018181525050828160c001906001600160a01b031690816001600160a01b0316815250508160070160009054906101000a90046001600160a01b03168160e001906001600160a01b031690816001600160a01b0316815250508060046000600554815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c08201518160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550905050600760008360000160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020019081526020016000206005549080600181540180825580915050600190039060005260206000200160009091909190915055600060086000856001600160a01b03166001600160a01b031681526020019081526020016000209050611b8a8360000160009054906101000a90046001600160a01b031682600001612f2890919063ffffffff16565b5082546001600160a01b031660009081526002820160209081526040822060a0850151815460018101835591845291832001556005805491611bcb83613c93565b90915550506001830154600284015560a08201516040517fc1329eea12893a6eff780df43cf4ec708fdc6ce7fbacf84ee8cf9355a9af1dd891611c149188919033908990613c07565b60405180910390a1505060018055505050565b60026001541415611c4a5760405162461bcd60e51b81526004016108ce90613b32565b6002600155600081815260046020526040902060068101546001600160a01b03163314611c895760405162461bcd60e51b81526004016108ce90613903565b60078101546001600160a01b0316611ca057600080fd5b60070180546001600160a01b03191690555060018055565b60026001541415611cdb5760405162461bcd60e51b81526004016108ce90613b32565b600260015580611cfd5760405162461bcd60e51b81526004016108ce90613a62565b600082815260046020526040902060068101546001600160a01b03163314611d375760405162461bcd60e51b81526004016108ce90613903565b600381015415611d595760405162461bcd60e51b81526004016108ce906139fa565b80546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611d89903090600401613682565b60206040518083038186803b158015611da157600080fd5b505afa158015611db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd991906135a3565b82546001600160a01b031660009081526009602052604081205491925090611e02908584612c87565b90508260010154818460020154611e199190613c44565b1115611e2457600080fd5b611e2c613316565b83546001600160a01b039081168252602082018390526004850154608083015260055460a08301523360c083015260078501541660e0820152600284018054839190600090611e7c908490613c44565b9091555050600580546000908152600460208181526040808420865181546001600160a01b03199081166001600160a01b03928316178355888501516001808501919091558985015160028086019190915560608b0151600386015560808b01519785019790975560a08a018051858b015560c08b0151600686018054851691861691909117905560e08b015160079586018054909416908516179092558c548316885292855283872088548154808601835591895286892090910155338752600885528387208c54909216875294018352908420925183549182018455928452908320015581549190611f6f83613c93565b91905055507fbfc02f4250f5e54a31a72371b1932061a0245db2afb5124b547306f6ebffd8ad868260a00151876040516112e893929190613c2e565b6000816001600160a01b031663f968f4936040518163ffffffff1660e01b815260040160206040518083038186803b158015611fe657600080fd5b505afa158015611ffa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a591906134e5565b612026612f3d565b6001600160a01b03166120376121e2565b6001600160a01b03161461205d5760405162461bcd60e51b81526004016108ce90613965565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6120af612f3d565b6001600160a01b03166120c06121e2565b6001600160a01b0316146120e65760405162461bcd60e51b81526004016108ce90613965565b601480546001600160a01b0319166001600160a01b0392909216919091179055565b60006107e86002612c7c565b61211c612f3d565b6001600160a01b031661212d6121e2565b6001600160a01b0316146121535760405162461bcd60e51b81526004016108ce90613965565b801561216a57612164600c83612f28565b50612177565b612175600c83613027565b505b5050565b600e54600f546010546011546001600160a01b03918216911684565b6001600160a01b03821660009081526007602052604081208054839081106121cf57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b6000546001600160a01b031690565b6001600160a01b0382166000908152600860205260408120610f86908361303c565b60006108a560028361303c565b6013546001600160a01b031681565b61223a600a82612e26565b156122575760405162461bcd60e51b81526004016108ce90613922565b6011546001600160a01b03166122ca57600f5434146122885760405162461bcd60e51b81526004016108ce90613a1f565b601054600f546040516001600160a01b039092169181156108fc0291906000818181858888f193505050501580156122c4573d6000803e3d6000fd5b506122ed565b601154601054600f546122ed926001600160a01b03908116923392911690612d36565b612177600a82612f28565b6001600160a01b03811660009081526008602052604081206108a590612c7c565b60006108a5600a8361303c565b600260015414156123495760405162461bcd60e51b81526004016108ce90613b32565b60026001556402540be40081106123725760405162461bcd60e51b81526004016108ce9061388d565b600082815260046020526040902060068101546001600160a01b031633146123ac5760405162461bcd60e51b81526004016108ce90613903565b818160040154106123cf5760405162461bcd60e51b81526004016108ce906139dd565b80546123e690600a906001600160a01b0316612e26565b612550576000816002015482600101546124009190613c7c565b9050600061241682600e60000154612710612c87565b83546040516370a0823160e01b81529192506000916001600160a01b03909116906370a082319061244b903090600401613682565b60206040518083038186803b15801561246357600080fd5b505afa158015612477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061249b91906135a3565b84546001600160a01b0316600090815260096020526040812054919250906124e5908490849015610c445787546001600160a01b0316600090815260096020526040902054610c47565b8554601054919250612504916001600160a01b03918216911683612e3b565b828560020160008282546125189190613c44565b909155505084546001600160a01b031660009081526009602052604081208054859290612546908490613c7c565b9091555050505050505b600481018290556040517fefaff1b90138281d215452c67f793017f52e456f65c28ac63f5309a89a059b47906125899085908590613bf9565b60405180910390a150506001805550565b601254604051632a35efd360e21b81526001600160a01b039091169063a8d7bf4c906125ca903390600401613682565b60206040518083038186803b1580156125e257600080fd5b505afa1580156125f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061261a91906134e5565b8061262b575061262b600c33612e26565b6126475760405162461bcd60e51b81526004016108ce90613acf565b801561265857612164600a83612f28565b612175600a83613027565b6001600160a01b0391821660009081526008602090815260408083209390941682526002909201909152205490565b60096020526000908152604090205481565b6014546001600160a01b031681565b600080836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126e29190613682565b60206040518083038186803b1580156126fa57600080fd5b505afa15801561270e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273291906135a3565b6001600160a01b0385166000908152600960205260409020549091506108a1908483612c87565b6004602081905260009182526040909120805460018201546002830154600384015494840154600585015460068601546007909601546001600160a01b03958616979496939592939192918216911688565b60006108a5600c83612e26565b6001600160a01b031660009081526007602052604090205490565b6000818152600460205260408120600581015482906127f190612a83565b82546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190612826903090600401613682565b60206040518083038186803b15801561283e57600080fd5b505afa158015612852573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061287691906135a3565b83546001600160a01b0316600090815260096020526040812054919250906128c0908490849015610c445786546001600160a01b0316600090815260096020526040902054610c47565b9695505050505050565b600081815260046020818152604080842081516101008101835281546001600160a01b0390811680835260018401549583019590955260028301548285015260038301546060830152828601546080830152600583015460a08301526006830154811660c083015260079092015490911660e082015290516370a0823160e01b81528493849384938493849384938493849384939192849290916370a082319161297691309101613682565b60206040518083038186803b15801561298e57600080fd5b505afa1580156129a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c691906135a3565b82516001600160a01b03166000908152600960205260408120549192509015612a085782516001600160a01b0316600090815260096020526040902054612a0b565b60015b90506000612a1e84602001518484612c87565b90506000612a3185604001518585612c87565b90508460a0015185600001518383886020015189604001518a606001518b608001518c60c001518d60e001519e509e509e509e509e509e509e509e509e509e5050505050509193959799509193959799565b60008181526004602052604081206003810154829015612aa4576002612aa7565b60015b905060008160ff16600114612ac0578260010154612ad4565b82600201548360010154612ad49190613c7c565b90506000612b048460030154856004015484428860070160009054906101000a90046001600160a01b0316613048565b90508260ff1660021415612b215760028401546128c09082613c7c565b95945050505050565b60055481565b60006108a5600c8361303c565b60065481565b612b4b612f3d565b6001600160a01b0316612b5c6121e2565b6001600160a01b031614612b825760405162461bcd60e51b81526004016108ce90613965565b6001600160a01b038116612ba85760405162461bcd60e51b81526004016108ce90613847565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b612c0b612f3d565b6001600160a01b0316612c1c6121e2565b6001600160a01b031614612c425760405162461bcd60e51b81526004016108ce90613965565b600e93909355600f91909155601080546001600160a01b039283166001600160a01b03199182161790915560118054929093169116179055565b60006108a58261314d565b600080806000198587098587029250828110838203039150508060001415612cc15760008411612cb657600080fd5b508290049050610f86565b808411612ccd57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b600080856001600160a01b03166323b872dd868686604051602401612d5d93929190613696565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051612d969190613649565b6000604051808303816000865af19150503d8060008114612dd3576040519150601f19603f3d011682016040523d82523d6000602084013e612dd8565b606091505b5091509150818015612e02575080511580612e02575080806020019051810190612e0291906134e5565b612e1e5760405162461bcd60e51b81526004016108ce90613aee565b505050505050565b6000610f86836001600160a01b038416613151565b600080846001600160a01b031663a9059cbb8585604051602401612e609291906136ba565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051612e999190613649565b6000604051808303816000865af19150503d8060008114612ed6576040519150601f19603f3d011682016040523d82523d6000602084013e612edb565b606091505b5091509150818015612f05575080511580612f05575080806020019051810190612f0591906134e5565b612f215760405162461bcd60e51b81526004016108ce906137f0565b5050505050565b6000610f86836001600160a01b038416613169565b3390565b600080846001600160a01b031663095ea7b38585604051602401612f669291906136ba565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051612f9f9190613649565b6000604051808303816000865af19150503d8060008114612fdc576040519150601f19603f3d011682016040523d82523d6000602084013e612fe1565b606091505b509150915081801561300b57508051158061300b57508080602001905181019061300b91906134e5565b612f215760405162461bcd60e51b81526004016108ce906138ab565b6000610f86836001600160a01b0384166131b3565b6000610f8683836132d0565b60006001600160a01b038216158015906130ce5750816001600160a01b031663f968f4936040518163ffffffff1660e01b815260040160206040518083038186803b15801561309657600080fd5b505afa1580156130aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ce91906134e5565b156130da575082612b21565b8515806130e657508486145b15613102578285106130f95760006130fb565b835b9050612b21565b828581111561310e5750845b868110156131195750855b60006131258883613c7c565b905060006131338989613c7c565b9050613140878383612c87565b9998505050505050505050565b5490565b60009081526001919091016020526040902054151590565b60006131758383613151565b6131ab575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556108a5565b5060006108a5565b600081815260018301602052604081205480156132c65760006131d7600183613c7c565b85549091506000906131eb90600190613c7c565b9050600086600001828154811061321257634e487b7160e01b600052603260045260246000fd5b906000526020600020015490508087600001848154811061324357634e487b7160e01b600052603260045260246000fd5b60009182526020909120015561325a836001613c44565b6000828152600189016020526040902055865487908061328a57634e487b7160e01b600052603160045260246000fd5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506108a5565b60009150506108a5565b815460009082106132f35760405162461bcd60e51b81526004016108ce90613783565b8260000182815481106121cf57634e487b7160e01b600052603260045260246000fd5b60405180610100016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b600060208284031215613387578081fd5b8135610f8681613cc4565b600080604083850312156133a4578081fd5b82356133af81613cc4565b915060208301356133bf81613cc4565b809150509250929050565b6000806000606084860312156133de578081fd5b83356133e981613cc4565b925060208401356133f981613cc4565b929592945050506040919091013590565b60008060006040848603121561341e578283fd5b833561342981613cc4565b9250602084013567ffffffffffffffff80821115613445578384fd5b818601915086601f830112613458578384fd5b813581811115613466578485fd5b87602060a08302850101111561347a578485fd5b6020830194508093505050509250925092565b6000806040838503121561349f578182fd5b82356134aa81613cc4565b915060208301356133bf81613cdc565b600080604083850312156134cc578182fd5b82356134d781613cc4565b946020939093013593505050565b6000602082840312156134f6578081fd5b8151610f8681613cdc565b600060a08284031215613512578081fd5b60405160a0810181811067ffffffffffffffff8211171561354157634e487b7160e01b83526041600452602483fd5b604052823561354f81613cc4565b80825250602083013560208201526040830135604082015260608301356060820152608083013561357f81613cc4565b60808201529392505050565b60006020828403121561359c578081fd5b5035919050565b6000602082840312156135b4578081fd5b5051919050565b600080604083850312156135cd578182fd5b8235915060208301356133bf81613cc4565b600080604083850312156135f1578182fd5b50508035926020909101359150565b60008060008060808587031215613615578182fd5b8435935060208501359250604085013561362e81613cc4565b9150606085013561363e81613cc4565b939692955090935050565b60008251815b81811015613669576020818601810151858301520161364f565b818111156136775782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039889168152602081019790975260408701959095526060860193909352608085019190915260a0840152831660c083015290911660e08201526101000190565b6001600160a01b039a8b168152602081019990995260408901979097526060880195909552608087019390935260a0860191909152851660c085015290931660e08301526101008201929092526101208101919091526101400190565b901515815260200190565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252601190820152701512535154d51053540812539590531251607a1b604082015260600190565b6020808252601f908201527f5472616e7366657248656c7065723a205452414e534645525f4641494c454400604082015260600190565b602080825260069082015265105353d5539560d21b604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526004908201526354494d4560e01b604082015260600190565b6020808252601e908201527f5472616e7366657248656c7065723a20415050524f56455f4641494c45440000604082015260600190565b6020808252600790820152661393d50814d15560ca1b604082015260600190565b60208082526005908201526427aba722a960d91b604082015260600190565b6020808252600490820152631410525160e21b604082015260600190565b6020808252600b908201526a1352538811115413d4d25560aa1b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252600690820152651411549253d160d21b604082015260600190565b6020808252600990820152684e4f20504152414d5360b81b604082015260600190565b60208082526003908201526211539160ea1b604082015260600190565b6020808252600b908201526a2627a1a5902a2ca822901960a91b604082015260600190565b6020808252600b908201526a119151481393d50813515560aa1b604082015260600190565b60208082526004908201526329a2a62360e11b604082015260600190565b6020808252600b908201526a16915493c8105353d5539560aa1b604082015260600190565b60208082526006908201526553484152455360d01b604082015260600190565b6020808252600e908201526d16915493c815d2551211149055d360921b604082015260600190565b60208082526005908201526420a226a4a760d91b604082015260600190565b60208082526024908201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416040820152631253115160e21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b998a526001600160a01b0398891660208b015260408a01979097526060890195909552608088019390935260a087019190915260c086015260e08501528216610100840152166101208201526101400190565b918252602082015260400190565b93845260208401929092526001600160a01b03908116604084015216606082015260800190565b9283526020830191909152604082015260600190565b60008219821115613c5757613c57613cae565b500190565b600082613c7757634e487b7160e01b81526012600452602481fd5b500490565b600082821015613c8e57613c8e613cae565b500390565b6000600019821415613ca757613ca7613cae565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114613cd957600080fd5b50565b8015158114613cd957600080fdfea2646970667358221220e78cf8e6ed610868dc3d549269bb38235edf487a98c2154c253483f91753c51d64736f6c63430008010033

Verified Source Code Full Match

Compiler: v0.8.1+commit.df193b15 EVM: istanbul Optimization: Yes (200 runs)
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT

// File @openzeppelin/contracts/token/ERC20/[email protected]

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @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);
}
Context.sol 26 lines
// SPDX-License-Identifier: MIT

// File @openzeppelin/contracts/utils/[email protected]

pragma solidity ^0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}
Ownable.sol 70 lines
// SPDX-License-Identifier: MIT

// File @openzeppelin/contracts/access/[email protected]

pragma solidity ^0.8.0;

import "./Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}
FullMath.sol 110 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Sourced from https://gist.github.com/paulrberg/439ebe860cd2f9893852e2cab5655b65, credits to Paulrberg for porting to solidity v0.8
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = a * b
        // Compute the product mod 2**256 and mod 2**256 - 1
        // then use the Chinese Remainder Theorem to reconstruct
        // the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2**256 + prod0
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            require(denominator > 0);
            assembly {
                result := div(prod0, denominator)
            }
            return result;
        }

        // Make sure the result is less than 2**256.
        // Also prevents denominator == 0
        require(denominator > prod1);

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0]
        // Compute remainder using mulmod
        uint256 remainder;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        // Subtract 256 bit number from 512 bit number
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator
        // Compute largest power of two divisor of denominator.
        // Always >= 1.
        unchecked {
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

            // Because the division is now exact we can divide by multiplying
            // with the modular inverse of denominator. This will give us the
            // correct result modulo 2**256. Since the precoditions guarantee
            // that the outcome is less than 2**256, this is the final result.
            // We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inv;
            return result;
        }
    }
}
TokenVesting.sol 577 lines
// SPDX-License-Identifier: UNLICENSED
// ALL RIGHTS RESERVED
// Unicrypt by SDDTech reserves all rights on this code. You may NOT copy these contracts.

// This contract locks ERC20 tokens. This can be used for:
// - Token developers to prove they have locked tokens
// - Presale projects or investors to lock a portion of tokens for a vesting period
// - Farming platforms to lock a percentage of the farmed rewards for a period of time
// - To lock tokens until a specific unlock date.
// - To send tokens to someone under a time lock.

// This contract is for ERC20 tokens, and supports high deflationary and rebasing tokens by using a pooling and share issuing mechanism.
// This is NOT for AMM LP tokens (such as UNIV2), Please use our liquidity lockers for this.
// Locking LP tokens in this contract will not show in the Unicrypt browser.

// *** LOCK TYPES ***
// Lock Type 1: when startEmission == 0 the lock is considered lockType 1. This is a normal lock
// whereby tokens can be withdrawn on the due date (endEmission).

// Lock Type 2: when startEmission != 0. Lock tokens over a period, with an amount withdrawable every block. 
// This scales linearly over time from startEmission -> endEmission. 
// e.g. If the lock period is 100 seconds, 50 seconds after the startEmission you can withdraw 50% of the lock.
// Instead of making 10 locks for 10 months to withdraw tokens at the end of each month, you can now make 1 linear scaling lock with a period
// of 10 months and withdraw the relative share every block.

// *** CUSTOM PREMATURE UNLOCKING CONDITIONS ***
// All locks support premature unlocking conditions. A premature unlock condition can be anything that implements the IUnlockCondition interface
// If IUnlockCondition(address).unlockTokens() returns true, the lock withdraw date is overriden and the entire lock value can be withdrawn.
// The key here is this is for premature unlocks, locks always fall back to the endEmission date 
// even if unlockTokens() returns false, and are therefore always withdrawble in full by the unlockDate.
// Example use cases, Imagine a presale is 1 week long. Marketers tokens are locked for 1 week to prevent them initiating
// markets and setting initial prices on an AMM. The presale concludes within 5 minuites. Marketers now need to wait 1 week,
// to access their tokens. With conditional unlocks a condition can be set to return true once a presale has concluded
// and override the 1 week lock making their tokens instantly withdrawble post presale. 
// Another use case could be to allow token developers or investors to prematurely unlock their tokens
// if the price reaches a specified target, or for governance to vote for developers to unlock tokens prematurely 
// for development purposes met or raodmap goals met.
// Get creative!

// Please be aware if you are locking tokens to prove to your community you have locked tokens for long term you should not use a premature unlocking condition 
// as these types of locks will be shown differently in the browser to a normal lock with no unlocking condition.
// Unlocking conditions can always be revoked by the lock owner to give more credibility to the lock.

pragma solidity ^0.8.0;

import "./TransferHelper.sol";
import './VestingMathLibrary.sol';
import './FullMath.sol';

import "./EnumerableSet.sol";
import "./Ownable.sol";
import "./ReentrancyGuard.sol";
import "./IERC20.sol";

interface IMigrator {
    function migrate(address token, uint256 sharesDeposited, uint256 sharesWithdrawn, uint256 startEmission, uint256 endEmission, uint256 lockID, address owner, address condition, uint256 amountInTokens, uint256 option) external returns (bool);
}

interface IUnicryptAdmin {
    function userIsAdmin(address _user) external view returns (bool);
}

interface ITokenBlacklist {
    function checkToken(address _token) external view;
}

contract TokenVesting is Ownable, ReentrancyGuard {
  using EnumerableSet for EnumerableSet.AddressSet;

  struct UserInfo {
    EnumerableSet.AddressSet lockedTokens; // records all token addresses the user has locked
    mapping(address => uint256[]) locksForToken; // map erc20 address to lockId for that token
  }

  struct TokenLock {
    address tokenAddress; // The token address
    uint256 sharesDeposited; // the total amount of shares deposited
    uint256 sharesWithdrawn; // amount of shares withdrawn
    uint256 startEmission; // date token emission begins
    uint256 endEmission; // the date the tokens can be withdrawn
    uint256 lockID; // lock id per token lock
    address owner; // the owner who can edit or withdraw the lock
    address condition; // address(0) = no condition, otherwise the condition contract must implement IUnlockCondition
  }
  
  struct LockParams {
    address payable owner; // the user who can withdraw tokens once the lock expires.
    uint256 amount; // amount of tokens to lock
    uint256 startEmission; // 0 if lock type 1, else a unix timestamp
    uint256 endEmission; // the unlock date as a unix timestamp (in seconds)
    address condition; // address(0) = no condition, otherwise the condition must implement IUnlockCondition
  }

  EnumerableSet.AddressSet private TOKENS; // list of all unique tokens that have a lock
  mapping(uint256 => TokenLock) public LOCKS; // map lockID nonce to the lock
  uint256 public NONCE = 0; // incremental lock nonce counter, this is the unique ID for the next lock
  uint256 public MINIMUM_DEPOSIT = 100; // minimum divisibility per lock at time of locking
  
  mapping(address => uint256[]) private TOKEN_LOCKS; // map token address to array of lockIDs for that token
  mapping(address => UserInfo) private USERS;

  mapping(address => uint) public SHARES; // map token to number of shares per token, shares allow rebasing and deflationary tokens to compute correctly
  
  EnumerableSet.AddressSet private ZERO_FEE_WHITELIST; // Tokens that have been whitelisted to bypass all fees
  EnumerableSet.AddressSet private TOKEN_WHITELISTERS; // whitelisting contracts and users who can enable no fee for tokens.
  
  struct FeeStruct {
    uint256 tokenFee;
    uint256 freeLockingFee;
    address payable feeAddress;
    address freeLockingToken; // if this is address(0) then it is the gas token of the network (e.g ETH, BNB, Matic)
  }
  
  FeeStruct public FEES;
  
  IUnicryptAdmin UNCX_ADMINS;
  IMigrator public MIGRATOR;
  ITokenBlacklist public BLACKLIST; // prevent AMM tokens with a blacklisting contract

  event onLock(uint256 lockID, address token, address owner, uint256 amountInTokens, uint256 startEmission, uint256 endEmission);
  event onWithdraw(address lpToken, uint256 amountInTokens);
  event onRelock(uint256 lockID, uint256 unlockDate);
  event onTransferLock(uint256 lockIDFrom, uint256 lockIDto, address oldOwner, address newOwner);
  event onSplitLock(uint256 fromLockID, uint256 toLockID, uint256 amountInTokens);
  event onMigrate(uint256 lockID, uint256 amountInTokens);

  constructor (IUnicryptAdmin _uncxAdmins) {
    UNCX_ADMINS = _uncxAdmins;
    FEES.tokenFee = 35;
    FEES.feeAddress = payable(0xAA3d85aD9D128DFECb55424085754F6dFa643eb1);
    FEES.freeLockingFee = 10e18;
  }
  
  /**
   * @notice set the migrator contract which allows the lock to be migrated
   */
  function setMigrator(IMigrator _migrator) external onlyOwner {
    MIGRATOR = _migrator;
  }
  
  function setBlacklistContract(ITokenBlacklist _contract) external onlyOwner {
    BLACKLIST = _contract;
  }
  
  function setFees(uint256 _tokenFee, uint256 _freeLockingFee, address payable _feeAddress, address _freeLockingToken) external onlyOwner {
    FEES.tokenFee = _tokenFee;
    FEES.freeLockingFee = _freeLockingFee;
    FEES.feeAddress = _feeAddress;
    FEES.freeLockingToken = _freeLockingToken;
  }
  
  /**
   * @notice whitelisted accounts and contracts who can call the editZeroFeeWhitelist function
   */
  function adminSetWhitelister(address _user, bool _add) external onlyOwner {
    if (_add) {
      TOKEN_WHITELISTERS.add(_user);
    } else {
      TOKEN_WHITELISTERS.remove(_user);
    }
  }
  
  // Pay a once off fee to have free use of the lockers for the token
  function payForFreeTokenLocks (address _token) external payable {
      require(!ZERO_FEE_WHITELIST.contains(_token), 'PAID');
      // charge Fee
      if (FEES.freeLockingToken == address(0)) {
          require(msg.value == FEES.freeLockingFee, 'FEE NOT MET');
          FEES.feeAddress.transfer(FEES.freeLockingFee);
      } else {
          TransferHelper.safeTransferFrom(address(FEES.freeLockingToken), address(msg.sender), FEES.feeAddress, FEES.freeLockingFee);
      }
      ZERO_FEE_WHITELIST.add(_token);
  }
  
  // Callable by UNCX_ADMINS or whitelisted contracts (such as presale contracts)
  function editZeroFeeWhitelist (address _token, bool _add) external {
    require(UNCX_ADMINS.userIsAdmin(msg.sender) || TOKEN_WHITELISTERS.contains(msg.sender), 'ADMIN');
    if (_add) {
      ZERO_FEE_WHITELIST.add(_token);
    } else {
      ZERO_FEE_WHITELIST.remove(_token);
    }
  }

  /**
   * @notice Creates one or multiple locks for the specified token
   * @param _token the erc20 token address
   * @param _lock_params an array of locks with format: [LockParams[owner, amount, startEmission, endEmission, condition]]
   * owner: user or contract who can withdraw the tokens
   * amount: must be >= 100 units
   * startEmission = 0 : LockType 1
   * startEmission != 0 : LockType 2 (linear scaling lock)
   * use address(0) for no premature unlocking condition
   * Fails if startEmission is not less than EndEmission
   * Fails is amount < 100
   */
  function lock (address _token, LockParams[] calldata _lock_params) external nonReentrant {
    require(_lock_params.length > 0, 'NO PARAMS');
    if (address(BLACKLIST) != address(0)) {
        BLACKLIST.checkToken(_token);
    }
    uint256 totalAmount = 0;
    for (uint256 i = 0; i < _lock_params.length; i++) {
        totalAmount += _lock_params[i].amount;
    }

    uint256 balanceBefore = IERC20(_token).balanceOf(address(this));
    TransferHelper.safeTransferFrom(_token, address(msg.sender), address(this), totalAmount);
    uint256 amountIn = IERC20(_token).balanceOf(address(this)) - balanceBefore;

    // Fees
    if (!ZERO_FEE_WHITELIST.contains(_token)) {
      uint256 lockFee = FullMath.mulDiv(amountIn, FEES.tokenFee, 10000);
      TransferHelper.safeTransfer(_token, FEES.feeAddress, lockFee);
      amountIn -= lockFee;
    }
    
    uint256 shares = 0;
    for (uint256 i = 0; i < _lock_params.length; i++) {
        LockParams memory lock_param = _lock_params[i];
        require(lock_param.startEmission < lock_param.endEmission, 'PERIOD');
        require(lock_param.endEmission < 1e10, 'TIMESTAMP INVALID'); // prevents errors when timestamp entered in milliseconds
        require(lock_param.amount >= MINIMUM_DEPOSIT, 'MIN DEPOSIT');
        uint256 amountInTokens = FullMath.mulDiv(lock_param.amount, amountIn, totalAmount);

        if (SHARES[_token] == 0) {
          shares = amountInTokens;
        } else {
          shares = FullMath.mulDiv(amountInTokens, SHARES[_token], balanceBefore == 0 ? 1 : balanceBefore);
        }
        require(shares > 0, 'SHARES');
        SHARES[_token] += shares;
        balanceBefore += amountInTokens;

        TokenLock memory token_lock;
        token_lock.tokenAddress = _token;
        token_lock.sharesDeposited = shares;
        token_lock.startEmission = lock_param.startEmission;
        token_lock.endEmission = lock_param.endEmission;
        token_lock.lockID = NONCE;
        token_lock.owner = lock_param.owner;
        if (lock_param.condition != address(0)) {
            // if the condition contract does not implement the interface and return a bool
            // the below line will fail and revert the tx as the conditional contract is invalid
            IUnlockCondition(lock_param.condition).unlockTokens();
            token_lock.condition = lock_param.condition;
        }
    
        // record the lock globally
        LOCKS[NONCE] = token_lock;
        TOKENS.add(_token);
        TOKEN_LOCKS[_token].push(NONCE);
    
        // record the lock for the user
        UserInfo storage user = USERS[lock_param.owner];
        user.lockedTokens.add(_token);
        user.locksForToken[_token].push(NONCE);
        
        NONCE ++;
        emit onLock(token_lock.lockID, _token, token_lock.owner, amountInTokens, token_lock.startEmission, token_lock.endEmission);
    }
  }
  
   /**
   * @notice withdraw a specified amount from a lock. _amount is the ideal amount to be withdrawn.
   * however, this amount might be slightly different in rebasing tokens due to the conversion to shares,
   * then back into an amount
   * @param _lockID the lockID of the lock to be withdrawn
   * @param _amount amount of tokens to withdraw
   */
  function withdraw (uint256 _lockID, uint256 _amount) external nonReentrant {
    TokenLock storage userLock = LOCKS[_lockID];
    require(userLock.owner == msg.sender, 'OWNER');
    // convert _amount to its representation in shares
    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));
    uint256 shareDebit = FullMath.mulDiv(SHARES[userLock.tokenAddress], _amount, balance);
    // round _amount up to the nearest whole share if the amount of tokens specified does not translate to
    // at least 1 share.
    if (shareDebit == 0 && _amount > 0) {
      shareDebit ++;
    }
    require(shareDebit > 0, 'ZERO WITHDRAWL');
    uint256 withdrawableShares = getWithdrawableShares(userLock.lockID);
    // dust clearance block, as mulDiv rounds down leaving one share stuck, clear all shares for dust amounts
    if (shareDebit + 1 == withdrawableShares) {
      if (FullMath.mulDiv(SHARES[userLock.tokenAddress], balance / SHARES[userLock.tokenAddress], balance) == 0){
        shareDebit++;
      }
    }
    require(withdrawableShares >= shareDebit, 'AMOUNT');
    userLock.sharesWithdrawn += shareDebit;

    // now convert shares to the actual _amount it represents, this may differ slightly from the 
    // _amount supplied in this methods arguments.
    uint256 amountInTokens = FullMath.mulDiv(shareDebit, balance, SHARES[userLock.tokenAddress]);
    SHARES[userLock.tokenAddress] -= shareDebit;
    
    TransferHelper.safeTransfer(userLock.tokenAddress, msg.sender, amountInTokens);
    emit onWithdraw(userLock.tokenAddress, amountInTokens);
  }
  
  /**
   * @notice extend a lock with a new unlock date, if lock is Type 2 it extends the emission end date
   */
  function relock (uint256 _lockID, uint256 _unlock_date) external nonReentrant {
    require(_unlock_date < 1e10, 'TIME'); // prevents errors when timestamp entered in milliseconds
    TokenLock storage userLock = LOCKS[_lockID];
    require(userLock.owner == msg.sender, 'OWNER');
    require(userLock.endEmission < _unlock_date, 'END');
    // percent fee
    if (!ZERO_FEE_WHITELIST.contains(userLock.tokenAddress)) {
        uint256 remainingShares = userLock.sharesDeposited - userLock.sharesWithdrawn;
        uint256 feeInShares = FullMath.mulDiv(remainingShares, FEES.tokenFee, 10000);
        uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));
        uint256 feeInTokens = FullMath.mulDiv(feeInShares, balance, SHARES[userLock.tokenAddress] == 0 ? 1 : SHARES[userLock.tokenAddress]);
        TransferHelper.safeTransfer(userLock.tokenAddress, FEES.feeAddress, feeInTokens);
        userLock.sharesWithdrawn += feeInShares;
        SHARES[userLock.tokenAddress] -= feeInShares;
    }
    userLock.endEmission = _unlock_date;
    emit onRelock(_lockID, _unlock_date);
  }
  
  /**
   * @notice increase the amount of tokens per a specific lock, this is preferable to creating a new lock
   * Its possible to increase someone elses lock here it does not need to be your own, useful for contracts
   */
  function incrementLock (uint256 _lockID, uint256 _amount) external nonReentrant {
    TokenLock storage userLock = LOCKS[_lockID];
    require(_amount >= MINIMUM_DEPOSIT, 'MIN DEPOSIT');
    
    uint256 balanceBefore = IERC20(userLock.tokenAddress).balanceOf(address(this));
    TransferHelper.safeTransferFrom(userLock.tokenAddress, address(msg.sender), address(this), _amount);
    uint256 amountInTokens = IERC20(userLock.tokenAddress).balanceOf(address(this)) - balanceBefore;

    // percent fee
    if (!ZERO_FEE_WHITELIST.contains(userLock.tokenAddress)) {
        uint256 lockFee = FullMath.mulDiv(amountInTokens, FEES.tokenFee, 10000);
        TransferHelper.safeTransfer(userLock.tokenAddress, FEES.feeAddress, lockFee);
        amountInTokens -= lockFee;
    }
    uint256 shares;
    if (SHARES[userLock.tokenAddress] == 0) {
      shares = amountInTokens;
    } else {
      shares = FullMath.mulDiv(amountInTokens, SHARES[userLock.tokenAddress], balanceBefore);
    }
    require(shares > 0, 'SHARES');
    SHARES[userLock.tokenAddress] += shares;
    userLock.sharesDeposited += shares;
    emit onLock(userLock.lockID, userLock.tokenAddress, userLock.owner, amountInTokens, userLock.startEmission, userLock.endEmission);
  }
  
  /**
   * @notice transfer a lock to a new owner, e.g. presale project -> project owner
   * Please be aware this generates a new lock, and nulls the old lock, so a new ID is assigned to the new lock.
   */
  function transferLockOwnership (uint256 _lockID, address payable _newOwner) external nonReentrant {
    require(msg.sender != _newOwner, 'SELF');
    TokenLock storage transferredLock = LOCKS[_lockID];
    require(transferredLock.owner == msg.sender, 'OWNER');
    
    TokenLock memory token_lock;
    token_lock.tokenAddress = transferredLock.tokenAddress;
    token_lock.sharesDeposited = transferredLock.sharesDeposited;
    token_lock.sharesWithdrawn = transferredLock.sharesWithdrawn;
    token_lock.startEmission = transferredLock.startEmission;
    token_lock.endEmission = transferredLock.endEmission;
    token_lock.lockID = NONCE;
    token_lock.owner = _newOwner;
    token_lock.condition = transferredLock.condition;
    
    // record the lock globally
    LOCKS[NONCE] = token_lock;
    TOKEN_LOCKS[transferredLock.tokenAddress].push(NONCE);
    
    // record the lock for the new owner 
    UserInfo storage newOwner = USERS[_newOwner];
    newOwner.lockedTokens.add(transferredLock.tokenAddress);
    newOwner.locksForToken[transferredLock.tokenAddress].push(token_lock.lockID);
    NONCE ++;
    
    // zero the lock from the old owner
    transferredLock.sharesWithdrawn = transferredLock.sharesDeposited;
    emit onTransferLock(_lockID, token_lock.lockID, msg.sender, _newOwner);
  }
  
  /**
   * @notice split a lock into two seperate locks, useful when a lock is about to expire and youd like to relock a portion
   * and withdraw a smaller portion
   * Only works on lock type 1, this feature does not work with lock type 2
   * @param _amount the amount in tokens
   */
  function splitLock (uint256 _lockID, uint256 _amount) external nonReentrant {
    require(_amount > 0, 'ZERO AMOUNT');
    TokenLock storage userLock = LOCKS[_lockID];
    require(userLock.owner == msg.sender, 'OWNER');
    require(userLock.startEmission == 0, 'LOCK TYPE 2');

    // convert _amount to its representation in shares
    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));
    uint256 amountInShares = FullMath.mulDiv(SHARES[userLock.tokenAddress], _amount, balance);

    require(userLock.sharesWithdrawn + amountInShares <= userLock.sharesDeposited);
    
    TokenLock memory token_lock;
    token_lock.tokenAddress = userLock.tokenAddress;
    token_lock.sharesDeposited = amountInShares;
    token_lock.endEmission = userLock.endEmission;
    token_lock.lockID = NONCE;
    token_lock.owner = msg.sender;
    token_lock.condition = userLock.condition;
    
    // debit previous lock
    userLock.sharesWithdrawn += amountInShares;
    
    // record the new lock globally
    LOCKS[NONCE] = token_lock;
    TOKEN_LOCKS[userLock.tokenAddress].push(NONCE);
    
    // record the new lock for the owner 
    USERS[msg.sender].locksForToken[userLock.tokenAddress].push(token_lock.lockID);
    NONCE ++;
    emit onSplitLock(_lockID, token_lock.lockID, _amount);
  }
  
  /**
   * @notice migrates to the next locker version, only callable by lock owners
   */
  function migrate (uint256 _lockID, uint256 _option) external nonReentrant {
    require(address(MIGRATOR) != address(0), "NOT SET");
    TokenLock storage userLock = LOCKS[_lockID];
    require(userLock.owner == msg.sender, 'OWNER');
    uint256 sharesAvailable = userLock.sharesDeposited - userLock.sharesWithdrawn;
    require(sharesAvailable > 0, 'AMOUNT');

    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));
    uint256 amountInTokens = FullMath.mulDiv(sharesAvailable, balance, SHARES[userLock.tokenAddress]);
    
    TransferHelper.safeApprove(userLock.tokenAddress, address(MIGRATOR), amountInTokens);
    MIGRATOR.migrate(userLock.tokenAddress, userLock.sharesDeposited, userLock.sharesWithdrawn, userLock.startEmission,
    userLock.endEmission, userLock.lockID, userLock.owner, userLock.condition, amountInTokens, _option);
    
    userLock.sharesWithdrawn = userLock.sharesDeposited;
    SHARES[userLock.tokenAddress] -= sharesAvailable;
    emit onMigrate(_lockID, amountInTokens);
  }
  
  /**
   * @notice premature unlock conditions can be malicous (prevent withdrawls by failing to evalaute or return non bools)
   * or not give community enough insurance tokens will remain locked until the end date, in such a case, it can be revoked
   */
  function revokeCondition (uint256 _lockID) external nonReentrant {
    TokenLock storage userLock = LOCKS[_lockID];
    require(userLock.owner == msg.sender, 'OWNER');
    require(userLock.condition != address(0)); // already set to address(0)
    userLock.condition = address(0);
  }
  
  // test a condition on front end, added here for convenience in UI, returns unlockTokens() bool, or fails
  function testCondition (address condition) external view returns (bool) {
      return (IUnlockCondition(condition).unlockTokens());
  }
  
  // returns withdrawable share amount from the lock, taking into consideration start and end emission
  function getWithdrawableShares (uint256 _lockID) public view returns (uint256) {
    TokenLock storage userLock = LOCKS[_lockID];
    uint8 lockType = userLock.startEmission == 0 ? 1 : 2;
    uint256 amount = lockType == 1 ? userLock.sharesDeposited - userLock.sharesWithdrawn : userLock.sharesDeposited;
    uint256 withdrawable;
    withdrawable = VestingMathLibrary.getWithdrawableAmount (
      userLock.startEmission, 
      userLock.endEmission, 
      amount, 
      block.timestamp, 
      userLock.condition
    );
    if (lockType == 2) {
      withdrawable -= userLock.sharesWithdrawn;
    }
    return withdrawable;
  }
  
  // convenience function for UI, converts shares to the current amount in tokens
  function getWithdrawableTokens (uint256 _lockID) external view returns (uint256) {
    TokenLock storage userLock = LOCKS[_lockID];
    uint256 withdrawableShares = getWithdrawableShares(userLock.lockID);
    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));
    uint256 amountTokens = FullMath.mulDiv(withdrawableShares, balance, SHARES[userLock.tokenAddress] == 0 ? 1 : SHARES[userLock.tokenAddress]);
    return amountTokens;
  }

  // For UI use
  function convertSharesToTokens (address _token, uint256 _shares) external view returns (uint256) {
    uint256 balance = IERC20(_token).balanceOf(address(this));
    return FullMath.mulDiv(_shares, balance, SHARES[_token]);
  }

  function convertTokensToShares (address _token, uint256 _tokens) external view returns (uint256) {
    uint256 balance = IERC20(_token).balanceOf(address(this));
    return FullMath.mulDiv(SHARES[_token], _tokens, balance);
  }
  
  // For use in UI, returns more useful lock Data than just querying LOCKS,
  // such as the real-time token amount representation of a locks shares
  function getLock (uint256 _lockID) external view returns (uint256, address, uint256, uint256, uint256, uint256, uint256, uint256, address, address) {
      TokenLock memory tokenLock = LOCKS[_lockID];

      uint256 balance = IERC20(tokenLock.tokenAddress).balanceOf(address(this));
      uint256 totalSharesOr1 = SHARES[tokenLock.tokenAddress] == 0 ? 1 : SHARES[tokenLock.tokenAddress];
      // tokens deposited and tokens withdrawn is provided for convenience in UI, with rebasing these amounts will change
      uint256 tokensDeposited = FullMath.mulDiv(tokenLock.sharesDeposited, balance, totalSharesOr1);
      uint256 tokensWithdrawn = FullMath.mulDiv(tokenLock.sharesWithdrawn, balance, totalSharesOr1);
      return (tokenLock.lockID, tokenLock.tokenAddress, tokensDeposited, tokensWithdrawn, tokenLock.sharesDeposited, tokenLock.sharesWithdrawn, tokenLock.startEmission, tokenLock.endEmission, 
      tokenLock.owner, tokenLock.condition);
  }
  
  function getNumLockedTokens () external view returns (uint256) {
    return TOKENS.length();
  }
  
  function getTokenAtIndex (uint256 _index) external view returns (address) {
    return TOKENS.at(_index);
  }
  
  function getTokenLocksLength (address _token) external view returns (uint256) {
    return TOKEN_LOCKS[_token].length;
  }
  
  function getTokenLockIDAtIndex (address _token, uint256 _index) external view returns (uint256) {
    return TOKEN_LOCKS[_token][_index];
  }
  
  // user functions
  function getUserLockedTokensLength (address _user) external view returns (uint256) {
    return USERS[_user].lockedTokens.length();
  }
  
  function getUserLockedTokenAtIndex (address _user, uint256 _index) external view returns (address) {
    return USERS[_user].lockedTokens.at(_index);
  }
  
  function getUserLocksForTokenLength (address _user, address _token) external view returns (uint256) {
    return USERS[_user].locksForToken[_token].length;
  }
  
  function getUserLockIDForTokenAtIndex (address _user, address _token, uint256 _index) external view returns (uint256) {
    return USERS[_user].locksForToken[_token][_index];
  }
  
  // no Fee Tokens
  function getZeroFeeTokensLength () external view returns (uint256) {
    return ZERO_FEE_WHITELIST.length();
  }
  
  function getZeroFeeTokenAtIndex (uint256 _index) external view returns (address) {
    return ZERO_FEE_WHITELIST.at(_index);
  }
  
  function tokenOnZeroFeeWhitelist (address _token) external view returns (bool) {
    return ZERO_FEE_WHITELIST.contains(_token);
  }
  
  // whitelist
  function getTokenWhitelisterLength () external view returns (uint256) {
    return TOKEN_WHITELISTERS.length();
  }
  
  function getTokenWhitelisterAtIndex (uint256 _index) external view returns (address) {
    return TOKEN_WHITELISTERS.at(_index);
  }
  
  function getTokenWhitelisterStatus (address _user) external view returns (bool) {
    return TOKEN_WHITELISTERS.contains(_user);
  }
}
EnumerableSet.sol 299 lines
// SPDX-License-Identifier: MIT

// File @openzeppelin/contracts/utils/structs/[email protected]

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

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            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] = toDeleteIndex + 1; // All indexes are 1-based

            // 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) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

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

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


    // 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));
    }
}
TransferHelper.sol 21 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

}
ReentrancyGuard.sol 64 lines
// SPDX-License-Identifier: MIT

// File @openzeppelin/contracts/security/[email protected]

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor () {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make 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;
    }
}
VestingMathLibrary.sol 41 lines
// SPDX-License-Identifier: UNLICENSED
// ALL RIGHTS RESERVED
// Unicrypt by SDDTech reserves all rights on this code. You may NOT copy these contracts.

pragma solidity ^0.8.0;

import './FullMath.sol';

// Allows a seperate contract with a unlockTokens() function to be used to override unlock dates
interface IUnlockCondition {
    function unlockTokens() external view returns (bool);
}

library VestingMathLibrary {

  // gets the withdrawable amount from a lock
  function getWithdrawableAmount (uint256 startEmission, uint256 endEmission, uint256 amount, uint256 timeStamp, address condition) internal view returns (uint256) {
    // It is possible in some cases IUnlockCondition(condition).unlockTokens() will fail (func changes state or does not return a bool)
    // for this reason we implemented revokeCondition per lock so funds are never stuck in the contract.
    
    // Prematurely release the lock if the condition is met
    if (condition != address(0) && IUnlockCondition(condition).unlockTokens()) {
      return amount;
    }
    // Lock type 1 logic block (Normal Unlock on due date)
    if (startEmission == 0 || startEmission == endEmission) {
        return endEmission < timeStamp ? amount : 0;
    }
    // Lock type 2 logic block (Linear scaling lock)
    uint256 timeClamp = timeStamp;
    if (timeClamp > endEmission) {
        timeClamp = endEmission;
    }
    if (timeClamp < startEmission) {
        timeClamp = startEmission;
    }
    uint256 elapsed = timeClamp - startEmission;
    uint256 fullPeriod = endEmission - startEmission;
    return FullMath.mulDiv(amount, elapsed, fullPeriod); // fullPeriod cannot equal zero due to earlier checks and restraints when locking tokens (startEmission < endEmission)
  }
}

Read Contract

BLACKLIST 0xc8b0cf68 → address
FEES 0x8b7b23ee → uint256, uint256, address, address
LOCKS 0xcf0d5af3 → address, uint256, uint256, uint256, uint256, uint256, address, address
MIGRATOR 0x9ecd7472 → address
MINIMUM_DEPOSIT 0xf19451d8 → uint256
NONCE 0xe091dd1a → uint256
SHARES 0xc368803a → uint256
convertSharesToTokens 0x0cdebc9e → uint256
convertTokensToShares 0xca5cc0c2 → uint256
getLock 0xd68f4dd1 → uint256, address, uint256, uint256, uint256, uint256, uint256, uint256, address, address
getNumLockedTokens 0x783451e8 → uint256
getTokenAtIndex 0x97988dce → address
getTokenLockIDAtIndex 0x8ba74f17 → uint256
getTokenLocksLength 0xd060e175 → uint256
getTokenWhitelisterAtIndex 0xe52c4b7a → address
getTokenWhitelisterLength 0x00623ae3 → uint256
getTokenWhitelisterStatus 0xcf6dde4a → bool
getUserLockIDForTokenAtIndex 0x1c30ffb1 → uint256
getUserLockedTokenAtIndex 0x903df806 → address
getUserLockedTokensLength 0xa6dcb8de → uint256
getUserLocksForTokenLength 0xbb5ee001 → uint256
getWithdrawableShares 0xe04ab139 → uint256
getWithdrawableTokens 0xd323cdbf → uint256
getZeroFeeTokenAtIndex 0xb1d7655c → address
getZeroFeeTokensLength 0x03e1cdf4 → uint256
owner 0x8da5cb5b → address
testCondition 0x675187a3 → bool
tokenOnZeroFeeWhitelist 0x1d7065db → bool

Write Contract 16 functions

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

adminSetWhitelister 0x86de2fcb
address _user
bool _add
editZeroFeeWhitelist 0xb4540fa7
address _token
bool _add
incrementLock 0x3717dee7
uint256 _lockID
uint256 _amount
lock 0x1e6d99d1
address _token
tuple[] _lock_params
migrate 0x3e54bacb
uint256 _lockID
uint256 _option
payForFreeTokenLocks 0xa34df14f
address _token
relock 0xb2fb30cb
uint256 _lockID
uint256 _unlock_date
renounceOwnership 0x715018a6
No parameters
revokeCondition 0x5a5b8d9e
uint256 _lockID
setBlacklistContract 0x741af17e
address _contract
setFees 0xfc0633d0
uint256 _tokenFee
uint256 _freeLockingFee
address _feeAddress
address _freeLockingToken
setMigrator 0x23cf3118
address _migrator
splitLock 0x6588fc03
uint256 _lockID
uint256 _amount
transferLockOwnership 0x5a04fb69
uint256 _lockID
address _newOwner
transferOwnership 0xf2fde38b
address newOwner
withdraw 0x441a3e70
uint256 _lockID
uint256 _amount

Recent Transactions

No transactions found for this address