Address Contract Partially Verified
Address
0x90FF684Fd8060eAff3e85eAe5F5F8C3cA6f67B2d
Balance
0 ETH
Nonce
1
Code Size
15696 bytes
Creator
0x0CdB34e6...77b8 at tx 0xdc6eb375...e16712
Indexed Transactions
0
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