Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x88DD5C421f7B9FCdB83FD534bd83d22F8B80eA75
Balance 0 ETH
Nonce 1
Code Size 17022 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

17022 bytes
0x6080604052600436106102ae5760003560e01c806375151b6311610175578063a22cb465116100dc578063c87b56dd11610095578063dd0228f51161006f578063dd0228f51461086e578063e231db761461088e578063e985e9c5146108ae578063f2fde38b146108ce57600080fd5b8063c87b56dd14610818578063d0cb39c614610838578063db0ed6a01461084e57600080fd5b8063a22cb46514610760578063a4c0ed3614610780578063b002249d146107a0578063b88d4fde146107c2578063c3d2c3c1146107e2578063c7a647231461070a57600080fd5b8063889513521161012e57806388951352146106a157806389676073146106c15780638da5cb5b146106d757806395d89b41146106f557806398807d841461070a5780639b4140cc1461074057600080fd5b806375151b63146105f757806376a19e5b146106175780637c898895146106375780637c9097de14610657578063817b1cd21461066c57806386d740371461068157600080fd5b80634a374d4d116102195780635fa7b584116101d25780635fa7b5841461053757806362d76d06146105575780636352211e146105775780636c0360eb1461059757806370a08231146105ac578063715018a6146105e257600080fd5b80634a374d4d146104745780634f1ef286146104a157806352d1902d146104b45780635476bd72146104d757806355f804b3146104f75780635deb761f1461051757600080fd5b80631b23009d1161026b5780631b23009d146103b457806323b872dd146103d45780632dc075e1146103f45780633659cfe61461041457806342842e0e14610434578063441a3e701461045457600080fd5b806301ffc9a7146102b357806306fdde03146102e8578063081812fc1461030a578063095ea7b3146103425780630e3bc974146103645780630f14b4d614610387575b600080fd5b3480156102bf57600080fd5b506102d36102ce366004613531565b6108ee565b60405190151581526020015b60405180910390f35b3480156102f457600080fd5b506102fd610940565b6040516102df91906135a6565b34801561031657600080fd5b5061032a6103253660046135b9565b6109ce565b6040516001600160a01b0390911681526020016102df565b34801561034e57600080fd5b5061036261035d3660046135ee565b610a1f565b005b34801561037057600080fd5b50610379610af8565b6040516102df92919061368c565b34801561039357600080fd5b506103a76103a23660046136b1565b610c71565b6040516102df91906136cc565b3480156103c057600080fd5b506103626103cf3660046136b1565b610da1565b3480156103e057600080fd5b506103626103ef3660046136df565b610dcb565b34801561040057600080fd5b5061036261040f3660046135b9565b610e02565b34801561042057600080fd5b5061036261042f3660046136b1565b610fdc565b34801561044057600080fd5b5061036261044f3660046136df565b6110c4565b34801561046057600080fd5b5061036261046f36600461371b565b6110df565b34801561048057600080fd5b5061049461048f36600461373d565b61136a565b6040516102df91906137b1565b6103626104af3660046138e2565b6114db565b3480156104c057600080fd5b506104c96115ab565b6040519081526020016102df565b3480156104e357600080fd5b506103626104f236600461392f565b61165e565b34801561050357600080fd5b506103626105123660046139aa565b6117d3565b34801561052357600080fd5b5060d75461032a906001600160a01b031681565b34801561054357600080fd5b506103626105523660046136b1565b6117e8565b34801561056357600080fd5b506103626105723660046139eb565b6119ad565b34801561058357600080fd5b5061032a6105923660046135b9565b611a88565b3480156105a357600080fd5b506102fd611abe565b3480156105b857600080fd5b506104c96105c73660046136b1565b6001600160a01b0316600090815260d4602052604090205490565b3480156105ee57600080fd5b50610362611acb565b34801561060357600080fd5b506102d36106123660046136b1565b611adf565b34801561062357600080fd5b50610362610632366004613aae565b611b0f565b34801561064357600080fd5b5060d05461032a906001600160a01b031681565b34801561066357600080fd5b50610362611b63565b34801561067857600080fd5b5060d5546104c9565b34801561068d57600080fd5b5061036261069c3660046136b1565b611b9a565b3480156106ad57600080fd5b506103626106bc3660046139eb565b611d3a565b3480156106cd57600080fd5b506104c960d55481565b3480156106e357600080fd5b506097546001600160a01b031661032a565b34801561070157600080fd5b506102fd611d78565b34801561071657600080fd5b506104c96107253660046136b1565b6001600160a01b0316600090815260d6602052604090205490565b34801561074c57600080fd5b5060cf5461032a906001600160a01b031681565b34801561076c57600080fd5b5061036261077b366004613af8565b611d85565b34801561078c57600080fd5b5061036261079b366004613b2f565b611e1c565b3480156107ac57600080fd5b506107b5611eda565b6040516102df9190613b88565b3480156107ce57600080fd5b506103626107dd366004613b9b565b611f3c565b3480156107ee57600080fd5b5061032a6107fd3660046136b1565b60c9602052600090815260409020546001600160a01b031681565b34801561082457600080fd5b506102fd6108333660046135b9565b611f97565b34801561084457600080fd5b506104c960d15481565b34801561085a57600080fd5b50610362610869366004613c02565b61202b565b34801561087a57600080fd5b506103a76108893660046136b1565b612197565b34801561089a57600080fd5b506103626108a9366004613c97565b61228e565b3480156108ba57600080fd5b506102d36108c936600461392f565b6122c5565b3480156108da57600080fd5b506103626108e93660046136b1565b6122f3565b60006001600160e01b031982166380ac58cd60e01b148061091f57506001600160e01b03198216635b5e139f60e01b145b8061093a57506001600160e01b031982166301ffc9a760e01b145b92915050565b60cb805461094d90613cba565b80601f016020809104026020016040519081016040528092919081815260200182805461097990613cba565b80156109c65780601f1061099b576101008083540402835291602001916109c6565b820191906000526020600020905b8154815290600101906020018083116109a957829003601f168201915b505050505081565b600081815260d360205260408120546001600160a01b0316610a035760405163aefe60c360e01b815260040160405180910390fd5b50600090815260ce60205260409020546001600160a01b031690565b6000610a2a82611a88565b9050806001600160a01b0316836001600160a01b031603610a5e5760405163250fdee360e21b815260040160405180910390fd5b336001600160a01b03821614801590610a7e5750610a7c81336122c5565b155b15610a9c576040516379d1e58f60e01b815260040160405180910390fd5b600082815260ce602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b606080600060ca805490506001600160401b03811115610b1a57610b1a61382d565b604051908082528060200260200182016040528015610b43578160200160208202803683370190505b50905060005b60ca54811015610c085760ca8181548110610b6657610b66613cf4565b6000918252602090912001546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610bb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bdb9190613d0a565b828281518110610bed57610bed613cf4565b6020908102919091010152610c0181613d39565b9050610b49565b5060ca8181805480602002602001604051908101604052809291908181526020018280548015610c6157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c43575b5050505050915092509250509091565b60ca546060906000906001600160401b03811115610c9157610c9161382d565b604051908082528060200260200182016040528015610cba578160200160208202803683370190505b50905060005b60ca54811015610d9a5760c9600060ca8381548110610ce157610ce1613cf4565b6000918252602080832091909101546001600160a01b039081168452908301939093526040918201902054905163078a5a6b60e11b81528683166004820152911690630f14b4d690602401602060405180830381865afa158015610d49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6d9190613d0a565b828281518110610d7f57610d7f613cf4565b6020908102919091010152610d9381613d39565b9050610cc0565b5092915050565b610da9612369565b60d080546001600160a01b0319166001600160a01b0392909216919091179055565b610dd533826123c3565b610df2576040516379d1e58f60e01b815260040160405180910390fd5b610dfd838383612422565b505050565b8033610e0d82611a88565b6001600160a01b0316816001600160a01b031614610e3e576040516379d1e58f60e01b815260040160405180910390fd5b33610e48816125e7565b600084815260d26020526040902060020154600160801b90046001600160401b031615610e88576040516305d89a4360e11b815260040160405180910390fd5b600084815260d260205260408120600290810154610eb69190600160401b90046001600160401b0316613d52565b600086815260d260205260409020600201549091504290610ee19083906001600160401b0316613d86565b6001600160401b03161115610f095760405163ce14619d60e01b815260040160405180910390fd5b6000610f158242613d86565b600087815260d26020908152604080832060028101805467ffffffffffffffff60801b1916600160801b6001600160401b0388160217905560010180549084905533845260d6909252822080549394509092839290610f75908490613db1565b925050819055508060d56000828254610f8e9190613db1565b90915550506040516001600160401b0383168152879033907f07728a9afd9c9ac8253019aedb66f63530f4fdeb57357c7eb64c601b528e64579060200160405180910390a350505050505050565b6001600160a01b037f00000000000000000000000088dd5c421f7b9fcdb83fd534bd83d22f8b80ea7516300361102d5760405162461bcd60e51b815260040161102490613dc8565b60405180910390fd5b7f00000000000000000000000088dd5c421f7b9fcdb83fd534bd83d22f8b80ea756001600160a01b0316611076600080516020614202833981519152546001600160a01b031690565b6001600160a01b03161461109c5760405162461bcd60e51b815260040161102490613e14565b6110a581612699565b604080516000808252602082019092526110c1918391906126a1565b50565b610dfd83838360405180602001604052806000815250611f3c565b81336110ea82611a88565b6001600160a01b0316816001600160a01b03161461111b576040516379d1e58f60e01b815260040160405180910390fd5b33611125816125e7565b600085815260d260205260409020600201546001600160401b0316156111b457600085815260d26020526040812060020154600160801b90046001600160401b03169081900361118857604051635b89f32f60e11b815260040160405180910390fd5b42816001600160401b031611156111b257604051636e56f29360e01b815260040160405180910390fd5b505b600085815260d26020526040902054808511156111e457604051631e9acf1760e31b815260040160405180910390fd5b604051858152869033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060200160405180910390a38085036112f457600086815260d2602090815260408083208381556001808201859055600290910180546001600160c01b031916905560d3835281842080546001600160a01b031916905533845260d49092528220805491929091611281908490613db1565b9091555050600086815260ce60205260409020546001600160a01b0316156112c057600086815260ce6020526040902080546001600160a01b03191690555b604051869060009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a461130e565b6112fe8582613db1565b600087815260d260205260409020555b33600090815260d660205260408120805487929061132d908490613db1565b925050819055508460d560008282546113469190613db1565b909155505060cf54611362906001600160a01b0316338761280c565b505050505050565b60606000826001600160401b038111156113865761138661382d565b6040519080825280602002602001820160405280156113df57816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816113a45790505b50905060005b838110156114d357600085858381811061140157611401613cf4565b60209081029290920135600081815260d3909352604090922054919250506001600160a01b03166114455760405163aefe60c360e01b815260040160405180910390fd5b600081815260d26020908152604091829020825160a08101845281548152600182015492810192909252600201546001600160401b0380821693830193909352600160401b810483166060830152600160801b9004909116608082015283518490849081106114b6576114b6613cf4565b602002602001018190525050806114cc90613d39565b90506113e5565b509392505050565b6001600160a01b037f00000000000000000000000088dd5c421f7b9fcdb83fd534bd83d22f8b80ea751630036115235760405162461bcd60e51b815260040161102490613dc8565b7f00000000000000000000000088dd5c421f7b9fcdb83fd534bd83d22f8b80ea756001600160a01b031661156c600080516020614202833981519152546001600160a01b031690565b6001600160a01b0316146115925760405162461bcd60e51b815260040161102490613e14565b61159b82612699565b6115a7828260016126a1565b5050565b6000306001600160a01b037f00000000000000000000000088dd5c421f7b9fcdb83fd534bd83d22f8b80ea75161461164b5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401611024565b5060008051602061420283398151915290565b611666612369565b61166f82611adf565b156116bc5760405162461bcd60e51b815260206004820152601a60248201527f546f6b656e20697320616c726561647920737570706f727465640000000000006044820152606401611024565b6001600160a01b03828116600081815260c9602052604080822080549486166001600160a01b031995861617905560ca80546001810182559083527f42d72674974f694b5f5159593243114d38a5c39c89d6b62fee061ff523240ee1018054909416831790935591516370a0823160e01b81523060048201526370a0823190602401602060405180830381865afa15801561175b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177f9190613d0a565b111561178e5761178e82611b9a565b6040516001600160a01b0382811682528316907fdbf34b45b47a653cf4940cccbec765f72d4d63de3237306905bfc0ee28832362906020015b60405180910390a25050565b6117db612369565b60d8610dfd828483613ea6565b6117f0612369565b6117f981611adf565b61183e5760405162461bcd60e51b8152602060048201526016602482015275151bdad95b881a5cc81b9bdd081cdd5c1c1bdc9d195960521b6044820152606401611024565b6001600160a01b03808216600090815260c96020526040812080546001600160a01b03198116909155909116905b60ca5481101561196f57826001600160a01b031660ca828154811061189357611893613cf4565b6000918252602090912001546001600160a01b03160361195f5760ca80546118bd90600190613db1565b815481106118cd576118cd613cf4565b60009182526020909120015460ca80546001600160a01b0390921691839081106118f9576118f9613cf4565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060ca80548061193857611938613f66565b600082815260209020810160001990810180546001600160a01b031916905501905561196f565b61196881613d39565b905061186c565b506040516001600160a01b0382811682528316907f39dcd754ec63af4b82c4c569ff1b6b4e55a8038e6545844747e54f2f2d4e8e50906020016117c7565b60005b8151811015611a595760c960008383815181106119cf576119cf613cf4565b6020908102919091018101516001600160a01b0390811683529082019290925260409081016000205490516351cff8d960e01b81523360048201529116906351cff8d990602401600060405180830381600087803b158015611a3057600080fd5b505af1158015611a44573d6000803e3d6000fd5b5050505080611a5290613d39565b90506119b0565b5060405133907feeab3fd62be4cb59cbdc42d5b0f676a3597ff387b9a85e62330cc17c2a3603db90600090a250565b600081815260d360205260408120546001600160a01b03168061093a5760405163aefe60c360e01b815260040160405180910390fd5b60d8805461094d90613cba565b611ad3612369565b611add600061286f565b565b6001600160a01b03818116600090815260c96020526040812054909116611b0757600061093a565b600192915050565b60d7546001600160a01b03163314611b3a576040516379d1e58f60e01b815260040160405180910390fd5b60d75460cf54611b58916001600160a01b03918216911630856128c1565b610dfd8383836128f9565b600054610100900460ff16611b8a5760405162461bcd60e51b815260040161102490613f7c565b611b92612ba2565b611add612bd1565b611ba381611adf565b611be55760405162461bcd60e51b8152602060048201526013602482015272151bdad95b881b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401611024565b6040516370a0823160e01b815230600482015281906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611c2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c529190613d0a565b905060008111611ca45760405162461bcd60e51b815260206004820152601e60248201527f43616e6e6f742064697374726962757465207a65726f2062616c616e636500006044820152606401611024565b6001600160a01b03808416600090815260c96020526040902054611ccd9184811691168361280c565b6001600160a01b03808416600090815260c960205260408082205481516306f4a2cd60e41b81529151931692636f4a2cd09260048084019391929182900301818387803b158015611d1d57600080fd5b505af1158015611d31573d6000803e3d6000fd5b50505050505050565b60005b81518110156115a757611d68828281518110611d5b57611d5b613cf4565b6020026020010151611b9a565b611d7181613d39565b9050611d3d565b60cc805461094d90613cba565b336001600160a01b0383168103611daf576040516320c5195360e21b815260040160405180910390fd5b6001600160a01b03818116600081815260cd6020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b60cf546001600160a01b03163314801590611e3d5750611e3b33611adf565b155b15611e5b57604051637e5f934960e01b815260040160405180910390fd5b82600003611e7c57604051632a9ffab760e21b815260040160405180910390fd5b60cf546001600160a01b03163303611ecb57600080611e9d83850185613c97565b90925090508115611eb957611eb486838784612bf8565b611ec4565b611ec48686836128f9565b5050611ed4565b611ed433611b9a565b50505050565b606060ca805480602002602001604051908101604052809291908181526020018280548015611f3257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611f14575b5050505050905090565b611f4633836123c3565b611f63576040516379d1e58f60e01b815260040160405180910390fd5b611f6e848484612422565b611f7a84848484612fe7565b611ed457604051630433c1a560e41b815260040160405180910390fd5b606060d88054611fa690613cba565b80601f0160208091040260200160405190810160405280929190818152602001828054611fd290613cba565b801561201f5780601f10611ff45761010080835404028352916020019161201f565b820191906000526020600020905b81548152906001019060200180831161200257829003601f168201915b50505050509050919050565b600054610100900460ff161580801561204b5750600054600160ff909116105b806120655750303b158015612065575060005460ff166001145b6120c85760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401611024565b6000805460ff1916600117905580156120eb576000805461ff0019166101001790555b6120f3611b63565b60cb6120ff8782613fc7565b5060cc61210c8682613fc7565b5060cf80546001600160a01b038087166001600160a01b03199283161790925560d0805486841690831617905560d78054928516929091169190911790558015611362576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b60d15460609060006121be846001600160a01b0316600090815260d4602052604090205490565b9050600080826001600160401b038111156121db576121db61382d565b604051908082528060200260200182016040528015612204578160200160208202803683370190505b50905060015b84811161227557600081815260d360205260409020546001600160a01b03808916911603612265578082848151811061224557612245613cf4565b60209081029190910101528261225a81613d39565b935050838314612275575b61226e81613d39565b905061220a565b5082821461228557612285614086565b95945050505050565b806001600160401b03166000036122b857604051635bf6323f60e11b815260040160405180910390fd5b6115a73383600084612bf8565b6001600160a01b03918216600090815260cd6020908152604080832093909416825291909152205460ff1690565b6122fb612369565b6001600160a01b0381166123605760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401611024565b6110c18161286f565b6097546001600160a01b03163314611add5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611024565b6000806123cf83611a88565b9050806001600160a01b0316846001600160a01b031614806123f657506123f681856122c5565b8061241a5750836001600160a01b031661240f846109ce565b6001600160a01b0316145b949350505050565b61242b81611a88565b6001600160a01b0316836001600160a01b03161461245b5760405162a1148160e81b815260040160405180910390fd5b6001600160a01b03821661248257604051633a954ecd60e21b815260040160405180910390fd5b600081815260ce6020526040902080546001600160a01b03191690556124a7836125e7565b6124b0826125e7565b600081815260d260205260408120600181015490546124cf919061409c565b6001600160a01b038516600090815260d660205260408120805492935083929091906124fc908490613db1565b90915550506001600160a01b038316600090815260d660205260408120805483929061252990849061409c565b90915550506001600160a01b038416600090815260d460205260408120805460019290612557908490613db1565b90915550506001600160a01b038316600090815260d46020526040812080546001929061258590849061409c565b9091555050600082815260d3602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b60005b60ca548110156115a75760c9600060ca838154811061260b5761260b613cf4565b6000918252602080832091909101546001600160a01b039081168452908301939093526040918201902054905163632447c960e01b8152848316600482015291169063632447c990602401600060405180830381600087803b15801561267057600080fd5b505af1158015612684573d6000803e3d6000fd5b505050508061269290613d39565b90506125ea565b6110c1612369565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156126d457610dfd836130e9565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561272e575060408051601f3d908101601f1916820190925261272b91810190613d0a565b60015b6127915760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201526d6f6e206973206e6f74205555505360901b6064820152608401611024565b60008051602061420283398151915281146128005760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f786044820152681a58589b195555525160ba1b6064820152608401611024565b50610dfd838383613185565b6040516001600160a01b038316602482015260448101829052610dfd90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526131aa565b609780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6040516001600160a01b0380851660248301528316604482015260648101829052611ed49085906323b872dd60e01b90608401612838565b82612903816125e7565b60d054604051632fe9ced760e11b8152600481018590526001600160401b03841660248201526000916001600160a01b031690635fd39dae90604401602060405180830381865afa15801561295c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129809190613d0a565b9050600061298e828661409c565b90506000846001600160401b03166000036129aa5760006129ac565b425b9050600060d15460016129bf919061409c565b6040805160a08101825289815260208082018881526001600160401b038088168486019081528c82166060860190815260006080870181815289825260d28752888220975188559451600180890191909155925160029097018054925195518516600160801b0267ffffffffffffffff60801b19968616600160401b026fffffffffffffffffffffffffffffffff19909416989095169790971791909117939093169190911790935560d3825283812080546001600160a01b038f166001600160a01b03199091168117909155815260d490915291822080549394509092909190612aab90849061409c565b909155505060d18054906000612ac083613d39565b90915550506001600160a01b038816600090815260d6602052604081208054859290612aed90849061409c565b925050819055508260d56000828254612b06919061409c565b909155505060408051888152602081018690526001600160401b03881681830152905182916001600160a01b038b16917f0bd0744e02d09668fbdaf7745e9ae454f27484fcf18308ebc65d8508f21d59129181900360600190a360405181906001600160a01b038a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050505050505050565b600054610100900460ff16612bc95760405162461bcd60e51b815260040161102490613f7c565b611add61327f565b600054610100900460ff16611add5760405162461bcd60e51b815260040161102490613f7c565b8284612c0382611a88565b6001600160a01b0316816001600160a01b031614612c34576040516379d1e58f60e01b815260040160405180910390fd5b85612c3e816125e7565b600086815260d260205260409020600201546001600160401b03600160401b8204811691600160801b900416801580612c7f575042816001600160401b0316115b8015612c9c5750816001600160401b0316866001600160401b0316105b15612cba57604051635bf6323f60e11b815260040160405180910390fd5b600088815260d2602052604081205490612cd4898361409c565b60d054604051632fe9ced760e11b8152600481018390526001600160401b038b1660248201529192506000916001600160a01b0390911690635fd39dae90604401602060405180830381865afa158015612d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d569190613d0a565b90508915612d705760008b815260d2602052604090208290555b846001600160401b0316896001600160401b031614612dc35760008b815260d26020526040902060020180546fffffffffffffffff00000000000000001916600160401b6001600160401b038c16021790555b6001600160401b03891615612e025760008b815260d260205260409020600201805467ffffffffffffffff1916426001600160401b0316179055612e32565b6001600160401b03851615612e325760008b815260d260205260409020600201805467ffffffffffffffff191690555b60008b815260d26020526040902060020154600160801b90046001600160401b031615612e7d5760008b815260d260205260409020600201805467ffffffffffffffff60801b191690555b60008b815260d26020526040812060010154612e99908561409c565b612ea3838561409c565b612ead91906140b4565b90506000811315612f04576001600160a01b038d16600090815260d6602052604081208054839290612ee090849061409c565b925050819055508060d56000828254612ef9919061409c565b90915550612f6a9050565b6000811215612f6a57612f19816000196140f3565b6001600160a01b038e16600090815260d6602052604081208054909190612f41908490613db1565b90915550612f539050816000196140f3565b60d56000828254612f649190613db1565b90915550505b60008c815260d2602090815260409182902060010184905581518581529081018490526001600160401b038c16918101919091528c906001600160a01b038f16907f8dbb057b85ca29d98e176658d7a7843f454205d9d623e5f2e7c93919334b09959060600160405180910390a350505050505050505050505050565b60006001600160a01b0384163b156130de57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061302b903390899088908890600401614178565b6020604051808303816000875af1925050508015613066575060408051601f3d908101601f19168201909252613063918101906141ab565b60015b6130c4573d808015613094576040519150601f19603f3d011682016040523d82523d6000602084013e613099565b606091505b5080516000036130bc57604051630433c1a560e41b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061241a565b506001949350505050565b6001600160a01b0381163b6131565760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401611024565b60008051602061420283398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b61318e836132af565b60008251118061319b5750805b15610dfd57611ed483836132ef565b60006131ff826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661331b9092919063ffffffff16565b905080516000148061322057508080602001905181019061322091906141c8565b610dfd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611024565b600054610100900460ff166132a65760405162461bcd60e51b815260040161102490613f7c565b611add3361286f565b6132b8816130e9565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606061331483836040518060600160405280602781526020016142226027913961332a565b9392505050565b606061241a84846000856133a2565b6060600080856001600160a01b03168560405161334791906141e5565b600060405180830381855af49150503d8060008114613382576040519150601f19603f3d011682016040523d82523d6000602084013e613387565b606091505b50915091506133988683838761347d565b9695505050505050565b6060824710156134035760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611024565b600080866001600160a01b0316858760405161341f91906141e5565b60006040518083038185875af1925050503d806000811461345c576040519150601f19603f3d011682016040523d82523d6000602084013e613461565b606091505b50915091506134728783838761347d565b979650505050505050565b606083156134ec5782516000036134e5576001600160a01b0385163b6134e55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611024565b508161241a565b61241a83838151156135015781518083602001fd5b8060405162461bcd60e51b815260040161102491906135a6565b6001600160e01b0319811681146110c157600080fd5b60006020828403121561354357600080fd5b81356133148161351b565b60005b83811015613569578181015183820152602001613551565b83811115611ed45750506000910152565b6000815180845261359281602086016020860161354e565b601f01601f19169290920160200192915050565b602081526000613314602083018461357a565b6000602082840312156135cb57600080fd5b5035919050565b80356001600160a01b03811681146135e957600080fd5b919050565b6000806040838503121561360157600080fd5b61360a836135d2565b946020939093013593505050565b600081518084526020808501945080840160005b838110156136515781516001600160a01b03168752958201959082019060010161362c565b509495945050505050565b600081518084526020808501945080840160005b8381101561365157815187529582019590820190600101613670565b60408152600061369f6040830185613618565b8281036020840152612285818561365c565b6000602082840312156136c357600080fd5b613314826135d2565b602081526000613314602083018461365c565b6000806000606084860312156136f457600080fd5b6136fd846135d2565b925061370b602085016135d2565b9150604084013590509250925092565b6000806040838503121561372e57600080fd5b50508035926020909101359150565b6000806020838503121561375057600080fd5b82356001600160401b038082111561376757600080fd5b818501915085601f83011261377b57600080fd5b81358181111561378a57600080fd5b8660208260051b850101111561379f57600080fd5b60209290920196919550909350505050565b602080825282518282018190526000919060409081850190868401855b82811015613820578151805185528681015187860152858101516001600160401b0390811687870152606080830151821690870152608091820151169085015260a090930192908501906001016137ce565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561386b5761386b61382d565b604052919050565b600082601f83011261388457600080fd5b81356001600160401b0381111561389d5761389d61382d565b6138b0601f8201601f1916602001613843565b8181528460208386010111156138c557600080fd5b816020850160208301376000918101602001919091529392505050565b600080604083850312156138f557600080fd5b6138fe836135d2565b915060208301356001600160401b0381111561391957600080fd5b61392585828601613873565b9150509250929050565b6000806040838503121561394257600080fd5b61394b836135d2565b9150613959602084016135d2565b90509250929050565b60008083601f84011261397457600080fd5b5081356001600160401b0381111561398b57600080fd5b6020830191508360208285010111156139a357600080fd5b9250929050565b600080602083850312156139bd57600080fd5b82356001600160401b038111156139d357600080fd5b6139df85828601613962565b90969095509350505050565b600060208083850312156139fe57600080fd5b82356001600160401b0380821115613a1557600080fd5b818501915085601f830112613a2957600080fd5b813581811115613a3b57613a3b61382d565b8060051b9150613a4c848301613843565b8181529183018401918481019088841115613a6657600080fd5b938501935b83851015613a8b57613a7c856135d2565b82529385019390850190613a6b565b98975050505050505050565b80356001600160401b03811681146135e957600080fd5b600080600060608486031215613ac357600080fd5b613acc846135d2565b925060208401359150613ae160408501613a97565b90509250925092565b80151581146110c157600080fd5b60008060408385031215613b0b57600080fd5b613b14836135d2565b91506020830135613b2481613aea565b809150509250929050565b60008060008060608587031215613b4557600080fd5b613b4e856135d2565b93506020850135925060408501356001600160401b03811115613b7057600080fd5b613b7c87828801613962565b95989497509550505050565b6020815260006133146020830184613618565b60008060008060808587031215613bb157600080fd5b613bba856135d2565b9350613bc8602086016135d2565b92506040850135915060608501356001600160401b03811115613bea57600080fd5b613bf687828801613873565b91505092959194509250565b600080600080600060a08688031215613c1a57600080fd5b85356001600160401b0380821115613c3157600080fd5b613c3d89838a01613873565b96506020880135915080821115613c5357600080fd5b50613c6088828901613873565b945050613c6f604087016135d2565b9250613c7d606087016135d2565b9150613c8b608087016135d2565b90509295509295909350565b60008060408385031215613caa57600080fd5b8235915061395960208401613a97565b600181811c90821680613cce57607f821691505b602082108103613cee57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613d1c57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600060018201613d4b57613d4b613d23565b5060010190565b60006001600160401b0380841680613d7a57634e487b7160e01b600052601260045260246000fd5b92169190910492915050565b60006001600160401b03808316818516808303821115613da857613da8613d23565b01949350505050565b600082821015613dc357613dc3613d23565b500390565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b601f821115610dfd57600081815260208120601f850160051c81016020861015613e875750805b601f850160051c820191505b8181101561136257828155600101613e93565b6001600160401b03831115613ebd57613ebd61382d565b613ed183613ecb8354613cba565b83613e60565b6000601f841160018114613f055760008515613eed5750838201355b600019600387901b1c1916600186901b178355613f5f565b600083815260209020601f19861690835b82811015613f365786850135825560209485019460019092019101613f16565b5086821015613f535760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b634e487b7160e01b600052603160045260246000fd5b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b81516001600160401b03811115613fe057613fe061382d565b613ff481613fee8454613cba565b84613e60565b602080601f83116001811461402957600084156140115750858301515b600019600386901b1c1916600185901b178555611362565b600085815260208120601f198616915b8281101561405857888601518255948401946001909101908401614039565b50858210156140765787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052600160045260246000fd5b600082198211156140af576140af613d23565b500190565b60008083128015600160ff1b8501841216156140d2576140d2613d23565b6001600160ff1b03840183138116156140ed576140ed613d23565b50500390565b60006001600160ff1b038184138284138082168684048611161561411957614119613d23565b600160ff1b600087128281168783058912161561413857614138613d23565b6000871292508782058712848416161561415457614154613d23565b8785058712818416161561416a5761416a613d23565b505050929093029392505050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906133989083018461357a565b6000602082840312156141bd57600080fd5b81516133148161351b565b6000602082840312156141da57600080fd5b815161331481613aea565b600082516141f781846020870161354e565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212204ed04bffd45aaa720fc9da4fe2029f1efa20993a67c6dfb9ea4e5366ab906ffd64736f6c634300080f0033

Verified Source Code Full Match

Compiler: v0.8.15+commit.e14f2714 EVM: london Optimization: Yes (200 runs)
RewardsPool.sol 121 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

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

import "./interfaces/IRewardsPoolController.sol";
import "./interfaces/IERC677.sol";

/**
 * @title RewardsPool
 * @notice Handles reward distribution for a single asset
 * @dev rewards can only be positive (user balances can only increase)
 */
contract RewardsPool {
    using SafeERC20 for IERC677;

    IERC677 public immutable token;
    IRewardsPoolController public immutable controller;

    uint256 public rewardPerToken;
    uint256 public totalRewards;
    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public userRewards;

    event Withdraw(address indexed account, uint256 amount);
    event DistributeRewards(address indexed sender, uint256 amountStaked, uint256 amount);

    constructor(address _controller, address _token) {
        controller = IRewardsPoolController(_controller);
        token = IERC677(_token);
    }

    /**
     * @notice returns an account's total withdrawable rewards (principal balance + newly earned rewards)
     * @param _account account address
     * @return account's total unclaimed rewards
     **/
    function withdrawableRewards(address _account) public view virtual returns (uint256) {
        return
            (controller.staked(_account) * (rewardPerToken - userRewardPerTokenPaid[_account])) /
            1e18 +
            userRewards[_account];
    }

    /**
     * @notice withdraws an account's earned rewards
     **/
    function withdraw() external {
        _withdraw(msg.sender);
    }

    /**
     * @notice withdraws an account's earned rewards
     * @dev used by RewardsPoolController
     * @param _account account to withdraw for
     **/
    function withdraw(address _account) external {
        require(msg.sender == address(controller), "Controller only");
        _withdraw(_account);
    }

    /**
     * @notice ERC677 implementation that proxies reward distribution
     **/
    function onTokenTransfer(
        address,
        uint256,
        bytes calldata
    ) external {
        require(msg.sender == address(token), "Only callable by token");
        distributeRewards();
    }

    /**
     * @notice distributes new rewards that have been deposited
     **/
    function distributeRewards() public virtual {
        require(controller.totalStaked() > 0, "Cannot distribute when nothing is staked");
        uint256 toDistribute = token.balanceOf(address(this)) - totalRewards;
        totalRewards += toDistribute;
        _updateRewardPerToken(toDistribute);
        emit DistributeRewards(msg.sender, controller.totalStaked(), toDistribute);
    }

    /**
     * @notice updates an account's principal reward balance
     * @param _account account address
     **/
    function updateReward(address _account) public virtual {
        uint256 newRewards = withdrawableRewards(_account) - userRewards[_account];
        if (newRewards > 0) {
            userRewards[_account] += newRewards;
        }
        userRewardPerTokenPaid[_account] = rewardPerToken;
    }

    /**
     * @notice withdraws rewards for an account
     * @param _account account to withdraw for
     **/
    function _withdraw(address _account) internal virtual {
        uint256 toWithdraw = withdrawableRewards(_account);
        if (toWithdraw > 0) {
            updateReward(_account);
            userRewards[_account] -= toWithdraw;
            totalRewards -= toWithdraw;
            token.safeTransfer(_account, toWithdraw);
            emit Withdraw(_account, toWithdraw);
        }
    }

    /**
     * @notice updates rewardPerToken
     * @param _reward deposited reward amount
     **/
    function _updateRewardPerToken(uint256 _reward) internal {
        uint256 totalStaked = controller.totalStaked();
        require(totalStaked > 0, "Staked amount must be > 0");
        rewardPerToken += ((_reward * 1e18) / totalStaked);
    }
}
SDLPool.sol 673 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol";

import "../base/RewardsPoolController.sol";
import "../interfaces/IBoostController.sol";
import "../interfaces/IERC721Receiver.sol";

/**
 * @title SDL Pool
 * @notice Allows users to stake/lock SDL tokens and receive a percentage of the protocol's earned rewards
 */
contract SDLPool is RewardsPoolController, IERC721Upgradeable, IERC721MetadataUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;

    struct Lock {
        uint256 amount;
        uint256 boostAmount;
        uint64 startTime;
        uint64 duration;
        uint64 expiry;
    }

    string public name;
    string public symbol;

    mapping(address => mapping(address => bool)) private operatorApprovals;
    mapping(uint256 => address) private tokenApprovals;

    IERC20Upgradeable public sdlToken;
    IBoostController public boostController;

    uint256 public lastLockId;
    mapping(uint256 => Lock) private locks;
    mapping(uint256 => address) private lockOwners;
    mapping(address => uint256) private balances;

    uint256 public totalEffectiveBalance;
    mapping(address => uint256) private effectiveBalances;

    address public delegatorPool;

    string public baseURI;

    event InitiateUnlock(address indexed owner, uint256 indexed lockId, uint64 expiry);
    event Withdraw(address indexed owner, uint256 indexed lockId, uint256 amount);
    event CreateLock(
        address indexed owner,
        uint256 indexed lockId,
        uint256 amount,
        uint256 boostAmount,
        uint64 lockingDuration
    );
    event UpdateLock(
        address indexed owner,
        uint256 indexed lockId,
        uint256 amount,
        uint256 boostAmount,
        uint64 lockingDuration
    );

    error SenderNotAuthorized();
    error InvalidLockId();
    error InvalidValue();
    error InvalidLockingDuration();
    error InvalidParams();
    error TransferFromIncorrectOwner();
    error TransferToZeroAddress();
    error TransferToNonERC721Implementer();
    error ApprovalToCurrentOwner();
    error ApprovalToCaller();
    error UnauthorizedToken();
    error TotalDurationNotElapsed();
    error HalfDurationNotElapsed();
    error InsufficientBalance();
    error UnlockNotInitiated();
    error DuplicateContract();
    error ContractNotFound();
    error UnlockAlreadyInitiated();

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /**
     * @notice initializes contract
     * @param _name name of the staking derivative token
     * @param _symbol symbol of the staking derivative token
     * @param _boostController address of the boost controller
     * @param _delegatorPool address of the old contract this one will replace
     **/
    function initialize(
        string memory _name,
        string memory _symbol,
        address _sdlToken,
        address _boostController,
        address _delegatorPool
    ) public initializer {
        __RewardsPoolController_init();
        name = _name;
        symbol = _symbol;
        sdlToken = IERC20Upgradeable(_sdlToken);
        boostController = IBoostController(_boostController);
        delegatorPool = _delegatorPool;
    }

    /**
     * @notice reverts if `_owner` is not the owner of `_lockId`
     **/
    modifier onlyLockOwner(uint256 _lockId, address _owner) {
        if (_owner != ownerOf(_lockId)) revert SenderNotAuthorized();
        _;
    }

    /**
     * @notice returns the effective stake balance of an account
     * @dev the effective stake balance includes the actual amount of tokens an
     * account has staked across all locks plus any applicable boost gained by locking
     * @param _account address of account
     * @return effective stake balance
     **/
    function effectiveBalanceOf(address _account) external view returns (uint256) {
        return effectiveBalances[_account];
    }

    /**
     * @notice returns the number of locks owned by an account
     * @param _account address of account
     * @return total number of locks owned by account
     **/
    function balanceOf(address _account) public view returns (uint256) {
        return balances[_account];
    }

    /**
     * @notice returns the owner of a lock
     * @dev reverts if `_lockId` is invalid
     * @param _lockId id of the lock
     * @return lock owner
     **/
    function ownerOf(uint256 _lockId) public view returns (address) {
        address owner = lockOwners[_lockId];
        if (owner == address(0)) revert InvalidLockId();
        return owner;
    }

    /**
     * @notice returns the list of locks that corresponds to `_lockIds`
     * @dev reverts if any lockId is invalid
     * @param _lockIds list of lock ids
     * @return list of locks
     **/
    function getLocks(uint256[] calldata _lockIds) external view returns (Lock[] memory) {
        Lock[] memory retLocks = new Lock[](_lockIds.length);

        for (uint256 i = 0; i < _lockIds.length; ++i) {
            uint256 lockId = _lockIds[i];
            if (lockOwners[lockId] == address(0)) revert InvalidLockId();
            retLocks[i] = locks[lockId];
        }

        return retLocks;
    }

    /**
     * @notice returns a list of lockIds owned by an account
     * @param _owner address of account
     * @return list of lockIds
     **/
    function getLockIdsByOwner(address _owner) external view returns (uint256[] memory) {
        uint256 maxLockId = lastLockId;
        uint256 lockCount = balanceOf(_owner);
        uint256 lockIdsFound;
        uint256[] memory lockIds = new uint256[](lockCount);

        for (uint256 i = 1; i <= maxLockId; ++i) {
            if (lockOwners[i] == _owner) {
                lockIds[lockIdsFound] = i;
                lockIdsFound++;
                if (lockIdsFound == lockCount) break;
            }
        }

        assert(lockIdsFound == lockCount);

        return lockIds;
    }

    /**
     * @notice ERC677 implementation to stake/lock SDL tokens or distribute rewards
     * @dev
     * - will update/create a lock if the token transferred is SDL or will distribute rewards otherwise
     *
     * For Non-SDL:
     * - reverts if token is unsupported
     *
     * For SDL:
     * - set lockId to 0 to create a new lock or set lockId to > 0 to stake more into an existing lock
     * - set lockingDuration to 0 to stake without locking or set lockingDuration to > 0 to lock for an amount
     *   time in seconds
     * - see _updateLock() for more details on updating an existing lock or _createLock() for more details on
     *   creating a new lock
     * @param _sender of the stake
     * @param _value of the token transfer
     * @param _calldata encoded lockId (uint256) and lockingDuration (uint64)
     **/
    function onTokenTransfer(
        address _sender,
        uint256 _value,
        bytes calldata _calldata
    ) external override {
        if (msg.sender != address(sdlToken) && !isTokenSupported(msg.sender)) revert UnauthorizedToken();

        if (_value == 0) revert InvalidValue();

        if (msg.sender == address(sdlToken)) {
            (uint256 lockId, uint64 lockingDuration) = abi.decode(_calldata, (uint256, uint64));
            if (lockId > 0) {
                _updateLock(_sender, lockId, _value, lockingDuration);
            } else {
                _createLock(_sender, _value, lockingDuration);
            }
        } else {
            distributeToken(msg.sender);
        }
    }

    /**
     * @notice extends the locking duration of a lock
     * @dev
     * - reverts if `_lockId` is invalid or sender is not owner of lock
     * - reverts if `_lockingDuration` is less than current locking duration of lock
     * - reverts if `_lockingDuration` is 0 or exceeds the maximum
     * @param _lockId id of lock
     * @param _lockingDuration new locking duration to set
     **/
    function extendLockDuration(uint256 _lockId, uint64 _lockingDuration) external {
        if (_lockingDuration == 0) revert InvalidLockingDuration();
        _updateLock(msg.sender, _lockId, 0, _lockingDuration);
    }

    /**
     * @notice initiates the unlock period for a lock
     * @dev
     * - at least half of the locking duration must have elapsed to initiate the unlock period
     * - the unlock period consists of half of the locking duration
     * - boost will be set to 0 upon initiation of the unlock period
     *
     * - reverts if `_lockId` is invalid or sender is not owner of lock
     * - reverts if a minimum of half the locking duration has not elapsed
     * @param _lockId id of lock
     **/
    function initiateUnlock(uint256 _lockId) external onlyLockOwner(_lockId, msg.sender) updateRewards(msg.sender) {
        if (locks[_lockId].expiry != 0) revert UnlockAlreadyInitiated();
        uint64 halfDuration = locks[_lockId].duration / 2;
        if (locks[_lockId].startTime + halfDuration > block.timestamp) revert HalfDurationNotElapsed();

        uint64 expiry = uint64(block.timestamp) + halfDuration;
        locks[_lockId].expiry = expiry;

        uint256 boostAmount = locks[_lockId].boostAmount;
        locks[_lockId].boostAmount = 0;
        effectiveBalances[msg.sender] -= boostAmount;
        totalEffectiveBalance -= boostAmount;

        emit InitiateUnlock(msg.sender, _lockId, expiry);
    }

    /**
     * @notice withdraws unlocked SDL
     * @dev
     * - SDL can only be withdrawn if unlocked (once the unlock period has elapsed or if it was never
     *   locked in the first place)
     * - reverts if `_lockId` is invalid or sender is not owner of lock
     * - reverts if not unlocked
     * - reverts if `_amount` exceeds the amount staked in the lock
     * @param _lockId id of the lock
     * @param _amount amount to withdraw from the lock
     **/
    function withdraw(uint256 _lockId, uint256 _amount)
        external
        onlyLockOwner(_lockId, msg.sender)
        updateRewards(msg.sender)
    {
        if (locks[_lockId].startTime != 0) {
            uint64 expiry = locks[_lockId].expiry;
            if (expiry == 0) revert UnlockNotInitiated();
            if (expiry > block.timestamp) revert TotalDurationNotElapsed();
        }

        uint256 baseAmount = locks[_lockId].amount;
        if (_amount > baseAmount) revert InsufficientBalance();

        emit Withdraw(msg.sender, _lockId, _amount);

        if (_amount == baseAmount) {
            delete locks[_lockId];
            delete lockOwners[_lockId];
            balances[msg.sender] -= 1;
            if (tokenApprovals[_lockId] != address(0)) delete tokenApprovals[_lockId];
            emit Transfer(msg.sender, address(0), _lockId);
        } else {
            locks[_lockId].amount = baseAmount - _amount;
        }

        effectiveBalances[msg.sender] -= _amount;
        totalEffectiveBalance -= _amount;

        sdlToken.safeTransfer(msg.sender, _amount);
    }

    /**
     * @notice transfers a lock between accounts
     * @dev reverts if sender is not the owner of and not approved to transfer the lock
     * @param _from address to transfer from
     * @param _to address to transfer to
     * @param _lockId id of lock to transfer
     **/
    function transferFrom(
        address _from,
        address _to,
        uint256 _lockId
    ) external {
        if (!_isApprovedOrOwner(msg.sender, _lockId)) revert SenderNotAuthorized();
        _transfer(_from, _to, _lockId);
    }

    /**
     * @notice transfers a lock between accounts and validates that the receiver supports ERC721
     * @dev
     * - calls onERC721Received on `_to` if it is a contract or reverts if it is a contract
     *   and does not implemement onERC721Received
     * - reverts if sender is not the owner of and not approved to transfer the lock
     * - reverts if `_lockId` is invalid
     * @param _from address to transfer from
     * @param _to address to transfer to
     * @param _lockId id of lock to transfer
     **/
    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _lockId
    ) external {
        safeTransferFrom(_from, _to, _lockId, "");
    }

    /**
     * @notice transfers a lock between accounts and validates that the receiver supports ERC721
     * @dev
     * - calls onERC721Received on `_to` if it is a contract or reverts if it is a contract
     *   and does not implemement onERC721Received
     * - reverts if sender is not the owner of and not approved to transfer the lock
     * - reverts if `_lockId` is invalid
     * @param _from address to transfer from
     * @param _to address to transfer to
     * @param _lockId id of lock to transfer
     * @param _data optional data to pass to receiver
     **/
    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _lockId,
        bytes memory _data
    ) public {
        if (!_isApprovedOrOwner(msg.sender, _lockId)) revert SenderNotAuthorized();
        _transfer(_from, _to, _lockId);
        if (!_checkOnERC721Received(_from, _to, _lockId, _data)) revert TransferToNonERC721Implementer();
    }

    /**
     * @notice approves `_to` to transfer `_lockId` to another address
     * @dev
     * - approval is revoked on transfer and can also be revoked by approving zero address
     * - reverts if sender is not owner of lock and not an approved operator for the owner
     * - reverts if `_to` is owner of lock
     * - reverts if `_lockId` is invalid
     * @param _to address approved to transfer
     * @param _lockId id of lock
     **/
    function approve(address _to, uint256 _lockId) external {
        address owner = ownerOf(_lockId);

        if (_to == owner) revert ApprovalToCurrentOwner();
        if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) revert SenderNotAuthorized();

        tokenApprovals[_lockId] = _to;
        emit Approval(owner, _to, _lockId);
    }

    /**
     * @notice returns the address approved to transfer a lock
     * @param _lockId id of lock
     * @return approved address
     **/
    function getApproved(uint256 _lockId) public view returns (address) {
        if (lockOwners[_lockId] == address(0)) revert InvalidLockId();

        return tokenApprovals[_lockId];
    }

    /**
     * @notice approves _operator to transfer all tokens owned by sender
     * @dev
     * - approval will not be revoked until this function is called again with
     *   `_approved` set to false
     * - reverts if sender is `_operator`
     * @param _operator address to approve/unapprove
     * @param _approved whether address is approved or not
     **/
    function setApprovalForAll(address _operator, bool _approved) external {
        address owner = msg.sender;
        if (owner == _operator) revert ApprovalToCaller();

        operatorApprovals[owner][_operator] = _approved;
        emit ApprovalForAll(owner, _operator, _approved);
    }

    /**
     * @notice returns whether `_operator` is approved to transfer all tokens owned by `_owner`
     * @param _owner owner of tokens
     * @param _operator address approved to transfer
     * @return whether address is approved or not
     **/
    function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
        return operatorApprovals[_owner][_operator];
    }

    /**
     * @notice returns an account's staked amount for use by reward pools
     * controlled by this contract
     * @param _account account address
     * @return account's staked amount
     */
    function staked(address _account) external view override returns (uint256) {
        return effectiveBalances[_account];
    }

    /**
     * @notice returns the total staked amount for use by reward pools
     * controlled by this contract
     * @return total staked amount
     */
    function totalStaked() external view override returns (uint256) {
        return totalEffectiveBalance;
    }

    /**
     * @notice returns whether this contract supports an interface
     * @param _interfaceId id of interface
     * @return whether contract supports interface or not
     */
    function supportsInterface(bytes4 _interfaceId) external view returns (bool) {
        return
            _interfaceId == type(IERC721Upgradeable).interfaceId ||
            _interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
            _interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev returns the URI for a token
     */
    function tokenURI(uint256) external view returns (string memory) {
        return baseURI;
    }

    /**
     * @dev sets the base URI for all tokens
     */
    function setBaseURI(string calldata _baseURI) external onlyOwner {
        baseURI = _baseURI;
    }

    /**
     * @notice sets the boost controller
     * @dev this contract handles boost calculations for locking SDL
     * @param _boostController address of boost controller
     */
    function setBoostController(address _boostController) external onlyOwner {
        boostController = IBoostController(_boostController);
    }

    /**
     * @notice used by the delegator pool to migrate user stakes to this contract
     * @dev
     * - creates a new lock to represent the migrated stake
     * - reverts if `_lockingDuration` exceeds maximum
     * @param _sender owner of lock
     * @param _amount amount to stake
     * @param _lockingDuration duration of lock
     */
    function migrate(
        address _sender,
        uint256 _amount,
        uint64 _lockingDuration
    ) external {
        if (msg.sender != delegatorPool) revert SenderNotAuthorized();
        sdlToken.safeTransferFrom(delegatorPool, address(this), _amount);
        _createLock(_sender, _amount, _lockingDuration);
    }

    /**
     * @notice creates a new lock
     * @dev reverts if `_lockingDuration` exceeds maximum
     * @param _sender owner of lock
     * @param _amount amount to stake
     * @param _lockingDuration duration of lock
     */
    function _createLock(
        address _sender,
        uint256 _amount,
        uint64 _lockingDuration
    ) private updateRewards(_sender) {
        uint256 boostAmount = boostController.getBoostAmount(_amount, _lockingDuration);
        uint256 totalAmount = _amount + boostAmount;
        uint64 startTime = _lockingDuration != 0 ? uint64(block.timestamp) : 0;
        uint256 lockId = lastLockId + 1;

        locks[lockId] = Lock(_amount, boostAmount, startTime, _lockingDuration, 0);
        lockOwners[lockId] = _sender;
        balances[_sender] += 1;
        lastLockId++;

        effectiveBalances[_sender] += totalAmount;
        totalEffectiveBalance += totalAmount;

        emit CreateLock(_sender, lockId, _amount, boostAmount, _lockingDuration);
        emit Transfer(address(0), _sender, lockId);
    }

    /**
     * @notice updates an existing lock
     * @dev
     * - reverts if `_lockId` is invalid
     * - reverts if `_lockingDuration` is less than current locking duration of lock
     * - reverts if `_lockingDuration` exceeds maximum
     * @param _sender owner of lock
     * @param _lockId id of lock
     * @param _amount additional amount to stake
     * @param _lockingDuration duration of lock
     */
    function _updateLock(
        address _sender,
        uint256 _lockId,
        uint256 _amount,
        uint64 _lockingDuration
    ) private onlyLockOwner(_lockId, _sender) updateRewards(_sender) {
        uint64 curLockingDuration = locks[_lockId].duration;
        uint64 curExpiry = locks[_lockId].expiry;
        if ((curExpiry == 0 || curExpiry > block.timestamp) && _lockingDuration < curLockingDuration) {
            revert InvalidLockingDuration();
        }

        uint256 curBaseAmount = locks[_lockId].amount;

        uint256 baseAmount = curBaseAmount + _amount;
        uint256 boostAmount = boostController.getBoostAmount(baseAmount, _lockingDuration);

        if (_amount != 0) {
            locks[_lockId].amount = baseAmount;
        }

        if (_lockingDuration != curLockingDuration) {
            locks[_lockId].duration = _lockingDuration;
        }

        if (_lockingDuration != 0) {
            locks[_lockId].startTime = uint64(block.timestamp);
        } else if (curLockingDuration != 0) {
            delete locks[_lockId].startTime;
        }

        if (locks[_lockId].expiry != 0) {
            locks[_lockId].expiry = 0;
        }

        int256 diffTotalAmount = int256(baseAmount + boostAmount) - int256(curBaseAmount + locks[_lockId].boostAmount);
        if (diffTotalAmount > 0) {
            effectiveBalances[_sender] += uint256(diffTotalAmount);
            totalEffectiveBalance += uint256(diffTotalAmount);
        } else if (diffTotalAmount < 0) {
            effectiveBalances[_sender] -= uint256(-1 * diffTotalAmount);
            totalEffectiveBalance -= uint256(-1 * diffTotalAmount);
        }

        locks[_lockId].boostAmount = boostAmount;

        emit UpdateLock(_sender, _lockId, baseAmount, boostAmount, _lockingDuration);
    }

    /**
     * @notice transfers a lock between accounts
     * @dev
     * - reverts if `_from` is not the owner of the lock
     * - reverts if `to` is zero address
     * @param _from address to transfer from
     * @param _to address to transfer to
     * @param _lockId id of lock to transfer
     **/
    function _transfer(
        address _from,
        address _to,
        uint256 _lockId
    ) private {
        if (_from != ownerOf(_lockId)) revert TransferFromIncorrectOwner();
        if (_to == address(0)) revert TransferToZeroAddress();

        delete tokenApprovals[_lockId];

        _updateRewards(_from);
        _updateRewards(_to);

        uint256 effectiveBalanceChange = locks[_lockId].amount + locks[_lockId].boostAmount;
        effectiveBalances[_from] -= effectiveBalanceChange;
        effectiveBalances[_to] += effectiveBalanceChange;

        balances[_from] -= 1;
        balances[_to] += 1;
        lockOwners[_lockId] = _to;

        emit Transfer(_from, _to, _lockId);
    }

    /**
     * taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
     * @notice verifies that an address supports ERC721 and calls onERC721Received if applicable
     * @dev
     * - called after a lock is safe transferred
     * - calls onERC721Received on `_to` if it is a contract or reverts if it is a contract
     *   and does not implemement onERC721Received
     * @param _from address that lock is being transferred from
     * @param _to address that lock is being transferred to
     * @param _lockId id of lock
     * @param _data optional data to be passed to receiver
     */
    function _checkOnERC721Received(
        address _from,
        address _to,
        uint256 _lockId,
        bytes memory _data
    ) private returns (bool) {
        if (_to.code.length > 0) {
            try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _lockId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert TransferToNonERC721Implementer();
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @notice returns whether an account is authorized to transfer a lock
     * @dev returns true if `_spender` is approved to transfer `_lockId` or if `_spender` is
     * approved to transfer all locks owned by the owner of `_lockId`
     * @param _spender address of account
     * @param _lockId id of lock
     * @return whether address is authorized ot not
     **/
    function _isApprovedOrOwner(address _spender, uint256 _lockId) private view returns (bool) {
        address owner = ownerOf(_lockId);
        return (_spender == owner || isApprovedForAll(owner, _spender) || getApproved(_lockId) == _spender);
    }
}
IERC677.sol 12 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

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

interface IERC677 is IERC20 {
    function transferAndCall(
        address _to,
        uint256 _value,
        bytes calldata _data
    ) external returns (bool success);
}
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);
            }
        }
    }
}
IRewardsPool.sol 12 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

interface IRewardsPool {
    function updateReward(address _account) external;

    function withdraw(address _account) external;

    function distributeRewards() external;

    function withdrawableRewards(address _account) external view returns (uint256);
}
RewardsPoolController.sol 195 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "../interfaces/IRewardsPool.sol";
import "../RewardsPool.sol";

/**
 * @title Rewards Pool Controller
 * @notice Acts as a proxy for any number of rewards pools
 */
abstract contract RewardsPoolController is UUPSUpgradeable, OwnableUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;

    mapping(address => IRewardsPool) public tokenPools;
    address[] internal tokens;

    event WithdrawRewards(address indexed account);
    event AddToken(address indexed token, address rewardsPool);
    event RemoveToken(address indexed token, address rewardsPool);

    function __RewardsPoolController_init() public onlyInitializing {
        __Ownable_init();
        __UUPSUpgradeable_init();
    }

    modifier updateRewards(address _account) {
        _updateRewards(_account);
        _;
    }

    /**
     * @notice returns a list of supported tokens
     * @return list of token addresses
     **/
    function supportedTokens() external view returns (address[] memory) {
        return tokens;
    }

    /**
     * @notice returns true/false to whether a given token is supported
     * @param _token token address
     * @return is token supported
     **/
    function isTokenSupported(address _token) public view returns (bool) {
        return address(tokenPools[_token]) != address(0) ? true : false;
    }

    /**
     * @notice returns balances of supported tokens within the controller
     * @return list of supported tokens
     * @return list of token balances
     **/
    function tokenBalances() external view returns (address[] memory, uint256[] memory) {
        uint256[] memory balances = new uint256[](tokens.length);

        for (uint256 i = 0; i < tokens.length; ++i) {
            balances[i] = IERC20Upgradeable(tokens[i]).balanceOf(address(this));
        }

        return (tokens, balances);
    }

    /**
     * @notice ERC677 implementation to receive a token distribution
     **/
    function onTokenTransfer(
        address,
        uint256,
        bytes calldata
    ) external virtual {
        if (isTokenSupported(msg.sender)) {
            distributeToken(msg.sender);
        }
    }

    /**
     * @notice returns an account's staked amount for use by reward pools
     * controlled by this contract
     * @param _account account address
     * @return account's staked amount
     */
    function staked(address _account) external view virtual returns (uint256);

    /**
     * @notice returns the total staked amount for use by reward pools
     * controlled by this contract
     * @return total staked amount
     */
    function totalStaked() external view virtual returns (uint256);

    /**
     * @notice distributes token balances to their respective rewards pools
     * @param _tokens list of token addresses
     */
    function distributeTokens(address[] memory _tokens) public {
        for (uint256 i = 0; i < _tokens.length; ++i) {
            distributeToken(_tokens[i]);
        }
    }

    /**
     * @notice distributes a token balance to its respective rewards pool
     * @param _token token address
     */
    function distributeToken(address _token) public {
        require(isTokenSupported(_token), "Token not supported");

        IERC20Upgradeable token = IERC20Upgradeable(_token);
        uint256 balance = token.balanceOf(address(this));
        require(balance > 0, "Cannot distribute zero balance");

        token.safeTransfer(address(tokenPools[_token]), balance);
        tokenPools[_token].distributeRewards();
    }

    /**
     * @notice returns a list of withdrawable rewards for an account
     * @param _account account address
     * @return list of withdrawable reward amounts
     **/
    function withdrawableRewards(address _account) external view returns (uint256[] memory) {
        uint256[] memory withdrawable = new uint256[](tokens.length);

        for (uint256 i = 0; i < tokens.length; ++i) {
            withdrawable[i] = tokenPools[tokens[i]].withdrawableRewards(_account);
        }

        return withdrawable;
    }

    /**
     * @notice withdraws an account's earned rewards for a list of tokens
     * @param _tokens list of token addresses to withdraw rewards from
     **/
    function withdrawRewards(address[] memory _tokens) public {
        for (uint256 i = 0; i < _tokens.length; ++i) {
            tokenPools[_tokens[i]].withdraw(msg.sender);
        }
        emit WithdrawRewards(msg.sender);
    }

    /**
     * @notice adds a new token
     * @param _token token to add
     * @param _rewardsPool token rewards pool to add
     **/
    function addToken(address _token, address _rewardsPool) public onlyOwner {
        require(!isTokenSupported(_token), "Token is already supported");

        tokenPools[_token] = IRewardsPool(_rewardsPool);
        tokens.push(_token);

        if (IERC20Upgradeable(_token).balanceOf(address(this)) > 0) {
            distributeToken(_token);
        }

        emit AddToken(_token, _rewardsPool);
    }

    /**
     * @notice removes a supported token
     * @param _token address of token
     **/
    function removeToken(address _token) external onlyOwner {
        require(isTokenSupported(_token), "Token is not supported");

        IRewardsPool rewardsPool = tokenPools[_token];
        delete (tokenPools[_token]);
        for (uint256 i = 0; i < tokens.length; ++i) {
            if (tokens[i] == _token) {
                tokens[i] = tokens[tokens.length - 1];
                tokens.pop();
                break;
            }
        }

        emit RemoveToken(_token, address(rewardsPool));
    }

    /**
     * @dev triggers a reward update for a given account
     * @param _account account to update rewards for
     */
    function _updateRewards(address _account) internal {
        for (uint256 i = 0; i < tokens.length; ++i) {
            tokenPools[tokens[i]].updateReward(_account);
        }
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}
}
IERC721Receiver.sol 11 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
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);
}
IBoostController.sol 6 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

interface IBoostController {
    function getBoostAmount(uint256 _amount, uint64 _lockingDuration) external view returns (uint256);
}
IRewardsPoolController.sol 25 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

interface IRewardsPoolController {
    /**
     * @notice returns an account's stake balance for use by reward pools
     * controlled by this contract
     * @return account's balance
     */
    function staked(address _account) external view returns (uint256);

    /**
     * @notice returns the total staked amount for use by reward pools
     * controlled by this contract
     * @return total staked amount
     */
    function totalStaked() external view returns (uint256);

    /**
     * @notice adds a new token
     * @param _token token to add
     * @param _rewardsPool token rewards pool to add
     **/
    function addToken(address _token, address _rewardsPool) external;
}
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");
        }
    }
}
AddressUpgradeable.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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);
        }
    }
}
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;
}
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @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;
}
Initializable.sol 166 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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]
 * ```solidity
 * 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.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    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.
     *
     * 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.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * 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.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    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.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
UUPSUpgradeable.sol 112 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeTo(address newImplementation) public virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @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;
}
StorageSlotUpgradeable.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlotUpgradeable {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}
IERC20Upgradeable.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);
}
IERC1967Upgradeable.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
 *
 * _Available since v4.8.3._
 */
interface IERC1967Upgradeable {
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);
}
IBeaconUpgradeable.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeaconUpgradeable {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}
IERC721Upgradeable.sol 132 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721Upgradeable is IERC165Upgradeable {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
draft-IERC1822Upgradeable.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822ProxiableUpgradeable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}
SafeERC20Upgradeable.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20Upgradeable 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(IERC20Upgradeable 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));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20PermitUpgradeable 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(IERC20Upgradeable 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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
    }
}
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);
}
ERC1967UpgradeUpgradeable.sol 170 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/IERC1967Upgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 */
abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
    function __ERC1967Upgrade_init() internal onlyInitializing {
    }

    function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
    }
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            AddressUpgradeable.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), 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;
}
IERC20PermitUpgradeable.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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 IERC20PermitUpgradeable {
    /**
     * @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);
}
IERC721MetadataUpgradeable.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

Read Contract

balanceOf 0x70a08231 → uint256
baseURI 0x6c0360eb → string
boostController 0x7c898895 → address
delegatorPool 0x5deb761f → address
effectiveBalanceOf 0xc7a64723 → uint256
getApproved 0x081812fc → address
getLockIdsByOwner 0xdd0228f5 → uint256[]
getLocks 0x4a374d4d → tuple[]
isApprovedForAll 0xe985e9c5 → bool
isTokenSupported 0x75151b63 → bool
lastLockId 0xd0cb39c6 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
ownerOf 0x6352211e → address
proxiableUUID 0x52d1902d → bytes32
sdlToken 0x9b4140cc → address
staked 0x98807d84 → uint256
supportedTokens 0xb002249d → address[]
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
tokenBalances 0x0e3bc974 → address[], uint256[]
tokenPools 0xc3d2c3c1 → address
tokenURI 0xc87b56dd → string
totalEffectiveBalance 0x89676073 → uint256
totalStaked 0x817b1cd2 → uint256
withdrawableRewards 0x0f14b4d6 → uint256[]

Write Contract 23 functions

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

__RewardsPoolController_init 0x7c9097de
No parameters
addToken 0x5476bd72
address _token
address _rewardsPool
approve 0x095ea7b3
address _to
uint256 _lockId
distributeToken 0x86d74037
address _token
distributeTokens 0x88951352
address[] _tokens
extendLockDuration 0xe231db76
uint256 _lockId
uint64 _lockingDuration
initialize 0xdb0ed6a0
string _name
string _symbol
address _sdlToken
address _boostController
address _delegatorPool
initiateUnlock 0x2dc075e1
uint256 _lockId
migrate 0x76a19e5b
address _sender
uint256 _amount
uint64 _lockingDuration
onTokenTransfer 0xa4c0ed36
address _sender
uint256 _value
bytes _calldata
removeToken 0x5fa7b584
address _token
renounceOwnership 0x715018a6
No parameters
safeTransferFrom 0x42842e0e
address _from
address _to
uint256 _lockId
safeTransferFrom 0xb88d4fde
address _from
address _to
uint256 _lockId
bytes _data
setApprovalForAll 0xa22cb465
address _operator
bool _approved
setBaseURI 0x55f804b3
string _baseURI
setBoostController 0x1b23009d
address _boostController
transferFrom 0x23b872dd
address _from
address _to
uint256 _lockId
transferOwnership 0xf2fde38b
address newOwner
upgradeTo 0x3659cfe6
address newImplementation
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data
withdraw 0x441a3e70
uint256 _lockId
uint256 _amount
withdrawRewards 0x62d76d06
address[] _tokens

Recent Transactions

No transactions found for this address