Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x90FF684Fd8060eAff3e85eAe5F5F8C3cA6f67B2d
Balance 0 ETH
Nonce 1
Code Size 15696 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

15696 bytes
0x608060405234801561001057600080fd5b50600436106102ed5760003560e01c80638456cb5911610193578063cf94fdf5116100e4578063eb9f994b11610092578063eb9f994b146107b7578063efe33cfa146107ca578063f2fde38b146107dd578063f3fef3a3146107f0578063f5d85a7914610803578063fa2cc3c014610816578063ff85aee41461082957600080fd5b8063cf94fdf514610716578063cff7036a14610729578063d00fbae71461074c578063dc84aece1461075f578063dee3736c14610772578063e6fd48bc1461079b578063e9b085b2146107a457600080fd5b80639e5914da116101415780639e5914da146105cc578063ac318957146105df578063ad05e627146105f2578063b2fc784e14610615578063c64b02cd14610628578063c78f74201461063b578063c8820f6c1461070357600080fd5b80638456cb591461054657806384f31e961461054e5780638689da5d146105615780638da5cb5b1461057457806398293f81146105855780639a47ce13146105a65780639c7e2655146105b957600080fd5b80634570018c1161024d578063698c7eb3116101fb578063698c7eb3146104bc5780636d687fed146104cf57806370a1198e146104f2578063715018a61461050557806372485abf1461050d57806373806d10146105205780637b46c54f1461053357600080fd5b80634570018c1461043557806347e7ef24146104485780635750ec531461045b5780635c975abb14610483578063617761041461048e578063630b5ba1146104a15780636766efbe146104a957600080fd5b80631b8f3aae116102aa5780631b8f3aae146103ad5780632983a97c146103c0578063312580c7146103d357806334536524146103fe5780633b3f0ee6146104075780633f4ba83a1461041a578063453114631461042257600080fd5b806306bfa938146102f257806307337f2b1461032a578063081e3eda1461035d5780630ba84cd21461036f5780631441a5a91461038457806317caf6f1146103a4575b600080fd5b61030561030036600461309f565b61083c565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b61034d61033836600461309f565b60d26020526000908152604090205460ff1681565b6040519015158152602001610321565b60cc545b604051908152602001610321565b61038261037d3660046130bc565b610911565b005b60d754610397906001600160a01b031681565b60405161032191906130d5565b61036160d05481565b6103826103bb3660046130e9565b610968565b6103826103ce366004613122565b6109e9565b6103616103e13660046130e9565b60d560209081526000928352604080842090915290825290205481565b61036160cb5481565b6103976104153660046130e9565b610a7f565b610382610b3a565b61038261043036600461331c565b610b4c565b610382610443366004613384565b610b66565b6103826104563660046133a9565b610bcf565b61046e6104693660046130e9565b610c5b565b60408051928352602083019190915201610321565b60975460ff1661034d565b61038261049c36600461309f565b610c90565b610382610d11565b6103826104b73660046133d5565b610d69565b6103826104ca3660046130bc565b610def565b6104e26104dd3660046130e9565b610ec9565b60405161032194939291906134f0565b610382610500366004613560565b611002565b61038261101d565b61039761051b3660046130bc565b61102f565b60c954610397906001600160a01b031681565b61038261054136600461309f565b611059565b610382611182565b61038261055c366004613384565b611192565b61038261056f366004613384565b6111f7565b6033546001600160a01b0316610397565b61059861059336600461309f565b61125c565b6040516103219291906135d0565b6103826105b43660046135f5565b611307565b6103976105c73660046130bc565b6114dc565b6103826105da36600461309f565b6114ec565b6103826105ed366004613660565b61154f565b610605610600366004613695565b6116ce565b60405161032194939291906136d5565b61036161062336600461309f565b6117fb565b610382610636366004613384565b611914565b6106aa61064936600461309f565b60cd60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007909701546001600160a01b0396871697959694959394928316939282169260ff600160a01b90930483169290911689565b604080516001600160a01b039a8b16815260208101999099528801969096526060870194909452918616608086015290941660a084015292151560c083015260e082019290925290151561010082015261012001610321565b6103826107113660046133d5565b611979565b610382610724366004613710565b6119f4565b61034d61073736600461309f565b60d46020526000908152604090205460ff1681565b60ca54610397906001600160a01b031681565b61038261076d36600461375f565b611a59565b61039761078036600461309f565b60d6602052600090815260409020546001600160a01b031681565b61036160d15481565b6103826107b23660046137d8565b611c24565b6103826107c5366004613560565b611fad565b6103826107d8366004613813565b611fed565b6103826107eb36600461309f565b612056565b6103826107fe3660046133a9565b6120cc565b610382610811366004613841565b612145565b60d354610397906001600160a01b031681565b60d854610397906001600160a01b031681565b6001600160a01b03808216600090815260cd60209081526040808320815161012081018352815486168152600182015493810184905260028201549281019290925260038101546060830152600481015485166080830152600581015494851660a083015260ff600160a01b9095048516151560c0830152600681015460e083015260070154909316151561010084015260d05460cb549293849384938493916108e69190613897565b6108f091906138ae565b81602001516108fe886117fb565b60d0549450945094509450509193509193565b610919612200565b610921610d11565b60cb805490829055604080518281526020810184905233917f1d75b4af369dd9c67d43994eea5f98a89dcaa2d64156061ae12a4eaaeb43ff08910160405180910390a25050565b610970612200565b6001600160a01b03828116600081815260d6602090815260409182902080548686166001600160a01b0319821681179092558351948552909416908301819052908201929092527fdbd23579af5873a66ea220fd5ae10d1b4e84c32b5aa543a55671ca3be8d824d19060600160405180910390a1505050565b6109f1612200565b60c980546001600160a01b038087166001600160a01b03199283161790925560d3805486841690831617905560ca805485841690831617905560d88054928416929091169190911790556040517f7bf155f37e6059ebf981eaa922373a2593061c952bd4112e4cc2339da9f7cf5d90610a719086908690869086906138d0565b60405180910390a150505050565b33600090815260d2602052604081205460ff16610aaf5760405163f655705d60e01b815260040160405180910390fd5b604051632d096c4f60e21b815260009073f2a74fd90b43d6cd1e1262ef9a2e95396d2862c79063b425b13c90610aef9087908790309033906004016138d0565b602060405180830381865af4158015610b0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3091906138fb565b9150505b92915050565b610b42612200565b610b4a61225a565b565b610b546122a6565b610b6183833333856122ec565b505050565b610b6e6122a6565b60d8546001600160a01b03163314801590610b94575060ca546001600160a01b03163314155b15610bb257604051637f30378360e11b815260040160405180910390fd5b60ca54610bcb906001600160a01b031682846001612526565b5050565b610bd76122a6565b600260655403610c025760405162461bcd60e51b8152600401610bf990613918565b60405180910390fd5b60026065556001600160a01b038216600090815260cd60205260409020600781015460ff16610c4457604051631aa3480f60e21b815260040160405180910390fd5b610c518333846000612526565b5050600160655550565b6001600160a01b03828116600090815260cf6020908152604080832093851683529290522080546002909101545b9250929050565b610c98612200565b6001600160a01b038116610cbf576040516323048eb160e01b815260040160405180910390fd5b60d980546001810182556000919091527fcc6782fd46dd71c5f512301ab049782450b4eaf79fdac5443d93d274d39167860180546001600160a01b0319166001600160a01b0392909216919091179055565b610d196122a6565b60005b60cc54811015610d6657610d5660cc8281548110610d3c57610d3c61394f565b6000918252602090912001546001600160a01b0316611059565b610d5f81613965565b9050610d1c565b50565b610d716122a6565b6001600160a01b03838116600090815260cd60205260409020600501548491163314610db057604051630c41ae1360e41b815260040160405180910390fd5b600260655403610dd25760405162461bcd60e51b8152600401610bf990613918565b6002606555610de484838560006126b3565b505060016065555050565b610df7612200565b60d9548110610e195760405163d3482f7b60e01b815260040160405180910390fd5b60d98054610e299060019061397e565b81548110610e3957610e3961394f565b60009182526020909120015460d980546001600160a01b039092169183908110610e6557610e6561394f565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060d9805480610ea457610ea4613991565b600082815260209020810160001990810180546001600160a01b031916905501905550565b6001600160a01b038216600090815260cd6020526040812060609081908190610ef2878761274e565b60048201549095506001600160a01b031615610ff857600480820154604080516345b507e360e01b815290516001600160a01b03909216926345b507e39282820192600092908290030181865afa158015610f51573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f799190810190613a94565b6004808401546040516352146cdb60e01b81529397509195506001600160a01b03909116916352146cdb91610fb0918a91016130d5565b600060405180830381865afa158015610fcd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ff59190810190613b58565b91505b5092959194509250565b61100a6122a6565b61101784848384866122ec565b50505050565b611025612200565b610b4a600061287d565b60d9818154811061103f57600080fd5b6000918252602090912001546001600160a01b0316905081565b6110616122a6565b6001600160a01b038116600090815260cd6020526040902060028101544211158061108c575060d054155b15611095575050565b60006110a0836117fb565b9050806000036110b557504260029091015550565b60008260020154426110c7919061397e565b9050600060d054846001015460cb54846110e19190613897565b6110eb9190613897565b6110f591906138ae565b9050826111078264e8d4a51000613897565b61111191906138ae565b84600301546111209190613bdd565b60038501819055426002860181905560408051918252602082018690528101919091526001600160a01b038616907f50a1a2d4fcb1c08863a0b14fcc7d9d728e2b21d8d7588b9cfa3991efe8112ee79060600160405180910390a25050505050565b61118a612200565b610b4a6128cf565b61119a6122a6565b60d8546001600160a01b031633148015906111c0575060ca546001600160a01b03163314155b156111de57604051637f30378360e11b815260040160405180910390fd5b60ca54610bcb906001600160a01b0316828460016126b3565b6111ff6122a6565b60d8546001600160a01b03163314801590611225575060ca546001600160a01b03163314155b1561124357604051637f30378360e11b815260040160405180910390fd5b60d854610bcb906001600160a01b0316828460016126b3565b6001600160a01b03808216600090815260cd6020526040902060048101546060928392911661128b5750915091565b600480820154604080516345b507e360e01b815290516001600160a01b03909216926345b507e39282820192600092908290030181865afa1580156112d4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112fc9190810190613a94565b909590945092505050565b61130f612200565b611317610d11565b82811461133a576040516001621398b960e31b0319815260040160405180910390fd5b60005b838110156114d557600060cd600087878581811061135d5761135d61394f565b9050602002016020810190611372919061309f565b6001600160a01b03166001600160a01b031681526020019081526020016000206001015490508383838181106113aa576113aa61394f565b905060200201358160d0546113bf919061397e565b6113c99190613bdd565b60d0558383838181106113de576113de61394f565b9050602002013560cd60008888868181106113fb576113fb61394f565b9050602002016020810190611410919061309f565b6001600160a01b031681526020810191909152604001600020600101557f9d1e399e9f825d6a92c706d1784017e4e9e8c44116b04bf9d7b3dcffa37eddc88686848181106114605761146061394f565b9050602002016020810190611475919061309f565b828686868181106114885761148861394f565b905060200201356040516114ba939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a150806114cd81613965565b91505061133d565b5050505050565b60cc818154811061103f57600080fd5b6114f4612200565b60d780546001600160a01b0319166001600160a01b03831617905560405181907fdf63218877cb126f6c003f2b7f77327674cd6a0b53ad51deac392548ec12b0ed906115439083908190613bf0565b60405180910390a15050565b600054610100900460ff161580801561156f5750600054600160ff909116105b80611590575061157e3061290c565b158015611590575060005460ff166001145b6115f35760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bf9565b6000805460ff191660011790558015611616576000805461ff0019166101001790555b61161e61291b565b60c980546001600160a01b0386166001600160a01b031990911617905560cb83905560d1829055600060d081905560019060d2906116646033546001600160a01b031690565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558015611017576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610a71565b6001600160a01b038316600090815260cd60205260408120819060609082906116f7888861274e565b60048201549095506001600160a01b0316156117f15785866001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801561174c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117749190810190613c0a565b60048084015460405163211dc32d60e01b81529397509195506001600160a01b039091169163211dc32d916117ad918b918b9101613bf0565b602060405180830381865afa1580156117ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ee9190613c3e565b91505b5093509350935093565b6001600160a01b03808216600081815260cd6020526040812060ca5491939092911690036118a15760ca60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611876573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189a9190613c3e565b9392505050565b60d8546001600160a01b039081169084160361190a5760d860009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611876573d6000803e3d6000fd5b6006015492915050565b61191c6122a6565b60d8546001600160a01b03163314801590611942575060ca546001600160a01b03163314155b1561196057604051637f30378360e11b815260040160405180910390fd5b60d854610bcb906001600160a01b031682846001612526565b6119816122a6565b6001600160a01b03838116600090815260cd602052604090206005015484911633146119c057604051630c41ae1360e41b815260040160405180910390fd5b6002606554036119e25760405162461bcd60e51b8152600401610bf990613918565b6002606555610de48483856000612526565b6119fc6122a6565b6000816001600160401b03811115611a1657611a166131c2565b604051908082528060200260200182016040528015611a4957816020015b6060815260200190600190039081611a345790505b509050610b6183833333856122ec565b33600090815260d2602052604090205460ff16611a895760405163f655705d60e01b815260040160405180910390fd5b611a928361290c565b158015611aa757506001600160a01b03831615155b15611ac55760405163b66f944760e01b815260040160405180910390fd5b611ace8461290c565b158015611ae357506001600160a01b03841615155b15611b015760405163b66f944760e01b815260040160405180910390fd5b6001600160a01b038616600090815260ce602052604090205460ff16611b3a57604051636a325bd960e11b815260040160405180910390fd5b611b42610d11565b6001600160a01b038616600090815260cd602052604090206001015460d0548691611b6c9161397e565b611b769190613bdd565b60d0556001600160a01b03868116600081815260cd6020908152604091829020600181018a90556004810180546001600160a01b031916898716908117909155600582018054968b166001600160a81b031990971696909617600160a01b8915150217909555600701805460ff191686151517905590518881527fdb56252d0d52575e1a437302556b299c2a995c7fd5c619b8efda785dcf597d2891015b60405180910390a3505050505050565b33600090815260d2602052604090205460ff16611c545760405163f655705d60e01b815260040160405180910390fd5b611c5d8561290c565b611c7a576040516330704cfd60e11b815260040160405180910390fd5b611c838361290c565b158015611c9857506001600160a01b03831615155b15611cb65760405163b66f944760e01b815260040160405180910390fd5b611cbf8461290c565b158015611cd457506001600160a01b03841615155b15611cf25760405163b66f944760e01b815260040160405180910390fd5b6001600160a01b038516600090815260ce602052604090205460ff1615611d2c5760405163199dc5fd60e31b815260040160405180910390fd5b600060d1544211611d3f5760d154611d41565b425b90508660d054611d519190613bdd565b60d08190555060cc869080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b03160217905550604051806101200160405280876001600160a01b0316815260200188815260200182815260200160008152602001866001600160a01b03168152602001856001600160a01b0316815260200184151581526020016000815260200183151581525060cd6000886001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060c08201518160050160146101000a81548160ff02191690831515021790555060e082015181600601556101008201518160070160006101000a81548160ff021916908315150217905550905050600160ce6000886001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550846001600160a01b0316866001600160a01b03167fec85b1d1f037ff3a8722aaf5d4d8e7d93c7ff10c056430c18d76a9ec23aa397e89604051611f9c91815260200190565b60405180910390a350505050505050565b611fb56122a6565b60d3546001600160a01b03163314611fe057604051630c240a0760e11b815260040160405180910390fd5b61101784848333866122ec565b611ff5612200565b6001600160a01b038216600081815260d26020908152604091829020805460ff1916851515908117909155825193845260ff161515908301527f26b10598e51169a6f63965086cafd8665e54b0ff538233804909efe8d5c5810d9101611543565b61205e612200565b6001600160a01b0381166120c35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610bf9565b610d668161287d565b6120d46122a6565b6002606554036120f65760405162461bcd60e51b8152600401610bf990613918565b60026065556001600160a01b038216600090815260cd60205260409020600781015460ff1661213857604051631aa3480f60e21b815260040160405180910390fd5b610c5183338460006126b3565b61214d612200565b604051633a35ae2d60e01b81526001600160a01b0383811660048301528215156024830152849190821690633a35ae2d90604401600060405180830381600087803b15801561219b57600080fd5b505af11580156121af573d6000803e3d6000fd5b5050604080516001600160a01b03808916825287166020820152851515918101919091527fb7064f45347c985b72ff8f7e00fc943b8837432cca6d34d414425437bdc2456b92506060019050610a71565b6033546001600160a01b03163314610b4a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bf9565b61226261294a565b6097805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405161229c91906130d5565b60405180910390a1565b60975460ff1615610b4a5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610bf9565b60026065540361230e5760405162461bcd60e51b8152600401610bf990613918565b6002606555805184908114612339576040516001621398b960e31b0319815260040160405180910390fd5b6000805b8281101561247f5760008888838181106123595761235961394f565b905060200201602081019061236e919061309f565b6001600160a01b03808216600090815260cf60209081526040808320938c1683529290522090915061239f82611059565b6001600160a01b03808316600090815260d560209081526040808320938c168352929052908120546123d1848b612993565b6123db9190613bdd565b90506123e78186613bdd565b6001600160a01b03808516600081815260d560209081526040808320948f16835293815283822082905591815260cd9091522060030154835491965064e8d4a51000916124349190613897565b61243e91906138ae565b826001018190555061246b838a8a8a888151811061245e5761245e61394f565b6020026020010151612a05565b5050508061247890613965565b905061233d565b50801561249157612491858583612b08565b8080158015906124ab575060d7546001600160a01b031615155b156125175760d754604051637999b31f60e01b81526001600160a01b0388811660048301526024820184905290911690637999b31f90604401600060405180830381600087803b1580156124fe57600080fd5b505af1158015612512573d6000803e3d6000fd5b505050505b50506001606555505050505050565b61252f84611059565b6001600160a01b03808516600090815260cd6020908152604080832060cf8352818420948816845293909152902080541561256e5761256e8686612b6d565b6125788686612bbf565b8054612585908590613bdd565b815560068201805485919060009061259e908490613bdd565b909155508390506125bf578381600201546125b99190613bdd565b60028201555b600782015460ff16156125e35781546125e3906001600160a01b0316333087612c46565b6003820154815464e8d4a51000916125fa91613897565b61260491906138ae565b600182015583156126ab578261266657856001600160a01b0316856001600160a01b03167f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f628660405161265991815260200190565b60405180910390a36126ab565b856001600160a01b0316856001600160a01b03167f6d0456143026caba846332ec09535fc3171dcd0c340cf99ad1668e75bfc1c7c886604051611c1491815260200190565b505050505050565b6126bf84848484612cb1565b6001600160a01b038416600090815260cd60205260409020600781015460ff16156126fa5780546126fa906001600160a01b03168585612de9565b846001600160a01b0316846001600160a01b03167f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb8560405161273f91815260200190565b60405180910390a35050505050565b6001600160a01b03808316600090815260cd6020908152604080832060cf8352818420948616845293909152812060038301549192918361278e876117fb565b90508360020154421180156127a257508015155b156128125760008460020154426127b9919061397e565b9050600060d054866001015460cb54846127d39190613897565b6127dd9190613897565b6127e791906138ae565b9050826127f98264e8d4a51000613897565b61280391906138ae565b61280d9085613bdd565b935050505b6001830154835464e8d4a510009061282b908590613897565b61283591906138ae565b61283f919061397e565b6001600160a01b03808916600090815260d560209081526040808320938b16835292905220549095506128729086613bdd565b979650505050505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6128d76122a6565b6097805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861228f3390565b6001600160a01b03163b151590565b600054610100900460ff166129425760405162461bcd60e51b8152600401610bf990613c57565b610b4a612e19565b60975460ff16610b4a5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bf9565b6001600160a01b03808316600081815260cf602090815260408083209486168352938152838220600181015493835260cd9091529281206003015483549193928492909164e8d4a51000916129e89190613897565b6129f291906138ae565b6129fc919061397e565b95945050505050565b6001600160a01b03808516600090815260cd60205260409020600401541680156114d557815115612a97576040516369795e9360e01b81526001600160a01b038216906369795e9390612a6090879087908790600401613ca2565b600060405180830381600087803b158015612a7a57600080fd5b505af1158015612a8e573d6000803e3d6000fd5b505050506114d5565b604051636b09169560e01b81526001600160a01b03821690636b09169590612ac59087908790600401613bf0565b6020604051808303816000875af1158015612ae4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ab9190613cce565b60c954612b1f906001600160a01b03168383612de9565b60408051828152600060208201526001600160a01b0380851692908616917f3be17631ba479f3fb13e147852e85f56f49ff86f2799fb780f338087db571b75910160405180910390a3505050565b6000612b798383612993565b6001600160a01b03808516600090815260d560209081526040808320938716835292905290812080549293508392909190612bb5908490613bdd565b9091555050505050565b6001600160a01b03808316600090815260cd6020526040902060040154168015610b61576040516301c14b2d60e31b81526001600160a01b03821690630e0a596890612c0f9085906004016130d5565b600060405180830381600087803b158015612c2957600080fd5b505af1158015612c3d573d6000803e3d6000fd5b50505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526110179085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612e49565b612cba84611059565b6001600160a01b03808516600081815260cf60209081526040808320948816835293815283822092825260cd90529190912082158015612cfd5750838260020154105b15612d1b5760405163e997875560e01b815260040160405180910390fd5b815484118015612d285750825b15612d4657604051633bd20ca960e21b815260040160405180910390fd5b612d508686612b6d565b612d5a8686612bbf565b8154612d6790859061397e565b8255600681018054859190600090612d8090849061397e565b90915550839050612da157838260020154612d9b919061397e565b60028301555b6001600160a01b038616600090815260cd6020526040902060030154825464e8d4a5100091612dcf91613897565b612dd991906138ae565b8260010181905550505050505050565b6040516001600160a01b038316602482015260448101829052610b6190849063a9059cbb60e01b90606401612c7a565b600054610100900460ff16612e405760405162461bcd60e51b8152600401610bf990613c57565b610b4a3361287d565b6000612e9e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612f1b9092919063ffffffff16565b805190915015610b615780806020019051810190612ebc9190613cce565b610b615760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bf9565b6060612f2a8484600085612f32565b949350505050565b606082471015612f935760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bf9565b612f9c8561290c565b612fe85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bf9565b600080866001600160a01b031685876040516130049190613ceb565b60006040518083038185875af1925050503d8060008114613041576040519150601f19603f3d011682016040523d82523d6000602084013e613046565b606091505b50915091506128728282866060831561306057508161189a565b8251156130705782518084602001fd5b8160405162461bcd60e51b8152600401610bf99190613d07565b6001600160a01b0381168114610d6657600080fd5b6000602082840312156130b157600080fd5b813561189a8161308a565b6000602082840312156130ce57600080fd5b5035919050565b6001600160a01b0391909116815260200190565b600080604083850312156130fc57600080fd5b82356131078161308a565b915060208301356131178161308a565b809150509250929050565b6000806000806080858703121561313857600080fd5b84356131438161308a565b935060208501356131538161308a565b925060408501356131638161308a565b915060608501356131738161308a565b939692955090935050565b60008083601f84011261319057600080fd5b5081356001600160401b038111156131a757600080fd5b6020830191508360208260051b8501011115610c8957600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613200576132006131c2565b604052919050565b60006001600160401b03821115613221576132216131c2565b5060051b60200190565b600082601f83011261323c57600080fd5b8135602061325161324c83613208565b6131d8565b828152600592831b850182019282820191908785111561327057600080fd5b8387015b8581101561330f5780356001600160401b038111156132935760008081fd5b8801603f81018a136132a55760008081fd5b8581013560406132b761324c83613208565b82815291851b8301810191888101908d8411156132d45760008081fd5b938201935b838510156132fe57843592506132ee8361308a565b82825293890193908901906132d9565b885250505093850193508401613274565b5090979650505050505050565b60008060006040848603121561333157600080fd5b83356001600160401b038082111561334857600080fd5b6133548783880161317e565b9095509350602086013591508082111561336d57600080fd5b5061337a8682870161322b565b9150509250925092565b6000806040838503121561339757600080fd5b8235915060208301356131178161308a565b600080604083850312156133bc57600080fd5b82356133c78161308a565b946020939093013593505050565b6000806000606084860312156133ea57600080fd5b83356133f58161308a565b925060208401359150604084013561340c8161308a565b809150509250925092565b600081518084526020808501945080840160005b838110156134505781516001600160a01b03168752958201959082019060010161342b565b509495945050505050565b60005b8381101561347657818101518382015260200161345e565b50506000910152565b6000815180845261349781602086016020860161345b565b601f01601f19169290920160200192915050565b6000815180845260208085019450848260051b860182860160005b8581101561330f5783830389526134de83835161347f565b988501989250908401906001016134c6565b8481526000602060808184015261350a6080840187613417565b838103604085015261351c81876134ab565b8481036060860152855180825283870192509083019060005b8181101561355157835183529284019291840191600101613535565b50909998505050505050505050565b6000806000806060858703121561357657600080fd5b84356001600160401b038082111561358d57600080fd5b6135998883890161317e565b909650945060208701359150808211156135b257600080fd5b506135bf8782880161322b565b92505060408501356131738161308a565b6040815260006135e36040830185613417565b82810360208401526129fc81856134ab565b6000806000806040858703121561360b57600080fd5b84356001600160401b038082111561362257600080fd5b61362e8883890161317e565b9096509450602087013591508082111561364757600080fd5b506136548782880161317e565b95989497509550505050565b60008060006060848603121561367557600080fd5b83356136808161308a565b95602085013595506040909401359392505050565b6000806000606084860312156136aa57600080fd5b83356136b58161308a565b925060208401356136c58161308a565b9150604084013561340c8161308a565b8481526001600160a01b03841660208201526080604082018190526000906136ff9083018561347f565b905082606083015295945050505050565b6000806020838503121561372357600080fd5b82356001600160401b0381111561373957600080fd5b6137458582860161317e565b90969095509350505050565b8015158114610d6657600080fd5b60008060008060008060c0878903121561377857600080fd5b86356137838161308a565b955060208701359450604087013561379a8161308a565b935060608701356137aa8161308a565b925060808701356137ba81613751565b915060a08701356137ca81613751565b809150509295509295509295565b60008060008060008060c087890312156137f157600080fd5b8635955060208701356138038161308a565b9450604087013561379a8161308a565b6000806040838503121561382657600080fd5b82356138318161308a565b9150602083013561311781613751565b60008060006060848603121561385657600080fd5b83356138618161308a565b925060208401356138718161308a565b9150604084013561340c81613751565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610b3457610b34613881565b6000826138cb57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03948516815292841660208401529083166040830152909116606082015260800190565b60006020828403121561390d57600080fd5b815161189a8161308a565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161397757613977613881565b5060010190565b81810381811115610b3457610b34613881565b634e487b7160e01b600052603160045260246000fd5b600082601f8301126139b857600080fd5b81516001600160401b038111156139d1576139d16131c2565b6139e4601f8201601f19166020016131d8565b8181528460208386010111156139f957600080fd5b612f2a82602083016020870161345b565b600082601f830112613a1b57600080fd5b81516020613a2b61324c83613208565b82815260059290921b84018101918181019086841115613a4a57600080fd5b8286015b84811015613a895780516001600160401b03811115613a6d5760008081fd5b613a7b8986838b01016139a7565b845250918301918301613a4e565b509695505050505050565b60008060408385031215613aa757600080fd5b82516001600160401b0380821115613abe57600080fd5b818501915085601f830112613ad257600080fd5b81516020613ae261324c83613208565b82815260059290921b84018101918181019089841115613b0157600080fd5b948201945b83861015613b28578551613b198161308a565b82529482019490820190613b06565b91880151919650909350505080821115613b4157600080fd5b50613b4e85828601613a0a565b9150509250929050565b60006020808385031215613b6b57600080fd5b82516001600160401b03811115613b8157600080fd5b8301601f81018513613b9257600080fd5b8051613ba061324c82613208565b81815260059190911b82018301908381019087831115613bbf57600080fd5b928401925b8284101561287257835182529284019290840190613bc4565b80820180821115610b3457610b34613881565b6001600160a01b0392831681529116602082015260400190565b600060208284031215613c1c57600080fd5b81516001600160401b03811115613c3257600080fd5b612f2a848285016139a7565b600060208284031215613c5057600080fd5b5051919050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6001600160a01b038481168252831660208201526060604082018190526000906129fc90830184613417565b600060208284031215613ce057600080fd5b815161189a81613751565b60008251613cfd81846020870161345b565b9190910192915050565b60208152600061189a602083018461347f56fea2646970667358221220266d649a0a7a83ee3a38185e8fa01c5f75ad534b1e21e557c4c8f020cf85d20b64736f6c63430008130033

Verified Source Code Partial Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (100 runs)
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _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 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

    bool private _paused;

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

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

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

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

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

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

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

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

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

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

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

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

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

    uint256 private _status;

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

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

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

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

        _;

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _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 anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _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);
    }
}
ERC20.sol 383 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

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

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

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "./draft-IERC20Permit.sol";
import "../ERC20.sol";
import "../../../utils/cryptography/draft-EIP712.sol";
import "../../../utils/cryptography/ECDSA.sol";
import "../../../utils/Counters.sol";

/**
 * @dev Implementation 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.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    /**
     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
     * However, to ensure consistency with the upgradeable transpiler, we will continue
     * to reserve a slot.
     * @custom:oz-renamed-from _PERMIT_TYPEHASH
     */
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}
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);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
Counters.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}
draft-EIP712.sol 104 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;
    address private immutable _CACHED_THIS;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _CACHED_THIS = address(this);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}
ECDSA.sol 218 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.3) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}
Strings.sol 75 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}
IBaseRewardPool.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IBaseRewardPool {
    function stakingDecimals() external view returns (uint256);

    function totalStaked() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function rewardPerToken(address token) external view returns (uint256);

    function rewardTokenInfos()
        external
        view
        returns
        (
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols
        );

    function earned(address account, address token)
        external
        view
        returns (uint256);

    function allEarned(address account)
        external
        view
        returns (uint256[] memory pendingBonusRewards);

    function queueNewRewards(uint256 _rewards, address token)
        external
        returns (bool);

    function getReward(address _account, address _receiver) external returns (bool);

    function getRewards(address _account, address _receiver, address[] memory _rewardTokens) external;

    function updateFor(address account) external;

    function updateManager(address _rewardManager, bool _allowed) external;
}
IHarvesttablePoolHelper.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IPoolHelper.sol";


interface IHarvesttablePoolHelper is IPoolHelper {
    function harvest() external;
}
ILocker.sol 32 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILocker {

    struct UserUnlocking {
        uint256 startTime;
        uint256 endTime;
        uint256 amountInCoolDown; // total amount comitted to the unlock slot, never changes except when reseting slot
    }    

    function getUserUnlockingSchedule(address _user) external view returns (UserUnlocking[] memory slots);
    function getUserAmountInCoolDown(address _user) external view returns (uint256);
    function totalLocked() external view returns (uint256);
    function getFullyUnlock(address _user) external view returns(uint256 unlockedAmount);
    function getRewardablePercentWAD(address _user) external view returns(uint256 percent);
    function totalAmountInCoolDown() external view returns (uint256);
    function getUserNthUnlockSlot(address _user, uint256 n) external view returns (
        uint256 startTime,
        uint256 endTime,
        uint256 amountInCoolDown
    );

    function getUserUnlockSlotLength(address _user) external view returns (uint256);
    function getNextAvailableUnlockSlot(address _user) external view returns (uint256);
    function getUserTotalLocked(address _user) external view returns (uint256);
    function lock(uint256 _amount) external;
    function lockFor(uint256 _amount, address _for) external;
    function startUnlock(uint256 _amountToCoolDown) external;
    function cancelUnlock(uint256 _slotIndex) external;
    function unlock(uint256 slotIndex) external;    
}
IMasterMagpie.sol 130 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IMasterMagpie {
    function poolLength() external view returns (uint256);

    function setPoolManagerStatus(address _address, bool _bool) external;

    function add(
        uint256 _allocPoint,
        address _stakingTokenToken,
        address _rewarder,
        address _helper,
        bool _helperNeedsHarvest
    ) external;

    function createRewarder(
        address _stakingToken,
        address mainRewardToken
    ) external returns (address);

    function set(
        address _stakingToken,
        uint256 _allocPoint,
        address _helper,
        address _rewarder,
        bool _helperNeedsHarvest
    ) external;

    // View function to see pending GMPs on frontend.
    function getPoolInfo(
        address token
    )
        external
        view
        returns (
            uint256 emission,
            uint256 allocpoint,
            uint256 sizeOfPool,
            uint256 totalPoint
        );

    function rewarderBonusTokenInfo(
        address _stakingToken
    )
        external
        view
        returns (
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols
        );

    function pendingTokens(
        address _stakingToken,
        address _user,
        address token
    )
        external
        view
        returns (
            uint256 _pendingGMP,
            address _bonusTokenAddress,
            string memory _bonusTokenSymbol,
            uint256 _pendingBonusToken
        );

    function allPendingTokens(
        address _stakingToken,
        address _user
    )
        external
        view
        returns (
            uint256 pendingMGP,
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols,
            uint256[] memory pendingBonusRewards
        );

    function massUpdatePools() external;

    function updatePool(address _stakingToken) external;

    function deposit(address _stakingToken, uint256 _amount) external;

    function withdraw(address _stakingToken, uint256 _amount) external;

    function depositFor(
        address _stakingToken,
        uint256 _amount,
        address sender
    ) external;

    function withdrawFor(
        address _stakingToken,
        uint256 _amount,
        address _sender
    ) external;

    function depositVlMGPFor(uint256 _amount, address sender) external;

    function withdrawVlMGPFor(uint256 _amount, address sender) external;

    function depositMWomSVFor(uint256 _amount, address sender) external;

    function withdrawMWomSVFor(uint256 _amount, address sender) external;

    function multiclaimFor(
        address[] calldata _stakingTokens,
        address[][] calldata _rewardTokens,
        address user_address
    ) external;

    function multiclaimOnBehalf(
        address[] memory _stakingTokens,
        address[][] calldata _rewardTokens,
        address user_address
    ) external;

    function emergencyWithdraw(address _stakingToken, address sender) external;

    function updateEmissionRate(uint256 _gmpPerSec) external;

    function stakingInfo(
        address _stakingToken,
        address _user
    ) external view returns (uint256 depositAmount, uint256 availableAmount);
}
IMasterMagpieV2.sol 81 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IMasterMagpie {
    function poolLength() external view returns (uint256);

    function setPoolManagerStatus(address _address, bool _bool) external;

    function add(uint256 _allocPoint, address _stakingTokenToken, address _rewarder, address _helper, bool _helperNeedsHarvest, bool _transferStakingToken) external;

    function set(address _stakingToken, uint256 _allocPoint, address _helper,
        address _rewarder, bool _helperNeedsHarvest, bool _transferStakingToken) external;

    function createRewarder(address _stakingTokenToken, address mainRewardToken) external
        returns (address);

    // View function to see pending GMPs on frontend.
    function getPoolInfo(address token) external view
        returns (
            uint256 emission,
            uint256 allocpoint,
            uint256 sizeOfPool,
            uint256 totalPoint
        );

    function rewarderBonusTokenInfo(address _stakingToken) external view
        returns (address[] memory bonusTokenAddresses, string[] memory bonusTokenSymbols);

    function pendingTokens(address _stakingToken, address _user, address token) external view
        returns (
            uint256 _pendingGMP,
            address _bonusTokenAddress,
            string memory _bonusTokenSymbol,
            uint256 _pendingBonusToken
        );

    function allPendingTokens(address _stakingToken, address _user)external view
        returns (
            uint256 pendingMGP,
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols,
            uint256[] memory pendingBonusRewards
        );

    function massUpdatePools() external;

    function updatePool(address _stakingToken) external;

    function deposit(address _stakingToken, uint256 _amount) external;

    function withdraw(address _stakingToken, uint256 _amount) external;

    function depositFor(address _stakingToken, uint256 _amount, address sender) external;

    function withdrawFor(address _stakingToken, uint256 _amount, address _sender ) external;

    function depositVlMGPFor(uint256 _amount, address sender) external;

    function withdrawVlMGPFor(uint256 _amount, address sender) external;

    function depositMWomSVFor(uint256 _amount, address sender) external;

    function withdrawMWomSVFor(uint256 _amount, address sender) external;    

    function multiclaimFor(address[] calldata _stakingTokens, address[][] calldata _rewardTokens, address user_address) external;

    function multiclaimOnBehalf(address[] memory _stakingTokens, address[][] calldata _rewardTokens, address user_address) external;

    function emergencyWithdraw(address _stakingToken, address sender) external;

    function updateEmissionRate(uint256 _gmpPerSec) external;

    function stakingInfo(address _stakingToken, address _user)
        external
        view
        returns (uint256 depositAmount, uint256 availableAmount);

    function calLpSupply(address _stakingToken) external view returns (uint256);
}
IMintableERC20.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IMintableERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);

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

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

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function mint(address, uint256) external;
    function faucet(uint256) external;

    function burn(address, uint256) external;

    /**
     * @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
    );
}
IPoolHelper.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IPoolHelper {
    function totalStaked() external view returns (uint256);

    function balance(address _address) external view returns (uint256);

    function deposit(uint256 amount, uint256 minimumAmount) external;

    function withdraw(uint256 amount, uint256 minimumAmount) external;

    function isNative() external view returns (bool);

    function pid() external view returns (uint256);

    function depositToken() external view returns (address);

    function lpToken() external view returns (address);

    function rewarder() external view returns (address);

    function stakingToken() external view returns (address);
}
IReferralStorage.sol 14 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IReferralStorage {
    function getMyRefererInfo(address _account) external view returns (bytes32, address);
    function codeOwners(bytes32 _code) external view returns (address);
    function setReferrerTier(address _referrer, uint256 _tierId) external;    
    function setTier(uint256 _tierId, uint256 _totalRebate) external; 
    function setSharePercent(uint256 _sharePercent) external;
    function forceSetCodeOwner(bytes32 _code, address _newAccount) external;
    function trigger(address _referee, uint256 _amount) external;
    function updateTotalFactor(address _account) external;
}
ISimpleHelper.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ISimpleHelper {
    function depositFor(uint256 _amount, address _for) external;
}
IvlmgpPBaseRewarder.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IBaseRewardPool.sol";

interface IvlmgpPBaseRewarder is IBaseRewardPool {
    
    function queueMGP(uint256 _amount, address _user, address _receiver) external returns(bool);
}
IWNative.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWNative {
    function deposit() external payable;

    function withdraw(uint256 wad) external;

    function balanceOf(address account) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);
}
IMasterWombat.sol 30 lines
pragma solidity ^0.8.0;

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

interface IMasterWombat {

    function getAssetPid(address lp) external view returns(uint256);
    
    function depositFor(uint256 pid, uint256 amount, address account) external;

    function deposit(uint256 _pid, uint256 _amount) external returns (uint256, uint256);

    function withdraw(uint256 _pid, uint256 _amount) external returns (uint256, uint256);

    function multiClaim(uint256[] memory _pids) external returns (
        uint256 transfered,
        uint256[] memory amounts,
        uint256[] memory additionalRewards
    );

    function pendingTokens(uint256 _pid, address _user) external view
        returns (
            uint256 pendingRewards,
            IERC20[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols,
            uint256[] memory pendingBonusRewards
    );

    function migrate(uint256[] calldata _pids) external;
}
IWombatStakingV2.sol 50 lines
// SPDX-License-Identifier: MIT

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

pragma solidity ^0.8.0;

interface IWombatStaking {
    function convertWOM(uint256 amount) external returns (uint256);

    function masterWombat() external view returns (address);

    function deposit(
        address _lpToken,
        uint256 _amount,
        uint256 _minAmount,
        address _for,
        address _from
    ) external returns(uint256);

    function depositLP(address _lpToken, uint256 _lpAmount, address _for) external;

    function withdraw(
        address _lpToken,
        uint256 _amount,
        uint256 _minAmount,
        address _sender
    ) external;

    function withdrawLP(address _lpToken, uint256 _lpAmount, address _sender) external;

    function getPoolLp(address _lpToken) external view returns (address);

    function harvest(address _lpToken) external;

    function vote(
        address[] calldata _lpVote,
        int256[] calldata _deltas,
        address[] calldata _rewarders,
        address caller
    ) external returns (address[][] memory rewardTokens, uint256[][] memory feeAmounts);

    function voter() external view returns (address);

    function pendingBribeCallerFee(
        address[] calldata pendingPools
    )
        external
        view
        returns (IERC20[][] memory rewardTokens, uint256[][] memory callerFeeAmount);
}
MagpieFactoryLibV2.sol 50 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {MintableERC20} from "./MintableERC20.sol";
import {BaseRewardPoolV4} from "../rewards/BaseRewardPoolV4.sol";
import {WombatPoolHelperV4} from "../wombat/WombatPoolHelperV4.sol";

library MagpieFactoryLibV2 {

    function createRewarder(
        address _stakingToken,
        address _mainRewardToken,
        address _masterMagpie,
        address _rewardManager
    ) external returns (address) {
        BaseRewardPoolV4 _rewarder = new BaseRewardPoolV4(
            _stakingToken,
            _mainRewardToken,
            _masterMagpie,
            _rewardManager
        );
        return address(_rewarder);
    }

    function createWombatPoolHelper(
        uint256 _pid,
        address _depositToken,
        address _lpToken,
        address _wombatStaking,
        address _masterMagpie,
        address _rewarder,
        address _mWom,
        bool _isNative
    ) public returns (address) {
        WombatPoolHelperV4 pool = new WombatPoolHelperV4(
            _pid,
            _depositToken,
            _lpToken,
            _wombatStaking,
            _masterMagpie,
            _rewarder,
            _mWom,
            _isNative
        );
        return address(pool);
    }

}
MintableERC20.sol 22 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;

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

contract MintableERC20 is ERC20, Ownable {
    /*
    The ERC20 deployed will be owned by the others contracts of the protocol, specifically by
    MasterMagpie and WombatStaking, forbidding the misuse of these functions for nefarious purposes
    */
    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} 

    function mint(address account, uint256 amount) external virtual onlyOwner {
        _mint(account, amount);
    }

    function burn(address account, uint256 amount) external virtual onlyOwner {
        _burn(account, amount);
    }
}
Mgp.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol';

/// @title MGP
/// @author Magpie Team
contract MGP is ERC20('Magpie Token', 'MGP'), ERC20Permit('Magpie Token') {
    constructor(address _receipient, uint256 _totalSupply) {
        _mint(_receipient, _totalSupply);
    }
}
BaseRewardPoolV4.sol 354 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../interfaces/IMasterMagpieV2.sol";
import "../interfaces/IBaseRewardPool.sol";

/// @title A contract for managing rewards for a pool
/// @author Magpie Team
/// @notice You can use this contract for getting informations about rewards for a specific pools

/// @dev Changes in BaseRewardPoolV4 are:
// The usage of receipt token to get the totalStaked is replaced by _calLpSupply() from masterMagpie

contract BaseRewardPoolV4 is Ownable, IBaseRewardPool {
    using SafeERC20 for IERC20Metadata;
    using SafeERC20 for IERC20;

    /* ============ State Variables ============ */

    uint256 public constant DENOMINATOR = 10**12;

    address public immutable stakingToken;
    address public immutable operator;          // master magpie
    uint256 public immutable stakingTokenDecimals;

    address[] public rewardTokens;

    struct Reward {
        uint256 rewardPerTokenStored;
        uint256 queuedRewards;
        uint256 historicalRewards;
    }

    mapping(address => Reward) public rewards;                           // [rewardToken]
    // amount by [rewardToken][account], 
    mapping(address => mapping(address => uint256)) public userRewardPerTokenPaid;                 
    mapping(address => mapping(address => uint256)) public userRewards;  // amount by [rewardToken][account]
    mapping(address => bool) public isRewardToken;
    mapping(address => bool) public managers;

    /* ============ Events ============ */

    event RewardAdded(uint256 _reward, address indexed _token);
    event RewardPaid(address indexed _user, address indexed _receiver, uint256 _reward, address indexed _token);
    event ManagerUpdated(address indexed _manager, bool _allowed);
    event EmergencyWithdrawn(address indexed _manager, address _token, uint256 _amount);
    event RewardUpdatedFor(address indexed _user);

    /* ============ Errors ============ */

    error OnlyManager();
    error OnlyMasterMagpie();
    error NotAllowZeroAddress();
    error MustBeRewardToken();

    /* ============ Constructor ============ */

    constructor(
        address _stakingToken,
        address _rewardToken,
        address _masterMagpie,
        address _rewardManager
    ) {
        if(
            _stakingToken == address(0) ||
            _masterMagpie  == address(0) ||
            _rewardManager  == address(0)
        ) revert NotAllowZeroAddress();

        stakingToken = _stakingToken;
        stakingTokenDecimals = IERC20Metadata(stakingToken).decimals();
        operator = _masterMagpie;

        if (_rewardToken != address(0)) {
            rewards[_rewardToken] = Reward({
                rewardPerTokenStored: 0,
                queuedRewards: 0,
                historicalRewards: 0
            });
            rewardTokens.push(_rewardToken);

            isRewardToken[_rewardToken] = true;
        }

        managers[_rewardManager] = true;
    }

    /* ============ Modifiers ============ */

    modifier onlyManager() {
        if (!managers[msg.sender])
            revert OnlyManager();
        _;
    }

    modifier onlyMasterMagpie() {
        if (msg.sender != operator)
            revert OnlyMasterMagpie();
        _;
    }

    modifier updateReward(address _account) {
        _updateFor(_account);
        _;
    }

    modifier updateRewards(address _account, address[] memory _rewards) {
        uint256 length = _rewards.length;
        uint256 userShare = balanceOf(_account);
        
        for (uint256 index = 0; index < length; ++index) {
            address rewardToken = _rewards[index];
            // if a reward stopped queuing, no need to recalculate to save gas fee
            if (userRewardPerTokenPaid[rewardToken][_account] == rewardPerToken(rewardToken))
                continue;
            userRewards[rewardToken][_account] = _earned(_account, rewardToken, userShare);
            userRewardPerTokenPaid[rewardToken][_account] = rewardPerToken(rewardToken);
        }
        _;
    }  

    /* ============ External Getters ============ */

    /// @notice Returns current amount of staked tokens
    /// @return Returns current amount of staked tokens
    function totalStaked() public override virtual view returns (uint256) {
        return IMasterMagpie(operator).calLpSupply(stakingToken);
    }

    /// @notice Returns amount of staked tokens in master magpie by account
    /// @param _account Address account
    /// @return Returns amount of staked tokens by account
    function balanceOf(address _account) public override virtual view returns (uint256) {
        (uint256 staked, ) =  IMasterMagpie(operator).stakingInfo(stakingToken, _account);
        return staked;
    }

    function stakingDecimals() external override virtual view returns (uint256) {
        return stakingTokenDecimals;
    }

    /// @notice Returns amount of reward token per staking tokens in pool in 10**12
    /// @param _rewardToken Address reward token
    /// @return Returns amount of reward token per staking tokens in pool in 10**12
    function rewardPerToken(address _rewardToken)
        public
        override
        view
        returns (uint256)
    {
        return rewards[_rewardToken].rewardPerTokenStored;
    }

    function rewardTokenInfos()
        override
        external
        view
        returns
        (
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols
        )
    {
        uint256 rewardTokensLength = rewardTokens.length;
        bonusTokenAddresses = new address[](rewardTokensLength);
        bonusTokenSymbols = new string[](rewardTokensLength);
        for (uint256 i; i < rewardTokensLength; i++) {
            bonusTokenAddresses[i] = rewardTokens[i];
            bonusTokenSymbols[i] = IERC20Metadata(address(bonusTokenAddresses[i])).symbol();
        }
    }

    /// @notice Returns amount of reward token earned by a user
    /// @param _account Address account
    /// @param _rewardToken Address reward token
    /// @return Returns amount of reward token earned by a user
    function earned(address _account, address _rewardToken)
        public
        override
        view
        returns (uint256)
    {
        return _earned(_account, _rewardToken, balanceOf(_account));
    }

    /// @notice Returns amount of all reward tokens
    /// @param _account Address account
    /// @return pendingBonusRewards as amounts of all rewards.
    function allEarned(address _account)
        external
        override
        view
        returns (
            uint256[] memory pendingBonusRewards
        )
    {
        uint256 length = rewardTokens.length;
        pendingBonusRewards = new uint256[](length);
        for (uint256 i = 0; i < length; i++) {
            pendingBonusRewards[i] = earned(_account, rewardTokens[i]);
        }

        return pendingBonusRewards;
    }

    function getRewardLength() external view returns(uint256) {
        return rewardTokens.length;
    }    

    /* ============ External Functions ============ */

    /// @notice Updates the reward information for one account
    /// @param _account Address account
    function updateFor(address _account) override external {
        _updateFor(_account);
        emit RewardUpdatedFor(_account);
    }

    function getReward(address _account, address _receiver)
        public
        onlyMasterMagpie
        updateReward(_account)
        returns (bool)
    {
        uint256 length = rewardTokens.length;

        for (uint256 index = 0; index < length; ++index) {
            address rewardToken = rewardTokens[index];
            uint256 reward = userRewards[rewardToken][_account]; // updated during updateReward modifier
            if (reward > 0) {
                _sendReward(rewardToken, _account, _receiver, reward);
            }
        }

        return true;
    }

    function getRewards(address _account, address _receiver, address[] memory _rewardTokens) override
        external
        onlyMasterMagpie
        updateRewards(_account, _rewardTokens)
    {
        uint256 length = _rewardTokens.length;
        for (uint256 index = 0; index < length; ++index) {
            address rewardToken = _rewardTokens[index];
            uint256 reward = userRewards[rewardToken][_account]; // updated during updateReward modifier
            if (reward > 0) {
                _sendReward(rewardToken, _account, _receiver, reward);
            }
        }
    }

    /// @notice Sends new rewards to be distributed to the users staking. Only possible to donate already registered token
    /// @param _amountReward Amount of reward token to be distributed
    /// @param _rewardToken Address reward token
    function donateRewards(uint256 _amountReward, address _rewardToken) external {
        if (!isRewardToken[_rewardToken])
            revert MustBeRewardToken();

        _provisionReward(_amountReward, _rewardToken);
    }

    /* ============ Admin Functions ============ */

    function updateManager(address _rewardManager, bool _allowed) external onlyOwner {
        managers[_rewardManager] = _allowed;

        emit ManagerUpdated(_rewardManager, managers[_rewardManager]);
    }

    /// @notice Sends new rewards to be distributed to the users staking. Only callable by manager
    /// @param _amountReward Amount of reward token to be distributed
    /// @param _rewardToken Address reward token
    function queueNewRewards(uint256 _amountReward, address _rewardToken)
        override
        external
        onlyManager
        returns (bool)
    {
        if (!isRewardToken[_rewardToken]) {
            rewardTokens.push(_rewardToken);
            isRewardToken[_rewardToken] = true;
        }

        _provisionReward(_amountReward, _rewardToken);
        return true;
    }

    function emergencyWithdraw(address _rewardToken, address _to) external onlyManager {
        uint256 amount = IERC20(_rewardToken).balanceOf(address(this));
        IERC20(_rewardToken).safeTransfer(_to, amount);
        emit EmergencyWithdrawn(_to, _rewardToken, amount);
    }

    /* ============ Internal Functions ============ */

    function _provisionReward(uint256 _amountReward, address _rewardToken) internal {
        IERC20(_rewardToken).safeTransferFrom(
            msg.sender,
            address(this),
            _amountReward
        );
        Reward storage rewardInfo = rewards[_rewardToken];
        rewardInfo.historicalRewards =
            rewardInfo.historicalRewards +
            _amountReward;

        uint256 totalStake = totalStaked();
        if (totalStake == 0) {
            rewardInfo.queuedRewards += _amountReward;
        } else {
            if (rewardInfo.queuedRewards > 0) {
                _amountReward += rewardInfo.queuedRewards;
                rewardInfo.queuedRewards = 0;
            }
            rewardInfo.rewardPerTokenStored =
                rewardInfo.rewardPerTokenStored +
                (_amountReward * 10**stakingTokenDecimals * DENOMINATOR) /
                totalStake;
        }
        emit RewardAdded(_amountReward, _rewardToken);
    }

    function _earned(address _account, address _rewardToken, uint256 _userShare) internal view returns (uint256) {
        return ((_userShare *
            (rewardPerToken(_rewardToken) -
                userRewardPerTokenPaid[_rewardToken][_account])) /
            (10**stakingTokenDecimals * DENOMINATOR)) + userRewards[_rewardToken][_account];
    }

    function _sendReward(address _rewardToken, address _account, address _receiver, uint256 _amount) internal {
        userRewards[_rewardToken][_account] = 0;
        IERC20(_rewardToken).safeTransfer(_receiver, _amount);
        emit RewardPaid(_account, _receiver, _amount, _rewardToken);
    }

    function _updateFor(address _account) internal {
        uint256 length = rewardTokens.length;
        for (uint256 index = 0; index < length; ++index) {
            address rewardToken = rewardTokens[index];
            // if a reward stopped queuing, no need to recalculate to save gas fee
            if (userRewardPerTokenPaid[rewardToken][_account] == rewardPerToken(rewardToken))
                continue;

            userRewards[rewardToken][_account] = earned(_account, rewardToken);
            userRewardPerTokenPaid[rewardToken][_account] = rewardPerToken(rewardToken);
        }
    }
}
MasterMagpieV2.sol 893 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { ReentrancyGuardUpgradeable } from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
import { PausableUpgradeable } from '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol';

import "../Mgp.sol";
import "./BaseRewardPoolV4.sol";
import "../interfaces/IBaseRewardPool.sol";
import "../interfaces/IvlmgpPBaseRewarder.sol";
import "../interfaces/IHarvesttablePoolHelper.sol";
import "../interfaces/ILocker.sol";
import "../interfaces/IReferralStorage.sol";
import "../libraries/MagpieFactoryLibV2.sol";


// MasterMagpie is a boss. He says "go f your blocks lego boy, I'm gonna use timestamp instead".
// And to top it off, it takes no risks. Because the biggest risk is operator error.
// So we make it virtually impossible for the operator of this contract to cause a bug with people's harvests.
//
// Note that it's ownable and the owner wields tremendous power. The ownership
// will be transferred to a governance smart contract once MGP is sufficiently
// distributed and the community can show to govern itself.
//
/// @title A contract for managing all reward pools
/// @author Magpie Team
/// @notice You can use this contract for depositing MGP, MWOM, and Liquidity Pool tokens.
/// @dev All the ___For() function are function which are supposed to be called by other contract designed by Magpie's team


/// Note MasterMagpieV2 is only on Ethereum Mainnet right now.
/// @dev Changes in the MasterMagpieV2 are:

// The stakingToken of masterMagpie now is NOT the receipt token but the LP token of the wombat Pool

// The transfer of all staking tokens to and from the masterMagpie now has been removed except when the staking Token in mWom,
// which gets transferred to masterMagpie upon deposit and transferred from masterMagpie upon withdraw.

// The MPGRewardPool variable is only for backward compatibility, to avoid storage collision when
// upgrading the masterMagpie on Arb and BNB, removed all the logic related to it.

// Added the totalDeposit mapping to get the totalDeposit in a pool which was accomplished before by 
// 'IERC20(stakingToken).balanceOf(masterMagpie)' where staking token used to be the receipt token.
// totalDeposit gives the amount of LP staked by wombatStaking into wombat exchange.

// calLpSupply() is now public instead of internal so baseRewardPool can use it to get totalStaked 
// which was earlier achieved by IERC20(stakingToken).totalSupply() in baseRewardPool.

// removed the functions to set compounder, mgp, mwomsv and added the config methods that sets the
// vlmgp, mgp, compounder and mWomSV together.





contract MasterMagpieV2 is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable {
    using SafeERC20 for IERC20;

    /* ============ Structs ============ */

    // Info of each user.
    struct UserInfo {
        uint256 amount; // How many staking tokens the user has provided.
        uint256 rewardDebt; // Reward debt. See explanation below.
        uint256 available; // in case of locking
        //
        // We do some fancy math here. Basically, any point in time, the amount of MGPs
        // entitled to a user but is pending to be distributed is:
        //
        //   pending reward = (user.amount * pool.accMGPPerShare) - user.rewardDebt
        //
        // Whenever a user deposits or withdraws staking tokens to a pool. Here's what happens:
        //   1. The pool's `accMGPPerShare` (and `lastRewardTimestamp`) gets updated.
        //   2. User receives the pending reward sent to his/her address.
        //   3. User's `amount` gets updated.
        //   4. User's `rewardDebt` gets updated.
    }

    // Info of each pool.
    struct PoolInfo {
        address stakingToken; // Address of staking token contract to be staked.
        uint256 allocPoint; // How many allocation points assigned to this pool. MGPs to distribute per second.
        uint256 lastRewardTimestamp; // Last timestamp that MGPs distribution occurs.
        uint256 accMGPPerShare; // Accumulated MGPs per share, times 1e12. See below.
        address rewarder;
        address helper;
        bool    helperNeedsHarvest;
        uint256 totalDeposit;
        bool transferStakingToken;
    }

    /* ============ State Variables ============ */

    // The MGP TOKEN!
    MGP public mgp;

    ILocker public vlmgp;

    // MGP tokens created per second.
    uint256 public mgpPerSec;

    // Registered staking tokens 
    address[] public registeredToken;
    // Info of each pool.
    mapping(address => PoolInfo) public tokenToPoolInfo;
    // Set of all staking tokens that have been added as pools
    mapping(address => bool) private openPools;
    // Info of each user that stakes staking tokens [_staking][_account]
    mapping(address => mapping(address => UserInfo)) private userInfo;
    // Total allocation points. Must be the sum of all allocation points in all pools.
    uint256 public totalAllocPoint;
    // The timestamp when MGP mining starts.
    uint256 public startTimestamp;

    mapping(address => bool) public PoolManagers;

    address public compounder;

    /* ==== variable added for first upgrade === */

    mapping(address => bool) public MPGRewardPool; //  No use anymore, just to avoid storage collision while upgrading on Arb &BNB`

    /* ==== variable added for second upgrade === */

    mapping(address => mapping (address => uint256)) public unClaimedMgp; // unclaimed mgp reward before lastRewardTimestamp
    mapping(address => address) public legacyRewarder; // old rewarder

    /* ==== variable added for third upgrade === */

    address public referral;

    /* ==== variable added for fourth upgrade === */

    ILocker public mWomSV;
    
    /* ==== variable added for fifth upgrade === */

    address[] public AllocationManagers;

    /* ============ Events ============ */

    event Add(
        uint256 _allocPoint,
        address indexed _stakingToken,
        IBaseRewardPool indexed _rewarder
    );
    event Set(
        address indexed _stakingToken,
        uint256 _allocPoint,
        IBaseRewardPool indexed _rewarder
    );
    event Deposit(
        address indexed _user,
        address indexed _stakingToken,
        uint256 _amount
    );
    event Withdraw(
        address indexed _user,
        address indexed _stakingToken,
        uint256 _amount
    );
    event UpdatePool(
        address indexed _stakingToken,
        uint256 _lastRewardTimestamp,
        uint256 _lpSupply,
        uint256 _accMGPPerShare
    );
    event HarvestMGP(
        address indexed _account,
        address indexed _receiver,
        uint256 _amount,
        bool isLock
    );
    event UpdateEmissionRate(address indexed _user, uint256 _oldMgpPerSec, uint256 _newMgpPerSec);
    event UpdatePoolAlloc(address _stakingToken, uint256 _oldAllocPoint, uint256 _newAllocPoint);
    event PoolManagerStatus(address _account, bool _status);
    event DepositNotAvailable(address indexed _user,  address indexed _stakingToken, uint256 _amount);
    event LockFreePoolUpdated(address _stakingToken, bool _isRewardMGP);
    event LegacyRewarderSet(address _stakingToken, address _oldLegacyRewarder, address _newLegacyRewarder); 
    event ReferralSet(address _oldReferraladdress, address _newReferraladdress);
    event RewarderManagerUpdated(address _rewarder, address _manager, bool _allowed);
    event Configured(address _mgp, address _compounder, address _vlmgp, address _mWomSV);

    /* ============ Errors ============ */

    error OnlyPoolManager();
    error OnlyPoolHelper();
    error OnlyActivePool();
    error PoolExsisted();
    error InvalidStakingToken();
    error WithdrawAmountExceedsStaked();
    error UnlockAmountExceedsLocked();
    error MustBeContractOrZero();
    error OnlyCompounder();
    error OnlyLocker();
    error LengthMismatch();
    error onlyPoolsWithTokenTransfer();
    error OnlyWhiteListedAllocUpdator();
    error MustNotBeZero();
    error IndexOutOfBound();

    /* ============ Constructor ============ */    

    constructor(){
        _disableInitializers();
    }
    function __MasterMagpie_init(
        address _mgp,
        uint256 _mgpPerSec,
        uint256 _startTimestamp
    ) public initializer {

        __Ownable_init();        
        mgp = MGP(_mgp);
        mgpPerSec = _mgpPerSec;
        startTimestamp = _startTimestamp;
        totalAllocPoint = 0;
        PoolManagers[owner()] = true;
    }

    /* ============ Modifiers ============ */
    
    modifier _onlyPoolManager() {
        if (!PoolManagers[msg.sender])
            revert OnlyPoolManager();
        _;
    }

    modifier _onlyWhiteListed() {
        bool isCallerWhiteListed = false;
        for (uint i; i < AllocationManagers.length; i++) {
            if (AllocationManagers[i] == msg.sender) {
                isCallerWhiteListed = true;
                break;
            }
        }
        if (isCallerWhiteListed == true || owner() == msg.sender) {
            _;
        } else {
            revert OnlyWhiteListedAllocUpdator();
        }
    }

    modifier _onlyPoolHelper(address _stakedToken) {
        if (msg.sender != tokenToPoolInfo[_stakedToken].helper)
            revert OnlyPoolHelper();
        _;            
    }

    modifier _onlyWhiteListedLocker() {
        if(msg.sender != address(mWomSV) && msg.sender != address(vlmgp))
            revert OnlyLocker();
        _;
    }

    modifier _onlyCompounder() {
        if (msg.sender != compounder)
            revert OnlyCompounder();
        _;
    }

    /* ============ External Getters ============ */

    /// @notice Returns number of registered tokens, tokens having a registered pool.
    /// @return Returns number of registered tokens
    function poolLength() external view returns (uint256) {
        return registeredToken.length;
    }

    /// @notice Gives information about a Pool. Used for APR calculation and Front-End
    /// @param _stakingToken Staking token of the pool we want to get information from
    /// @return emission - Emissions of MGP from the contract, allocpoint - Allocated emissions of MGP to the pool,sizeOfPool - size of Pool, totalPoint total allocation points

    function getPoolInfo(address _stakingToken)
        external
        view
        returns (
            uint256 emission,
            uint256 allocpoint,
            uint256 sizeOfPool,
            uint256 totalPoint
        )
    {
        PoolInfo memory pool = tokenToPoolInfo[_stakingToken];
        return (
            (mgpPerSec * pool.allocPoint / totalAllocPoint),
            pool.allocPoint,
            calLpSupply(_stakingToken),
            totalAllocPoint
        );
    }

    /// @notice Provides available amount for a specific user for a specific pool.
    /// @param _stakingToken Staking token of the pool
    /// @param _user Address of the user

    function stakingInfo(address _stakingToken, address _user)
        public
        view
        returns (uint256 stakedAmount, uint256 availableAmount)
    {
        return (userInfo[_stakingToken][_user].amount, userInfo[_stakingToken][_user].available);
    }

    /// @notice View function to see pending reward tokens on frontend.
    /// @param _stakingToken Staking token of the pool
    /// @param _user Address of the user
    /// @param _rewardToken Specific pending reward token, apart from MGP
    /// @return pendingMGP - Expected amount of MGP the user can claim, bonusTokenAddress - token, bonusTokenSymbol - token Symbol,  pendingBonusToken - Expected amount of token the user can claim
    function pendingTokens(
        address _stakingToken,
        address _user,
        address _rewardToken
    )
        external
        view
        returns (
            uint256 pendingMGP,
            address bonusTokenAddress,
            string memory bonusTokenSymbol,
            uint256 pendingBonusToken
        )
    {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        pendingMGP = _calMGPReward(_stakingToken, _user);

        // If it's a multiple reward farm, we return info about the specific bonus token
        if (address(pool.rewarder) != address(0)) {
            (bonusTokenAddress, bonusTokenSymbol) = (
                _rewardToken,
                IERC20Metadata(_rewardToken).symbol()
            );
            pendingBonusToken = IBaseRewardPool(pool.rewarder).earned(
                _user,
                _rewardToken
            );
        }
    }

    function allPendingTokens(address _stakingToken, address _user)
        external view returns (
            uint256 pendingMGP,
            address[] memory bonusTokenAddresses,
            string[] memory bonusTokenSymbols,
            uint256[] memory pendingBonusRewards
        )
    {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        pendingMGP = _calMGPReward(_stakingToken, _user);

        // If it's a multiple reward farm, we return all info about the bonus tokens
        if (address(pool.rewarder) != address(0)) {
            (bonusTokenAddresses, bonusTokenSymbols) = IBaseRewardPool(pool.rewarder).rewardTokenInfos();
            pendingBonusRewards = IBaseRewardPool(pool.rewarder).allEarned(_user);
        }
    }

    function rewarderBonusTokenInfo(address _stakingToken) public view
        returns (address[] memory bonusTokenAddresses, string[] memory bonusTokenSymbols)
    {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if (address(pool.rewarder) == address(0)) {
            return (bonusTokenAddresses, bonusTokenSymbols);
        }

        (bonusTokenAddresses, bonusTokenSymbols) = IBaseRewardPool(pool.rewarder).rewardTokenInfos();
    }

    /* ============ External Functions ============ */

    /// @notice Deposits staking token to the pool, updates pool and distributes rewards
    /// @param _stakingToken Staking token of the pool
    /// @param _amount Amount to deposit to the pool
    function deposit(address _stakingToken, uint256 _amount) external whenNotPaused nonReentrant {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if(!pool.transferStakingToken)
            revert onlyPoolsWithTokenTransfer();
        _deposit(_stakingToken, msg.sender, _amount, false);
    }

    /// @notice Withdraw staking tokens from Master Mgapie for only pools which have staking token transferred to masterMagpie.
    /// @param _stakingToken Staking token of the pool
    /// @param _amount amount to withdraw
    function withdraw(address _stakingToken, uint256 _amount) external whenNotPaused nonReentrant {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if(!pool.transferStakingToken)
            revert onlyPoolsWithTokenTransfer();
        _withdraw(_stakingToken, msg.sender, _amount, false);
    }

    /// @notice Deposit staking tokens to Master Magpie. Can only be called by pool helper
    /// @param _stakingToken Staking token of the pool
    /// @param _amount Amount to deposit
    /// @param _for Address of the user the pool helper is depositing for, and also harvested reward will be sent to
    function depositFor(
        address _stakingToken,
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyPoolHelper(_stakingToken) nonReentrant {
        _deposit(_stakingToken, _for, _amount, false);
    }

    /// @notice Withdraw staking tokens from Mastser Magpie for a specific user. Can only be called by pool helper
    /// @param _stakingToken Staking token of the pool
    /// @param _amount amount to withdraw   
    /// @param _for address of the user to withdraw for, and also harvested reward will be sent to
    function withdrawFor(
        address _stakingToken,
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyPoolHelper(_stakingToken) nonReentrant {
        _withdraw(_stakingToken, _for, _amount, false);
    }

    /// @notice Update reward variables of the given pool to be up-to-date.
    /// @param _stakingToken Staking token of the pool
    function updatePool(address _stakingToken) public whenNotPaused {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if (block.timestamp <= pool.lastRewardTimestamp || totalAllocPoint == 0) {
            return;
        }
        uint256 lpSupply = calLpSupply(_stakingToken);
        if (lpSupply == 0) {
            pool.lastRewardTimestamp = block.timestamp;
            return;
        }        
        uint256 multiplier = block.timestamp - pool.lastRewardTimestamp;
        uint256 mgpReward = (multiplier * mgpPerSec * pool.allocPoint) / totalAllocPoint;
        
        pool.accMGPPerShare = pool.accMGPPerShare + ((mgpReward * 1e12) / lpSupply);
        pool.lastRewardTimestamp = block.timestamp;

        emit UpdatePool(
            _stakingToken,
            pool.lastRewardTimestamp,
            lpSupply,
            pool.accMGPPerShare
        );
    }    

    /// @notice Update reward variables for all pools. Be mindful of gas costs!
    function massUpdatePools() public whenNotPaused {
        for (uint256 pid = 0; pid < registeredToken.length; ++pid) {
            updatePool(registeredToken[pid]);
        }
    }

    /// @notice Claims for each of the pools with specified rewards to claim for each pool
    function multiclaimSpec(address[] calldata _stakingTokens, address[][] memory _rewardTokens)
        external whenNotPaused
    {
        _multiClaim(_stakingTokens, msg.sender, msg.sender, _rewardTokens);
    }

    /// @notice Claims for each of the pools with specified rewards to claim for each pool
    function multiclaimFor(address[] calldata _stakingTokens, address[][] memory _rewardTokens, address _account)
        external whenNotPaused
    {
        _multiClaim(_stakingTokens, _account, _account, _rewardTokens);
    }

    /// @notice Claims for each of the pools with specified rewards to claim for each pool. ONLY callable by compounder!!!!!!
    function multiclaimOnBehalf(address[] calldata _stakingTokens, address[][] memory _rewardTokens, address _account)
        external whenNotPaused _onlyCompounder
    {
        _multiClaim(_stakingTokens, _account, msg.sender, _rewardTokens);
    }

    /// @notice Claim for all rewards for the pools
    function multiclaim(address[] calldata _stakingTokens)
        external whenNotPaused
    {
        address[][] memory rewardTokens = new address[][](_stakingTokens.length);
        _multiClaim(_stakingTokens, msg.sender, msg.sender, rewardTokens);
    }

    /* ============ VLMGP interaction Functions ============ */

    function depositVlMGPFor(
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyWhiteListedLocker {
        _deposit(address(vlmgp), _for, _amount, true);
    }
    
    function withdrawVlMGPFor(
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyWhiteListedLocker {
        _withdraw(address(vlmgp), _for, _amount, true);
    }

    function depositMWomSVFor(
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyWhiteListedLocker {
        _deposit(address(mWomSV), _for, _amount, true);
    }
    
    function withdrawMWomSVFor(
        uint256 _amount,
        address _for
    ) external whenNotPaused _onlyWhiteListedLocker {
        _withdraw(address(mWomSV), _for, _amount, true);
    }    

    function calLpSupply(address _stakingToken) public view returns (uint256) {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if (_stakingToken == address(vlmgp)) {
            return IERC20(address(vlmgp)).totalSupply();
        }
        if (_stakingToken == address(mWomSV)) {
            return IERC20(address(mWomSV)).totalSupply();
        }
        return pool.totalDeposit;
    }

    /* ============ Internal Functions ============ */

    /// @notice internal function to deal with deposit staking token
    function _deposit(address _stakingToken, address _account, uint256 _amount, bool _isLock) internal {
        updatePool(_stakingToken);

        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        UserInfo storage user = userInfo[_stakingToken][_account];

        if (user.amount > 0) {
            _harvestMGP(_stakingToken, _account);
        }
        _harvestBaseRewarder(_stakingToken, _account);

        user.amount = user.amount + _amount;
        pool.totalDeposit += _amount;
        if (!_isLock) {
            user.available = user.available + _amount;
        }
        if(pool.transferStakingToken){
            IERC20(pool.stakingToken).safeTransferFrom(address(msg.sender), address(this), _amount);
        }
        user.rewardDebt = (user.amount * pool.accMGPPerShare) / 1e12;

        if (_amount > 0)
            if (!_isLock)
                emit Deposit(_account, _stakingToken, _amount);
            else
                emit DepositNotAvailable(_account, _stakingToken, _amount);
    }

    /// @notice internal function to deal with withdraw staking token
    function _withdraw(address _stakingToken, address _account, uint256 _amount, bool _isLock) internal {
        _harvestAndUnstake(_stakingToken, _account, _amount, _isLock);
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        if(pool.transferStakingToken){
            IERC20(pool.stakingToken).safeTransfer(_account, _amount);
        }
        emit Withdraw(_account, _stakingToken, _amount);
    }

    function _harvestAndUnstake(address _stakingToken, address _account, uint256 _amount, bool _isLock) internal {
        updatePool(_stakingToken);

        UserInfo storage user = userInfo[_stakingToken][_account];
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];

        if (!_isLock && user.available < _amount)
            revert WithdrawAmountExceedsStaked();
        else if(user.amount < _amount && _isLock)
            revert UnlockAmountExceedsLocked();
        
        _harvestMGP(_stakingToken, _account);
        _harvestBaseRewarder(_stakingToken, _account);

        user.amount = user.amount - _amount;
        pool.totalDeposit -= _amount;
        
        if(!_isLock)
            user.available = user.available - _amount;
        user.rewardDebt = (user.amount * tokenToPoolInfo[_stakingToken].accMGPPerShare) / 1e12;
    }

    function _multiClaim(address[] calldata _stakingTokens, address _user, address _receiver, address[][] memory _rewardTokens) internal nonReentrant {
        uint256 length = _stakingTokens.length;
        if (length != _rewardTokens.length) revert LengthMismatch();

        uint256 totalClaimableMGP;

        for (uint256 i = 0; i < length; ++i) {
            address _stakingToken = _stakingTokens[i];
            UserInfo storage user = userInfo[_stakingToken][_user];
            
            updatePool(_stakingToken);
            uint256 claimableMgp = _calNewMGP(_stakingToken, _user) + unClaimedMgp[_stakingToken][_user];

            totalClaimableMGP += claimableMgp;
            unClaimedMgp[_stakingToken][_user] = 0;
            user.rewardDebt = (user.amount * tokenToPoolInfo[_stakingToken].accMGPPerShare) / 1e12;
            _claimBaseRewarder(_stakingToken, _user, _receiver, _rewardTokens[i]);
        }

        if (totalClaimableMGP > 0) {
            _sendMGP(_user, _receiver, totalClaimableMGP);
        }

        uint256 totalReward = totalClaimableMGP;

        if (totalReward > 0 && referral != address(0)) {
            IReferralStorage(referral).trigger(_user, totalReward);
        }
    }

    /// @notice calculate MGP reward based at current timestamp, for frontend only
    function _calMGPReward(address _stakingToken, address _user) internal view returns(uint256 pendingMGP) {
        PoolInfo storage pool = tokenToPoolInfo[_stakingToken];
        UserInfo storage user = userInfo[_stakingToken][_user];
        uint256 accMGPPerShare = pool.accMGPPerShare;
        uint256 lpSupply = calLpSupply(_stakingToken);

        if (block.timestamp > pool.lastRewardTimestamp && lpSupply != 0) {
            uint256 multiplier = block.timestamp - pool.lastRewardTimestamp;
            uint256 mgpReward = (multiplier * mgpPerSec * pool.allocPoint) /
            totalAllocPoint;
            accMGPPerShare = accMGPPerShare + (mgpReward * 1e12) / lpSupply;
        }

        pendingMGP = (user.amount * accMGPPerShare) / 1e12 - user.rewardDebt;
        pendingMGP += unClaimedMgp[_stakingToken][_user];
    }

    /// @notice Harvest MGP for an account
    /// only update the reward counting but not sending them to user
    function _harvestMGP(address _stakingToken, address _account) internal {
        // Harvest MGP
        uint256 pending = _calNewMGP(_stakingToken, _account);
        unClaimedMgp[_stakingToken][_account] += pending;
    }

    /// @notice calculate MGP reward based on current accMGPPerShare
    function _calNewMGP(address _stakingToken, address _account) view internal returns(uint256) {
        UserInfo storage user = userInfo[_stakingToken][_account];
        uint256 pending = (user.amount * tokenToPoolInfo[_stakingToken].accMGPPerShare) /
            1e12 -
            user.rewardDebt;
        return pending;
    }

    /// @notice Harvest reward token in BaseRewarder for an account. NOTE: Baserewarder use user staking token balance as source to
    /// calculate reward token amount
    function _claimBaseRewarder(address _stakingToken, address _account, address _receiver, address[] memory _rewardTokens) internal {
        IBaseRewardPool rewarder = IBaseRewardPool(tokenToPoolInfo[_stakingToken].rewarder);
        if (address(rewarder) != address(0)) {
            if (_rewardTokens.length > 0)
                rewarder.getRewards(_account, _receiver, _rewardTokens);
            else
                // if not specifiying any reward token, just claim them all
                rewarder.getReward(_account, _receiver);
        }
    }

    /// only update the reward counting on in base rewarder but not sending them to user
    function _harvestBaseRewarder(address _stakingToken, address _account) internal {
        IBaseRewardPool rewarder = IBaseRewardPool(tokenToPoolInfo[_stakingToken].rewarder);
        if (address(rewarder) != address(0))
            rewarder.updateFor(_account);
    }

    function _sendMGP(address _account, address _receiver, uint256 _amount) internal {
        IERC20(mgp).safeTransfer(_receiver, _amount);

        emit HarvestMGP(_account, _receiver, _amount, false);
    }


    /* ============ Admin Functions ============ */
    /// @notice Used to give edit rights to the pools in this contract to a Pool Manager
    /// @param _account Pool Manager Adress
    /// @param _allowedManager True gives rights, False revokes them
    function setPoolManagerStatus(address _account, bool _allowedManager)
        external
        onlyOwner
    {
        PoolManagers[_account] = _allowedManager;    

        emit PoolManagerStatus(_account, PoolManagers[_account]);
    }

    function config(address _mgp, address _compounder, address _vlmgp, address _mWomSV) external onlyOwner{
        mgp= MGP(_mgp);
        compounder = _compounder;
        vlmgp = ILocker(_vlmgp);
        mWomSV = ILocker(_mWomSV);

        emit Configured(_mgp, _compounder, _vlmgp, _mWomSV);
    }

    /**
     * @dev pause pool, restricting certain operations
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @dev unpause pool, enabling certain operations
     */
    function unpause() external onlyOwner {
        _unpause();
    }   

    /// @notice Add a new pool. Can only be called by a PoolManager.
    /// @notice Careful with the _transferStakingToken, only to be set true if staking tokens
    /// are to be transferred to masterMagpie, wrongly setting it true can cause user's balance on 
    /// @param _allocPoint Allocation points of MGP to the pool
    /// @param _stakingToken Staking token of the pool
    /// @param _rewarder Address of the rewarder for the pool
    /// @param _helper Address of the helper for the pool
    /// @param _helperNeedsHarvest Helper needs harvest
    /// @param _transferStakingToken do staking tokens have to be transferred while doing 
    /// deposit or withdraw
    function add(
        uint256 _allocPoint,
        address _stakingToken,
        address _rewarder,
        address _helper,
        bool _helperNeedsHarvest,
        bool _transferStakingToken
    ) external _onlyPoolManager {
        if (!Address.isContract(address(_stakingToken)))
            revert InvalidStakingToken();

        if (!Address.isContract(address(_helper)) && address(_helper) != address(0))
            revert MustBeContractOrZero();

        if (!Address.isContract(address(_rewarder)) && address(_rewarder) != address(0))
            revert MustBeContractOrZero();

        if (openPools[_stakingToken])
            revert PoolExsisted();

        uint256 lastRewardTimestamp = block.timestamp > startTimestamp
            ? block.timestamp
            : startTimestamp;
        totalAllocPoint = totalAllocPoint + _allocPoint;
        registeredToken.push(_stakingToken);

        tokenToPoolInfo[_stakingToken] = PoolInfo({
            stakingToken: _stakingToken,
            allocPoint: _allocPoint,
            lastRewardTimestamp: lastRewardTimestamp,
            accMGPPerShare: 0,
            rewarder: _rewarder,
            helper: _helper,
            helperNeedsHarvest: _helperNeedsHarvest,
            totalDeposit: 0,
            transferStakingToken: _transferStakingToken
        });
        openPools[_stakingToken] = true;
        emit Add(_allocPoint, _stakingToken, IBaseRewardPool(_rewarder));
    }

    /// @notice Updates the given pool's MGP allocation point, rewarder address and locker address if overwritten. Can only be called by a Pool Manager.
    /// @notice Careful with the _transferStakingToken, ONLY to be set true if staking tokens are to be transferred to masterMagpie, wrongly setting 
    /// it true can cause loss of funds(currently, it is true for only mWom Pool)
    /// @param _stakingToken Staking token of the pool
    /// @param _allocPoint Allocation points of MGP to the pool
    /// @param _rewarder Address of the rewarder for the pool
    function set(
        address _stakingToken,
        uint256 _allocPoint,
        address _helper,
        address _rewarder,
        bool _helperNeedsHarvest,
        bool _transferStakingToken
    ) external _onlyPoolManager {
        if (!Address.isContract(address(_rewarder)) 
            && address(_rewarder) != address(0))
            revert MustBeContractOrZero();

        if (!Address.isContract(address(_helper)) 
            && address(_helper) != address(0))
            revert MustBeContractOrZero();            

        if (!openPools[_stakingToken])
            revert OnlyActivePool();

        massUpdatePools();

        totalAllocPoint = 
            totalAllocPoint - 
            tokenToPoolInfo[_stakingToken].allocPoint +
            _allocPoint;

        tokenToPoolInfo[_stakingToken].allocPoint = _allocPoint;
        tokenToPoolInfo[_stakingToken].rewarder = _rewarder;
        tokenToPoolInfo[_stakingToken].helper = _helper;
        tokenToPoolInfo[_stakingToken].helperNeedsHarvest = _helperNeedsHarvest;
        tokenToPoolInfo[_stakingToken].transferStakingToken = _transferStakingToken;

        emit Set(
            _stakingToken,
            _allocPoint,
            IBaseRewardPool(tokenToPoolInfo[_stakingToken].rewarder)
        );
    }

    function createRewarder(
        address _stakingToken,
        address mainRewardToken
    ) external _onlyPoolManager returns (address) {
        address rewarder = MagpieFactoryLibV2.createRewarder(
            _stakingToken,
            mainRewardToken,
            address(this),
            msg.sender
        );

        return rewarder;
    }

    function setLegacyRewarder(address _stakingToken, address _legacyRewarder) external onlyOwner {
        address _oldLegacyRewarder = legacyRewarder[_stakingToken];
        legacyRewarder[_stakingToken] = _legacyRewarder;

        emit LegacyRewarderSet(_stakingToken, _oldLegacyRewarder, _legacyRewarder);
    }

    function setReferral(address _referral) external onlyOwner {
        address oldReferral = _referral;
        referral = _referral;

        emit ReferralSet(oldReferral, _referral);
    }    

    /// @notice Update the emission rate of MGP for MasterMagpie
    /// @param _mgpPerSec new emission per second
    function updateEmissionRate(uint256 _mgpPerSec) public onlyOwner {        
        massUpdatePools();
        uint256 oldEmissionRate = mgpPerSec;
        mgpPerSec = _mgpPerSec;

        emit UpdateEmissionRate(msg.sender, oldEmissionRate, mgpPerSec);
    }

    function updatePoolsAlloc(address[] calldata _stakingTokens, uint256[] calldata _allocPoints) external onlyOwner {
        massUpdatePools();

        if (_stakingTokens.length != _allocPoints.length)
            revert LengthMismatch();

        for (uint256 i = 0; i < _stakingTokens.length; i++) {
            uint256 oldAllocPoint = tokenToPoolInfo[_stakingTokens[i]].allocPoint;

            totalAllocPoint = totalAllocPoint - oldAllocPoint + _allocPoints[i];

            tokenToPoolInfo[_stakingTokens[i]].allocPoint = _allocPoints[i];

            emit UpdatePoolAlloc(_stakingTokens[i], oldAllocPoint, _allocPoints[i]);
        }
    }

    // BaseRewarePool manager functions

    function updateRewarderManager(address _rewarder, address _manager, bool _allowed) external onlyOwner {
        IBaseRewardPool rewarder = IBaseRewardPool(_rewarder);
        rewarder.updateManager(_manager, _allowed);

        emit RewarderManagerUpdated(_rewarder, _manager, _allowed);
    }

        function addWhitelistedAllocManager(
        address _newAllocationManager
    ) external onlyOwner {
        if (_newAllocationManager == address(0)) {
            revert MustNotBeZero();
        }
        AllocationManagers.push(_newAllocationManager);
    }

    function removeWhitelistedAllocManager(uint index) external onlyOwner {
        if (index >= AllocationManagers.length) {
            revert IndexOutOfBound();
        }
        AllocationManagers[index] = AllocationManagers[
            AllocationManagers.length - 1
        ];
        AllocationManagers.pop();
    }
}
WombatPoolHelperV4.sol 233 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

import "../interfaces/IBaseRewardPool.sol";
import "../interfaces/IHarvesttablePoolHelper.sol";
import "../interfaces/wombat/IWombatStakingV2.sol";
import "../interfaces/wombat/IMasterWombat.sol";
import "../interfaces/IMasterMagpie.sol";
import "../interfaces/IMintableERC20.sol";
import "../interfaces/IWNative.sol";
import "../interfaces/ISimpleHelper.sol";
/// @title WombatPoolHelper
/// @author Magpie Team
/// @notice This contract is the main contract that user will intreact with in order to stake stable in Wombat Pool

/// @dev PoolHelperV4 is currently only on Ethereum Mainnet.
// Difference between V4 and V1 are:
// 1. The withdrawLP functionality to withdraw LP token from masterWombat
// 2. The functionality to claim rewards with withdraw of deposit tokens or LP tokens
// 3. The stakingToken(used to be the receipt token on masterMagpie) and its related logic are now removed.

contract WombatPoolHelperV4 {
    using SafeERC20 for IERC20;

    /* ============ Constants ============ */

    address public immutable depositToken; // token to deposit into wombat
    address public immutable lpToken; // lp token receive from wombat, also the pool identified on womabtStaking
    address public immutable mWom;

    address public immutable masterMagpieV2;
    address public immutable wombatStakingV2;
    address public immutable rewarderV4;

    uint256 public immutable pid; // pid on master wombat

    bool public immutable isNative;

    /* ============ Events ============ */

    event NewDeposit(address indexed _user, uint256 _amount);
    event NewLpDeposit(address indexed _user, uint256 _amount);
    event NewWithdraw(address indexed _user, uint256 _amount);
    event NewLpWithdraw(address indexed _user, uint256 _amount);

    /* ============ Errors ============ */

    error NotNativeToken();

    /* ============ Constructor ============ */

    constructor(
        uint256 _pid,
        address _depositToken,
        address _lpToken,
        address _wombatStaking,
        address _masterMagpie,
        address _rewarder,
        address _mWom,
        bool _isNative
    ) {
        pid = _pid;
        depositToken = _depositToken;
        lpToken = _lpToken;
        wombatStakingV2 = _wombatStaking;
        masterMagpieV2 = _masterMagpie;
        rewarderV4 = _rewarder;
        mWom = _mWom;
        isNative = _isNative;
    }

    /* ============ External Getters ============ */

    /// notice get the amount of total staked LP token in master magpie
    function totalStaked() external view returns (uint256) {
        return IBaseRewardPool(rewarderV4).totalStaked();
    }

    /// @notice get the total amount of shares of a user
    /// @param _address the user
    /// @return the amount of shares
    function balance(
        address _address
    ) external view returns (uint256) {
        return IBaseRewardPool(rewarderV4).balanceOf(_address);
    }

    /// @notice returns the number of pending MGP of the contract for the given pool
    /// returns pendingTokens the number of pending MGP
    function pendingWom() external view returns (uint256 pendingTokens) {
        (pendingTokens, , , ) = IMasterWombat(
            IWombatStaking(wombatStakingV2).masterWombat()
        ).pendingTokens(pid, wombatStakingV2);
    }

    /* ============ External Functions ============ */

    /// @notice deposit stables in wombat pool, autostake in master magpie
    /// @param _amount the amount of stables to deposit
    function deposit(
        uint256 _amount,
        uint256 _minimumLiquidity
    ) external {
        _deposit(_amount, _minimumLiquidity, msg.sender, msg.sender);
    }

    function depositFor(uint256 _amount, address _for) external {
        IERC20(depositToken).safeTransferFrom(
            msg.sender,
            address(this),
            _amount
        );
        IERC20(depositToken).safeApprove(wombatStakingV2, _amount);
        _deposit(_amount, 0, _for, address(this));
    }

    function depositLP(uint256 _lpAmount) external {
        IWombatStaking(wombatStakingV2).depositLP(lpToken, _lpAmount, msg.sender);
        _stake(_lpAmount, msg.sender);

        emit NewLpDeposit(msg.sender, _lpAmount);
    }

    function depositNative(uint256 _minimumLiquidity) external payable {
        if (!isNative) revert NotNativeToken();
        // Dose need to limit the amount must > 0?

        // Swap the BNB to wBNB
        _wrapNative();
        // depsoit wBNB to the pool
        IWNative(depositToken).approve(wombatStakingV2, msg.value);
        _deposit(msg.value, _minimumLiquidity, msg.sender, address(this));
        IWNative(depositToken).approve(wombatStakingV2, 0);
    }

    /// @notice withdraw stables from wombat pool, auto unstake from master Magpie
    /// @param _liquidity the amount of liquidity to withdraw
    function withdraw(
        uint256 _liquidity,
        uint256 _minAmount
    ) external {
        _withdraw(_liquidity, _minAmount, false);
    }

    function withdrawAndClaim(
        uint256 _liquidity,
        uint256 _minAmount,
        bool _isClaim
    ) external {
        _withdraw(_liquidity, _minAmount, _isClaim);
    }

    function withdrawLP(uint256 _amount, bool claim) external {
        // withdraw from wombat exchange and harvest rewards to base rewarder
        IWombatStaking(wombatStakingV2).withdrawLP(lpToken, _amount, msg.sender);
        // unstke from Master Wombat and trigger reward distribution from basereward
        _unstake(_amount, msg.sender);
        // claim all rewards
        if (claim) _claimRewards(msg.sender);
        emit NewLpWithdraw(msg.sender, _amount);
    }

    function harvest() external {
        IWombatStaking(wombatStakingV2).harvest(lpToken);
    }

    /* ============ Internal Functions ============ */

    function _withdraw(
        uint256 _liquidity,
        uint256 _minAmount,
        bool _claim
    ) internal {
        // we have to withdraw from wombat exchange to harvest reward to base rewarder
        IWombatStaking(wombatStakingV2).withdraw(
            lpToken,
            _liquidity,
            _minAmount,
            msg.sender
        );
        // then we unstake from master wombat to trigger reward distribution from basereward
        _unstake(_liquidity, msg.sender);

        if (_claim) _claimRewards(msg.sender);
        emit NewWithdraw(msg.sender, _liquidity);
    }

    function _claimRewards(address _for) internal {
        address[] memory stakingTokens = new address[](1);
        stakingTokens[0] = lpToken;
        address[][] memory rewardTokens = new address[][](1);
        IMasterMagpie(masterMagpieV2).multiclaimFor(
            stakingTokens,
            rewardTokens,
            _for
        );
    }

    function _deposit(
        uint256 _amount,
        uint256 _minimumLiquidity,
        address _for,
        address _from
    ) internal {
        uint256 lpReceived = IWombatStaking(wombatStakingV2).deposit(
            lpToken,
            _amount,
            _minimumLiquidity,
            _for,
            _from
        );
        _stake(lpReceived, _for);

        emit NewDeposit(_for, _amount);
    }

    function _wrapNative() internal {
        IWNative(depositToken).deposit{value: msg.value}();
    }

    /// @notice stake the receipt token in the masterchief of GMP on behalf of the caller
    function _stake(uint256 _amount, address _sender) internal {
        IMasterMagpie(masterMagpieV2).depositFor(lpToken, _amount, _sender);
    }

    /// @notice unstake from the masterchief of GMP on behalf of the caller
    function _unstake(uint256 _amount, address _sender) internal {
        IMasterMagpie(masterMagpieV2).withdrawFor(lpToken, _amount, _sender);
    }
}

Read Contract

AllocationManagers 0x72485abf → address
MPGRewardPool 0xcff7036a → bool
PoolManagers 0x07337f2b → bool
allPendingTokens 0x6d687fed → uint256, address[], string[], uint256[]
calLpSupply 0xb2fc784e → uint256
compounder 0xfa2cc3c0 → address
getPoolInfo 0x06bfa938 → uint256, uint256, uint256, uint256
legacyRewarder 0xdee3736c → address
mWomSV 0xff85aee4 → address
mgp 0x73806d10 → address
mgpPerSec 0x34536524 → uint256
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
pendingTokens 0xad05e627 → uint256, address, string, uint256
poolLength 0x081e3eda → uint256
referral 0x1441a5a9 → address
registeredToken 0x9c7e2655 → address
rewarderBonusTokenInfo 0x98293f81 → address[], string[]
stakingInfo 0x5750ec53 → uint256, uint256
startTimestamp 0xe6fd48bc → uint256
tokenToPoolInfo 0xc78f7420 → address, uint256, uint256, uint256, address, address, bool, uint256, bool
totalAllocPoint 0x17caf6f1 → uint256
unClaimedMgp 0x312580c7 → uint256
vlmgp 0xd00fbae7 → address

Write Contract 31 functions

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

__MasterMagpie_init 0xac318957
address _mgp
uint256 _mgpPerSec
uint256 _startTimestamp
add 0xe9b085b2
uint256 _allocPoint
address _stakingToken
address _rewarder
address _helper
bool _helperNeedsHarvest
bool _transferStakingToken
addWhitelistedAllocManager 0x61776104
address _newAllocationManager
config 0x2983a97c
address _mgp
address _compounder
address _vlmgp
address _mWomSV
createRewarder 0x3b3f0ee6
address _stakingToken
address mainRewardToken
returns: address
deposit 0x47e7ef24
address _stakingToken
uint256 _amount
depositFor 0xc8820f6c
address _stakingToken
uint256 _amount
address _for
depositMWomSVFor 0xc64b02cd
uint256 _amount
address _for
depositVlMGPFor 0x4570018c
uint256 _amount
address _for
massUpdatePools 0x630b5ba1
No parameters
multiclaim 0xcf94fdf5
address[] _stakingTokens
multiclaimFor 0x70a1198e
address[] _stakingTokens
address[][] _rewardTokens
address _account
multiclaimOnBehalf 0xeb9f994b
address[] _stakingTokens
address[][] _rewardTokens
address _account
multiclaimSpec 0x45311463
address[] _stakingTokens
address[][] _rewardTokens
pause 0x8456cb59
No parameters
removeWhitelistedAllocManager 0x698c7eb3
uint256 index
renounceOwnership 0x715018a6
No parameters
set 0xdc84aece
address _stakingToken
uint256 _allocPoint
address _helper
address _rewarder
bool _helperNeedsHarvest
bool _transferStakingToken
setLegacyRewarder 0x1b8f3aae
address _stakingToken
address _legacyRewarder
setPoolManagerStatus 0xefe33cfa
address _account
bool _allowedManager
setReferral 0x9e5914da
address _referral
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
updateEmissionRate 0x0ba84cd2
uint256 _mgpPerSec
updatePool 0x7b46c54f
address _stakingToken
updatePoolsAlloc 0x9a47ce13
address[] _stakingTokens
uint256[] _allocPoints
updateRewarderManager 0xf5d85a79
address _rewarder
address _manager
bool _allowed
withdraw 0xf3fef3a3
address _stakingToken
uint256 _amount
withdrawFor 0x6766efbe
address _stakingToken
uint256 _amount
address _for
withdrawMWomSVFor 0x8689da5d
uint256 _amount
address _for
withdrawVlMGPFor 0x84f31e96
uint256 _amount
address _for

Recent Transactions

No transactions found for this address