Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x791194eA499a36CFBA3bc5B543954c8b507847F6
Balance 0 ETH
Nonce 1
Code Size 16779 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

16779 bytes
0x608060405234801561001057600080fd5b50600436106101f75760003560e01c8063a0aae47511610120578063ca15c873116100b8578063e9dc63751161007c578063e9dc637514610507578063effeeba91461051a578063f22abf3d1461052d578063f4ccc2cd14610540578063fcbfd87d1461055357600080fd5b8063ca15c8731461048d578063cba1c51d146104a0578063d5391393146104cc578063d547741f146104e1578063e6787123146104f457600080fd5b8063a0aae475146103eb578063a217fddf146103fe578063a3246ad314610406578063af5ec6af14610426578063b8e5ff4814610439578063ba1b56741461044c578063bbd7d2091461045f578063bdc32be014610472578063c60f3ed81461047a57600080fd5b806372380c0b1161019357806372380c0b146103015780637ec01135146103145780638dfd3bd3146103275780638fdc057f1461033a5780639010d07c1461035a57806391d148541461037a578063940d430a1461038d578063969fa11d146103955780639a976f41146103a857600080fd5b806301ffc9a7146101fc578063248a9ca31461022457806324cbece214610255578063268a9fbc1461026a5780632f2ff15d1461029557806330176e13146102a857806336568abe146102bb5780634829084d146102ce57806356ca623e146102e1575b600080fd5b61020f61020a366004613132565b610566565b60405190151581526020015b60405180910390f35b61024761023236600461315c565b60009081526020819052604090206001015490565b60405190815260200161021b565b6102686102633660046131dc565b6105ca565b005b61027d6102783660046132dd565b61071f565b6040516001600160601b03909116815260200161021b565b6102686102a3366004613310565b61074c565b6102686102b6366004613374565b610777565b6102686102c9366004613310565b6107cd565b6102686102dc3660046133a9565b61084b565b6102f46102ef36600461340e565b6108b3565b60405161021b9190613485565b61026861030f366004613498565b6108cf565b610268610322366004613542565b6109b0565b6102f46103353660046135e2565b610ad1565b61034d61034836600461360c565b610b89565b60405161021b9190613627565b61036d6103683660046136d2565b610e03565b60405161021b91906136f4565b61020f610388366004613310565b610e1b565b6102f4610e44565b6102686103a33660046137ba565b610ed6565b6103c76103b6366004613848565b6001600160401b03602082901c1691565b604080516001600160401b03909316835263ffffffff90911660208301520161021b565b6102686103f9366004613498565b610efd565b610247600081565b61041961041436600461315c565b610fc1565b60405161021b9190613871565b6102686104343660046138be565b611065565b61020f6104473660046132dd565b611355565b6102f461045a3660046135e2565b611389565b6103c761046d3660046135e2565b6113bb565b6102f4611452565b610268610488366004613374565b611461565b61024761049b36600461315c565b6114aa565b61020f6104ae36600461360c565b6001600160401b031660009081526006602052604090205460ff1690565b61024760008051602061413683398151915281565b6102686104ef366004613310565b6114c1565b610268610502366004613977565b6114e7565b6102f46105153660046135e2565b61161b565b610268610528366004613a7d565b611a78565b61026861053b366004613aeb565b611b4d565b61026861054e366004613ba8565b611c36565b610268610561366004613c17565b611cf8565b60006001600160e01b0319821663e9dc637560e01b148061058b575061058b82612027565b806105a657506001600160e01b03198216635d7433ed60e11b145b806105b557506105b58261205c565b806105c457506105c48261205c565b92915050565b6000805160206141368339815191526105e38133612081565b8786811415806105f35750848114155b806105fe5750828114155b1561062757306040516381eb6c7360e01b815260040161061e91906136f4565b60405180910390fd5b60005b81811015610711576107098c8c8c8481811061064857610648613c96565b905060200201602081019061065d919061340e565b8b8b8581811061066f5761066f613c96565b9050602002016020810190610684919061360c565b8a8a8681811061069657610696613c96565b90506020020160208101906106ab9190613cac565b8989878181106106bd576106bd613c96565b90506020028101906106cf9190613cc7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611cf892505050565b60010161062a565b505050505050505050505050565b60006bffffffffffffffff00000000602084901b1661074463ffffffff841682613d23565b949350505050565b6000828152602081905260409020600101546107688133612081565b61077283836120e5565b505050565b60006107838133612081565b61078f60028484613025565b507f2e9b34e5ec7377754a85ec13c1e9a442a00db0c46dbdefbb143dd0371fd20c1c60026040516107c09190613d89565b60405180910390a1505050565b6001600160a01b038116331461083d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161061e565b6108478282612107565b5050565b6000805160206141368339815191526108648133612081565b60005b8263ffffffff168163ffffffff1610156108aa576108a287878761088b8589613e0e565b600060405180602001604052806000815250612129565b600101610867565b50505050505050565b60606001600160a01b0382166108c88161243b565b9392505050565b6000805160206141368339815191526108e88133612081565b8584811415806108f85750828114155b1561091857306040516381eb6c7360e01b815260040161061e91906136f4565b60005b818110156109a45761099c8a8a8a8481811061093957610939613c96565b905060200201602081019061094e919061340e565b89898581811061096057610960613c96565b9050602002016020810190610975919061360c565b88888681811061098757610987613c96565b905060200201602081019061088b9190613e2d565b60010161091b565b50505050505050505050565b6000805160206141368339815191526109c98133612081565b63ffffffff8616841415806109e4575063ffffffff86168214155b15610a0457306040516381eb6c7360e01b815260040161061e91906136f4565b60005b8663ffffffff168163ffffffff161015610ac457610abc8b8b8b610a2b858d613e0e565b8a8a8763ffffffff16818110610a4357610a43613c96565b9050602002016020810190610a589190613cac565b89898863ffffffff16818110610a7057610a70613c96565b9050602002810190610a829190613cc7565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061212992505050565b600101610a07565b5050505050505050505050565b6001600160a01b03821660009081526004602090815260408083208484529091529020805460609190610b0390613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054610b2f90613d4e565b8015610b7c5780601f10610b5157610100808354040283529160200191610b7c565b820191906000526020600020905b815481529060010190602001808311610b5f57829003601f168201915b5050505050905092915050565b610bd96040518060e00160405280600015158152602001600063ffffffff168152602001600063ffffffff1681526020016060815260200160608152602001600015158152602001606081525090565b6001600160401b038216600090815260066020908152604091829020825160e081018452815460ff81161515825263ffffffff6101008204811694830194909452600160281b900490921692820192909252600182018054919291606084019190610c4390613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054610c6f90613d4e565b8015610cbc5780601f10610c9157610100808354040283529160200191610cbc565b820191906000526020600020905b815481529060010190602001808311610c9f57829003601f168201915b50505050508152602001600282018054610cd590613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054610d0190613d4e565b8015610d4e5780601f10610d2357610100808354040283529160200191610d4e565b820191906000526020600020905b815481529060010190602001808311610d3157829003601f168201915b5050509183525050600382015460ff1615156020820152600482018054604090920191610d7a90613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054610da690613d4e565b8015610df35780601f10610dc857610100808354040283529160200191610df3565b820191906000526020600020905b815481529060010190602001808311610dd657829003601f168201915b5050505050815250509050919050565b60008281526001602052604081206108c8908361248f565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b606060038054610e5390613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054610e7f90613d4e565b8015610ecc5780601f10610ea157610100808354040283529160200191610ecc565b820191906000526020600020905b815481529060010190602001808311610eaf57829003601f168201915b5050505050905090565b600080516020614136833981519152610eef8133612081565b6108aa878787878787612129565b600080516020614136833981519152610f168133612081565b858481141580610f265750828114155b15610f4657306040516381eb6c7360e01b815260040161061e91906136f4565b60005b818110156109a457610fb98a8a8a84818110610f6757610f67613c96565b90506020020135898985818110610f8057610f80613c96565b9050602002016020810190610f959190613cac565b888886818110610fa757610fa7613c96565b90506020028101906105289190613cc7565b600101610f49565b60606000610fce836114aa565b90506000816001600160401b03811115610fea57610fea613718565b604051908082528060200260200182016040528015611013578160200160208202803683370190505b50905060005b8281101561105d5761102b8582610e03565b82828151811061103d5761103d613c96565b6001600160a01b0390921660209283029190910190910152600101611019565b509392505050565b60008051602061413683398151915261107e8133612081565b6001600160401b038a166110a75730604051631e8ed5b760e21b815260040161061e91906136f4565b60018963ffffffff16116110d0573060405163556f32cf60e11b815260040161061e91906136f4565b6001600160401b038a1660009081526006602052604090205460ff161561110c573060405163417e5ac560e01b815260040161061e91906136f4565b6007600061111b8c600161071f565b6001600160601b0316815260208101919091526040016000205460ff1615611158573060405163c791123b60e01b815260040161061e91906136f4565b6040518060e001604052806001151581526020018a63ffffffff168152602001600063ffffffff16815260200189898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250604080516020601f8a018190048102820181019092528881529181019190899089908190840183828082843760009201919091525050509082525085151560208083019190915260408051601f8701839004830281018301825286815292019190869086908190840183828082843760009201829052509390945250506001600160401b038d1681526006602090815260409182902084518154868401519487015164ffffffffff1990911691151564ffffffff0019169190911761010063ffffffff958616021768ffffffff00000000001916600160281b9490911693909302929092178255606084015180519293506112c292600185019291909101906130a9565b50608082015180516112de9160028401916020909101906130a9565b5060a082015160038201805460ff191691151591909117905560c082015180516113129160048401916020909101906130a9565b50506040516001600160401b038c1691507f17e87b0d3633946279e63e9c5224728714ff4e3b70d9ce4f96315a3dfcb7e92390600090a250505050505050505050565b600060076000611365858561071f565b6001600160601b0316815260208101919091526040016000205460ff169392505050565b6001600160a01b03821660009081526005602090815260408083208484529091529020805460609190610b0390613d4e565b6001600160a01b038216600090815260086020908152604080832084845290915281205481906001600160601b03168061140a573060405163de6137d160e01b815260040161061e91906136f4565b6001600160a01b03851660009081526008602090815260408083208784528252909120549081901c6001600160401b0316906001600160601b03165b90969095509350505050565b606060028054610e5390613d4e565b600061146d8133612081565b61147960038484613025565b507f4f7104b3d6fe172ea68e4578b86f09da03e0a40a893443c2f36a29c298eb5dfc60036040516107c09190613d89565b60008181526001602052604081206105c49061249b565b6000828152602081905260409020600101546114dd8133612081565b6107728383612107565b6000805160206141368339815191526115008133612081565b8988811415806115105750868114155b8061151b5750848114155b806115265750828114155b1561154657306040516381eb6c7360e01b815260040161061e91906136f4565b60005b8181101561160b576116038e8e8e8481811061156757611567613c96565b905060200201602081019061157c919061340e565b8d8d8581811061158e5761158e613c96565b90506020020160208101906115a3919061360c565b8c8c868181106115b5576115b5613c96565b90506020020160208101906115ca9190613e2d565b8b8b878181106115dc576115dc613c96565b90506020020160208101906115f19190613cac565b8a8a88818110610a7057610a70613c96565b600101611549565b5050505050505050505050505050565b6001600160a01b038216600090815260056020908152604080832084845290915281208054606092919061164e90613d4e565b80601f016020809104026020016040519081016040528092919081815260200182805461167a90613d4e565b80156116c75780601f1061169c576101008083540402835291602001916116c7565b820191906000526020600020905b8154815290600101906020018083116116aa57829003601f168201915b5050505050905080516000146116de5790506105c4565b6001600160a01b038416600090815260086020908152604080832086845282528083205480831c6001600160401b031680855260068452828520835160e081018552815460ff81161515825263ffffffff6101008204811697830197909752600160281b90049095169385019390935260018301805491956001600160601b039093169492939160608401919061177490613d4e565b80601f01602080910402602001604051908101604052809291908181526020018280546117a090613d4e565b80156117ed5780601f106117c2576101008083540402835291602001916117ed565b820191906000526020600020905b8154815290600101906020018083116117d057829003601f168201915b5050505050815260200160028201805461180690613d4e565b80601f016020809104026020016040519081016040528092919081815260200182805461183290613d4e565b801561187f5780601f106118545761010080835404028352916020019161187f565b820191906000526020600020905b81548152906001019060200180831161186257829003601f168201915b5050509183525050600382015460ff16151560208201526004820180546040909201916118ab90613d4e565b80601f01602080910402602001604051908101604052809291908181526020018280546118d790613d4e565b80156119245780601f106118f957610100808354040283529160200191611924565b820191906000526020600020905b81548152906001019060200180831161190757829003601f168201915b505050505081525050905080600001511561195057611945878785856124a5565b9450505050506105c4565b6001600160a01b03871660009081526004602090815260408083208984529091528120805461197e90613d4e565b80601f01602080910402602001604051908101604052809291908181526020018280546119aa90613d4e565b80156119f75780601f106119cc576101008083540402835291602001916119f7565b820191906000526020600020905b8154815290600101906020018083116119da57829003601f168201915b505050505090508051600014611a3657600381604051602001611a1b929190613eb7565b604051602081830303815290604052955050505050506105c4565b6002611a41896108b3565b611a4a896128b3565b604051602001611a5c93929190613ed3565b6040516020818303038152906040529550505050505092915050565b600080516020614136833981519152611a918133612081565b6001600160a01b03861660009081526008602090815260408083208884529091529020546001600160601b0316611add573060405163de6137d160e01b815260040161061e91906136f4565b8315611b17576001600160a01b03861660009081526005602090815260408083208884529091529020611b11908484613025565b50611b45565b6001600160a01b038616600090815260046020908152604080832088845290915290206108aa908484613025565b505050505050565b600080516020614136833981519152611b668133612081565b6001600160401b03891660009081526006602052604090205460ff16611ba1573060405163b462b73960e01b815260040161061e91906136f4565b6001600160401b0389166000908152600660205260409020611bc7600182018a8a613025565b50611bd6600282018888613025565b5060038101805460ff1916861515179055611bf5600482018585613025565b506040516001600160401b038b16907f62f083d21da586e0e7c60e6b23cc25bddaa27044efa74af5709aee9f608f3baa90600090a250505050505050505050565b600080516020614136833981519152611c4f8133612081565b83828114611c7257306040516381eb6c7360e01b815260040161061e91906136f4565b60005b81811015611cee57611ce688888884818110611c9357611c93613c96565b9050602002016020810190611ca8919061340e565b878785818110611cba57611cba613c96565b9050602002016020810190611ccf919061360c565b600060405180602001604052806000815250611cf8565b600101611c75565b5050505050505050565b600080516020614136833981519152611d118133612081565b6001600160a01b038516611d3a57306040516302a042e760e11b815260040161061e91906136f4565b6001600160401b038416611d635730604051631e8ed5b760e21b815260040161061e91906136f4565b6000611d7085600161071f565b6001600160601b03811660009081526007602052604090205490915060ff1615611daf57306040516355e1a76960e01b815260040161061e91906136f4565b6001600160401b03851660009081526006602052604090205460ff1615611deb57306040516308e3b3ff60e11b815260040161061e91906136f4565b6001600160601b038116600090815260076020526040808220805460ff1916600117905551630525194b60e31b81526001600160a01b03891690632928ca5890611e39908a906004016136f4565b602060405180830381600087803b158015611e5357600080fd5b505af1158015611e67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8b9190613f19565b90508351600014611f06578415611ed3576001600160a01b038816600090815260056020908152604080832084845282529091208551611ecd928701906130a9565b50611f06565b6001600160a01b038816600090815260046020908152604080832084845282529091208551611f04928701906130a9565b505b6001600160a01b038816600081815260086020908152604080832085845282529182902080546001600160601b0319166001600160601b0387161790559051600181526001600160401b03891692849290917faaf99129db1f61b460485a1534e32eba23e0a11651060a62f560e1fbbe63837a91015b60405180910390a45050505050505050565b611f988282610e1b565b610847576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055611fce3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006108c8836001600160a01b0384166129b0565b60006001600160e01b03198216637005caad60e01b14806105c457506301ffc9a760e01b6001600160e01b03198316146105c4565b60006001600160e01b03198216635a05180f60e01b14806105c457506105c4826129ff565b61208b8282610e1b565b610847576120a3816001600160a01b03166014612a24565b6120ae836020612a24565b6040516020016120bf929190613f32565b60408051601f198184030181529082905262461bcd60e51b825261061e91600401613485565b6120ef8282611f8e565b60008281526001602052604090206107729082612012565b6121118282612bbf565b60008281526001602052604090206107729082612c24565b6001600160a01b03851661215257306040516302a042e760e11b815260040161061e91906136f4565b6001600160401b03841660009081526006602052604090205460ff1661218d573060405163b462b73960e01b815260040161061e91906136f4565b63ffffffff83166121b357306040516301474afd60e21b815260040161061e91906136f4565b6001600160401b03841660009081526006602052604090205463ffffffff610100909104811690841611156121fd573060405163979b28e360e01b815260040161061e91906136f4565b6000612209858561071f565b6001600160601b03811660009081526007602052604090205490915060ff161561224857306040516355e1a76960e01b815260040161061e91906136f4565b6001600160601b0381166000908152600760209081526040808320805460ff191660019081179091556001600160401b0389168452600690925290912080546005906122a390849063ffffffff600160281b90910416613e0e565b92506101000a81548163ffffffff021916908363ffffffff1602179055506000876001600160a01b0316632928ca58886040518263ffffffff1660e01b81526004016122ef91906136f4565b602060405180830381600087803b15801561230957600080fd5b505af115801561231d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123419190613f19565b905082516000146123bc578315612389576001600160a01b038816600090815260056020908152604080832084845282529091208451612383928601906130a9565b506123bc565b6001600160a01b0388166000908152600460209081526040808320848452825290912084516123ba928601906130a9565b505b6001600160a01b038816600081815260086020908152604080832085845282529182902080546001600160601b0319166001600160601b038716179055905163ffffffff881681526001600160401b03891692849290917faaf99129db1f61b460485a1534e32eba23e0a11651060a62f560e1fbbe63837a9101611f7c565b6060816124625750506040805180820190915260048152630307830360e41b602082015290565b8160005b8115612485578061247681613fa1565b915050600882901c9150612466565b6107448482612a24565b60006108c88383612c39565b60006105c4825490565b6001600160a01b03841660009081526004602090815260408083208684529091528120805460609291906124d890613d4e565b80601f016020809104026020016040519081016040528092919081815260200182805461250490613d4e565b80156125515780601f1061252657610100808354040283529160200191612551565b820191906000526020600020905b81548152906001019060200180831161253457829003601f168201915b5050506001600160401b0387166000908152600660209081526040808320815160e081018352815460ff81161515825263ffffffff6101008204811695830195909552600160281b900490931691830191909152600181018054969750929591945092506060840191906125c490613d4e565b80601f01602080910402602001604051908101604052809291908181526020018280546125f090613d4e565b801561263d5780601f106126125761010080835404028352916020019161263d565b820191906000526020600020905b81548152906001019060200180831161262057829003601f168201915b5050505050815260200160028201805461265690613d4e565b80601f016020809104026020016040519081016040528092919081815260200182805461268290613d4e565b80156126cf5780601f106126a4576101008083540402835291602001916126cf565b820191906000526020600020905b8154815290600101906020018083116126b257829003601f168201915b5050509183525050600382015460ff16151560208201526004820180546040909201916126fb90613d4e565b80601f016020809104026020016040519081016040528092919081815260200182805461272790613d4e565b80156127745780601f1061274957610100808354040283529160200191612774565b820191906000526020600020905b81548152906001019060200180831161275757829003601f168201915b5050505050815250509050806080015151600014801561279357508151155b156127d65760026127a3886108b3565b6127ac886128b3565b6040516020016127be93929190613ed3565b60405160208183030381529060405292505050610744565b60006127e186612c63565b905082516000146128185780836040516020016127ff929190613fbc565b6040516020818303038152906040529350505050610744565b8160a00151156128815760c0820151511561285a578082608001516128428763ffffffff166128b3565b8460c001516040516020016127ff9493929190613fe2565b80826080015161286f8763ffffffff166128b3565b6040516020016127ff93929190614039565b6080820151604051612897918391602001613fbc565b6040516020818303038152906040529350505050949350505050565b6060816128d75750506040805180820190915260018152600360fc1b602082015290565b8160005b811561290157806128eb81613fa1565b91506128fa9050600a83614092565b91506128db565b6000816001600160401b0381111561291b5761291b613718565b6040519080825280601f01601f191660200182016040528015612945576020820181803683370190505b5090505b84156107445761295a6001836140a6565b9150612967600a866140bd565b6129729060306140d1565b60f81b81838151811061298757612987613c96565b60200101906001600160f81b031916908160001a9053506129a9600a86614092565b9450612949565b60008181526001830160205260408120546129f7575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105c4565b5060006105c4565b60006001600160e01b03198216637965db0b60e01b14806105c457506105c482612027565b60606000612a338360026140e9565b612a3e9060026140d1565b6001600160401b03811115612a5557612a55613718565b6040519080825280601f01601f191660200182016040528015612a7f576020820181803683370190505b509050600360fc1b81600081518110612a9a57612a9a613c96565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612ac957612ac9613c96565b60200101906001600160f81b031916908160001a9053506000612aed8460026140e9565b612af89060016140d1565b90505b6001811115612b70576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612b2c57612b2c613c96565b1a60f81b828281518110612b4257612b42613c96565b60200101906001600160f81b031916908160001a90535060049490941c93612b6981614108565b9050612afb565b5083156108c85760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161061e565b612bc98282610e1b565b15610847576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006108c8836001600160a01b038416612f32565b6000826000018281548110612c5057612c50613c96565b9060005260206000200154905092915050565b6001600160401b0381166000908152600660209081526040808320815160e081018352815460ff81161515825263ffffffff6101008204811695830195909552600160281b90049093169183019190915260018101805460609493929183860191612ccd90613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054612cf990613d4e565b8015612d465780601f10612d1b57610100808354040283529160200191612d46565b820191906000526020600020905b815481529060010190602001808311612d2957829003601f168201915b50505050508152602001600282018054612d5f90613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054612d8b90613d4e565b8015612dd85780601f10612dad57610100808354040283529160200191612dd8565b820191906000526020600020905b815481529060010190602001808311612dbb57829003601f168201915b5050509183525050600382015460ff1615156020820152600482018054604090920191612e0490613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054612e3090613d4e565b8015612e7d5780601f10612e5257610100808354040283529160200191612e7d565b820191906000526020600020905b815481529060010190602001808311612e6057829003601f168201915b5050505050815250509050806060015151600014612e9f576060015192915050565b60038054612eac90613d4e565b80601f0160208091040260200160405190810160405280929190818152602001828054612ed890613d4e565b8015612f255780601f10612efa57610100808354040283529160200191612f25565b820191906000526020600020905b815481529060010190602001808311612f0857829003601f168201915b5050505050915050919050565b6000818152600183016020526040812054801561301b576000612f566001836140a6565b8554909150600090612f6a906001906140a6565b9050818114612fcf576000866000018281548110612f8a57612f8a613c96565b9060005260206000200154905080876000018481548110612fad57612fad613c96565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612fe057612fe061411f565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105c4565b60009150506105c4565b82805461303190613d4e565b90600052602060002090601f0160209004810192826130535760008555613099565b82601f1061306c5782800160ff19823516178555613099565b82800160010185558215613099579182015b8281111561309957823582559160200191906001019061307e565b506130a592915061311d565b5090565b8280546130b590613d4e565b90600052602060002090601f0160209004810192826130d75760008555613099565b82601f106130f057805160ff1916838001178555613099565b82800160010185558215613099579182015b82811115613099578251825591602001919060010190613102565b5b808211156130a5576000815560010161311e565b60006020828403121561314457600080fd5b81356001600160e01b0319811681146108c857600080fd5b60006020828403121561316e57600080fd5b5035919050565b80356001600160a01b038116811461318c57600080fd5b919050565b60008083601f8401126131a357600080fd5b5081356001600160401b038111156131ba57600080fd5b6020830191508360208260051b85010111156131d557600080fd5b9250929050565b600080600080600080600080600060a08a8c0312156131fa57600080fd5b6132038a613175565b985060208a01356001600160401b038082111561321f57600080fd5b61322b8d838e01613191565b909a50985060408c013591508082111561324457600080fd5b6132508d838e01613191565b909850965060608c013591508082111561326957600080fd5b6132758d838e01613191565b909650945060808c013591508082111561328e57600080fd5b5061329b8c828d01613191565b915080935050809150509295985092959850929598565b80356001600160401b038116811461318c57600080fd5b803563ffffffff8116811461318c57600080fd5b600080604083850312156132f057600080fd5b6132f9836132b2565b9150613307602084016132c9565b90509250929050565b6000806040838503121561332357600080fd5b8235915061330760208401613175565b60008083601f84011261334557600080fd5b5081356001600160401b0381111561335c57600080fd5b6020830191508360208285010111156131d557600080fd5b6000806020838503121561338757600080fd5b82356001600160401b0381111561339d57600080fd5b61144685828601613333565b600080600080600060a086880312156133c157600080fd5b6133ca86613175565b94506133d860208701613175565b93506133e6604087016132b2565b92506133f4606087016132c9565b9150613402608087016132c9565b90509295509295909350565b60006020828403121561342057600080fd5b6108c882613175565b60005b8381101561344457818101518382015260200161342c565b83811115613453576000848401525b50505050565b60008151808452613471816020860160208601613429565b601f01601f19169290920160200192915050565b6020815260006108c86020830184613459565b60008060008060008060006080888a0312156134b357600080fd5b6134bc88613175565b965060208801356001600160401b03808211156134d857600080fd5b6134e48b838c01613191565b909850965060408a01359150808211156134fd57600080fd5b6135098b838c01613191565b909650945060608a013591508082111561352257600080fd5b5061352f8a828b01613191565b989b979a50959850939692959293505050565b600080600080600080600080600060e08a8c03121561356057600080fd5b6135698a613175565b985061357760208b01613175565b975061358560408b016132b2565b965061359360608b016132c9565b95506135a160808b016132c9565b945060a08a01356001600160401b03808211156135bd57600080fd5b6135c98d838e01613191565b909650945060c08c013591508082111561328e57600080fd5b600080604083850312156135f557600080fd5b6135fe83613175565b946020939093013593505050565b60006020828403121561361e57600080fd5b6108c8826132b2565b6020815281511515602082015263ffffffff60208301511660408201526000604083015161365d606084018263ffffffff169052565b50606083015160e06080840152613678610100840182613459565b90506080840151601f19808584030160a08601526136968383613459565b925060a086015191506136ad60c086018315159052565b60c08601519150808584030160e0860152506136c98282613459565b95945050505050565b600080604083850312156136e557600080fd5b50508035926020909101359150565b6001600160a01b0391909116815260200190565b8035801515811461318c57600080fd5b634e487b7160e01b600052604160045260246000fd5b600082601f83011261373f57600080fd5b81356001600160401b038082111561375957613759613718565b604051601f8301601f19908116603f0116810190828211818310171561378157613781613718565b8160405283815286602085880101111561379a57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060008060008060c087890312156137d357600080fd5b6137dc87613175565b95506137ea60208801613175565b94506137f8604088016132b2565b9350613806606088016132c9565b925061381460808801613708565b915060a08701356001600160401b0381111561382f57600080fd5b61383b89828a0161372e565b9150509295509295509295565b60006020828403121561385a57600080fd5b81356001600160601b03811681146108c857600080fd5b6020808252825182820181905260009190848201906040850190845b818110156138b25783516001600160a01b03168352928401929184019160010161388d565b50909695505050505050565b600080600080600080600080600060c08a8c0312156138dc57600080fd5b6138e58a6132b2565b98506138f360208b016132c9565b975060408a01356001600160401b038082111561390f57600080fd5b61391b8d838e01613333565b909950975060608c013591508082111561393457600080fd5b6139408d838e01613333565b909750955085915061395460808d01613708565b945060a08c013591508082111561396a57600080fd5b5061329b8c828d01613333565b600080600080600080600080600080600060c08c8e03121561399857600080fd5b6139a18c613175565b9a506001600160401b038060208e013511156139bc57600080fd5b6139cc8e60208f01358f01613191565b909b50995060408d01358110156139e257600080fd5b6139f28e60408f01358f01613191565b909950975060608d0135811015613a0857600080fd5b613a188e60608f01358f01613191565b909750955060808d0135811015613a2e57600080fd5b613a3e8e60808f01358f01613191565b909550935060a08d0135811015613a5457600080fd5b50613a658d60a08e01358e01613191565b81935080925050509295989b509295989b9093969950565b600080600080600060808688031215613a9557600080fd5b613a9e86613175565b945060208601359350613ab360408701613708565b925060608601356001600160401b03811115613ace57600080fd5b613ada88828901613333565b969995985093965092949392505050565b60008060008060008060008060a0898b031215613b0757600080fd5b613b10896132b2565b975060208901356001600160401b0380821115613b2c57600080fd5b613b388c838d01613333565b909950975060408b0135915080821115613b5157600080fd5b613b5d8c838d01613333565b9097509550859150613b7160608c01613708565b945060808b0135915080821115613b8757600080fd5b50613b948b828c01613333565b999c989b5096995094979396929594505050565b600080600080600060608688031215613bc057600080fd5b613bc986613175565b945060208601356001600160401b0380821115613be557600080fd5b613bf189838a01613191565b90965094506040880135915080821115613c0a57600080fd5b50613ada88828901613191565b600080600080600060a08688031215613c2f57600080fd5b613c3886613175565b9450613c4660208701613175565b9350613c54604087016132b2565b9250613c6260608701613708565b915060808601356001600160401b03811115613c7d57600080fd5b613c898882890161372e565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613cbe57600080fd5b6108c882613708565b6000808335601e19843603018112613cde57600080fd5b8301803591506001600160401b03821115613cf857600080fd5b6020019150368190038213156131d557600080fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160601b03808316818516808303821115613d4557613d45613d0d565b01949350505050565b600181811c90821680613d6257607f821691505b60208210811415613d8357634e487b7160e01b600052602260045260246000fd5b50919050565b6000602080835260008454613d9d81613d4e565b80848701526040600180841660008114613dbe5760018114613dd257613e00565b60ff19851689840152606089019550613e00565b896000528660002060005b85811015613df85781548b8201860152908301908801613ddd565b8a0184019650505b509398975050505050505050565b600063ffffffff808316818516808303821115613d4557613d45613d0d565b600060208284031215613e3f57600080fd5b6108c8826132c9565b60008154613e5581613d4e565b60018281168015613e6d5760018114613e7e57613ead565b60ff19841687528287019450613ead565b8560005260208060002060005b85811015613ea45781548a820152908401908201613e8b565b50505082870194505b5050505092915050565b6000613ec38285613e48565b8351613d45818360208801613429565b6000613edf8286613e48565b8451613eef818360208901613429565b602d60f81b91019081528351613f0c816001840160208801613429565b0160010195945050505050565b600060208284031215613f2b57600080fd5b5051919050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351613f64816017850160208801613429565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613f95816028840160208801613429565b01602801949350505050565b6000600019821415613fb557613fb5613d0d565b5060010190565b60008351613fce818460208801613429565b835190830190613d45818360208801613429565b60008551613ff4818460208a01613429565b855190830190614008818360208a01613429565b855191019061401b818360208901613429565b845191019061402e818360208801613429565b019695505050505050565b6000845161404b818460208901613429565b84519083019061405f818360208901613429565b8451910190614072818360208801613429565b0195945050505050565b634e487b7160e01b600052601260045260246000fd5b6000826140a1576140a161407c565b500490565b6000828210156140b8576140b8613d0d565b500390565b6000826140cc576140cc61407c565b500690565b600082198211156140e4576140e4613d0d565b500190565b600081600019048311821515161561410357614103613d0d565b500290565b60008161411757614117613d0d565b506000190190565b634e487b7160e01b600052603160045260246000fdfe9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6a264697066735822122060648318e5dca2401b23884beabb0f2f939617a5bde89f604ea379a65975a23064736f6c63430008090033

Verified Source Code Full Match

Compiler: v0.8.9+commit.e5eed63a EVM: london Optimization: Yes (100 runs)
TokengateManifoldExtension.sol 803 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.9;

import "@manifoldxyz/creator-core-solidity/contracts/core/IERC721CreatorCore.sol";
import "@manifoldxyz/creator-core-solidity/contracts/extensions/CreatorExtension.sol";
import "@manifoldxyz/creator-core-solidity/contracts/extensions/ICreatorExtensionTokenURI.sol";

import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

import "./ITokengateManifoldExtension.sol";

/**
 * @title Default implementation of the ITokengateManifoldExtension interface.
 *
 * See {ITokengateManifoldExtension} for more information and a detailed explanation on the way the
 * token URI generation works.
 *
 * @author DSENT AG, www.dsent.com
 */
contract TokengateManifoldExtension is
    ITokengateManifoldExtension,
    CreatorExtension,
    ICreatorExtensionTokenURI,
    AccessControlEnumerable
{
    using Strings for uint256;

    /**
     * @dev Role for all addresses that are authorized to mint tokens through this extension.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /**
     * @dev Variable that stores the base token URI that is used during token URI generation. The base
     * token URI is used for editions that have neither a token URI suffix nor a full token URI specified.
     *
     * Refer to the 'Token URI Generation' section in {ITokengateManifoldExtension} for more information on this variable.
     */
    string private _baseTokenURI;

    /**
     * @dev Variable that stores the default token URI prefix that is used during token URI generation.
     * The default token URI prefix is used for editions that have a token URI suffix defined.
     *
     * Refer to the 'Token URI Generation' section in {ITokengateManifoldExtension} for more information on this variable.
     */
    string private _defaultTokenURIPrefix;

    /**
     * @dev Variable that stores custom token URI suffixes for single editions or editions of a series.
     *
     * Note: Maps creator addresses => tokenIds => token URI suffixes
     *
     * Refer to the 'Token URI Generation' section in {ITokengateManifoldExtension} for more information on this variable.
     */
    mapping(address => mapping(uint256 => string)) private _tokenURISuffixMap;

    /**
     * @dev Variable that stores full token URIs for single editions or editions of a series.
     *
     * Note: Maps creator addresses => tokenIds => full token URIs
     *
     * Refer to the 'Token URI Generation' section in {ITokengateManifoldExtension} for more information on this variable.
     */
    mapping(address => mapping(uint256 => string)) private _fullTokenURIMap;

    /**
     * @dev Variable that stores all series created for the specified project ids.
     *
     * Note: Maps project ids => Series custom types
     */
    mapping(uint64 => Series) private _seriesMap;

    /**
     * @dev Variable that stores whether a given edition id has been minted or not. Edition ids are
     * created by bit shifting a 64-bit project id with a 32-bit edition number into a 96-bit uint.
     *
     * Note: Maps edition ids => edition minted booleans
     */
    mapping(uint96 => bool) private _mintedEditionIdMap;

    /**
     * @dev Variable that stores edition ids for all created token ids. Edition ids consist of a
     * project id and edition number and are stored by bit shifting the 64-bit project id with the
     * 32-bit edition number into a 96-bit uint.
     *
     * Note: Maps creator addresses => token ids => edition ids
     */
    mapping(address => mapping(uint256 => uint96)) private _editionIdMap;

    /**
     * @dev Note: Declaring a constructor `payable` reduces the deployed EVM bytecode by 10 opcodes.
     */
    constructor(string memory baseTokenURI, string memory defaultTokenURIPrefix)
        payable
    {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _baseTokenURI = baseTokenURI;
        _defaultTokenURIPrefix = defaultTokenURIPrefix;
    }

    /**
     * @dev Check whether a given interface is supported by this extension.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(CreatorExtension, IERC165, AccessControlEnumerable)
        returns (bool)
    {
        return
            interfaceId == type(ICreatorExtensionTokenURI).interfaceId ||
            CreatorExtension.supportsInterface(interfaceId) ||
            interfaceId == type(ITokengateManifoldExtension).interfaceId ||
            AccessControlEnumerable.supportsInterface(interfaceId) ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {ITokengateManifoldExtension-createSeries}.
     */
    function createSeries(
        uint64 projectId,
        uint32 editionSize,
        string calldata tokenURIPrefix,
        string calldata tokenURISuffix,
        bool addEditionToTokenURISuffix,
        string calldata tokenURIExtension
    ) external onlyRole(MINTER_ROLE) {
        if (projectId == 0) {
            revert ProjectIdMustBePositive(address(this));
        }
        if (editionSize <= 1) {
            revert EditionSizeMustBeGreaterThanOne(address(this));
        }
        if (_seriesMap[projectId].hasEntry) {
            revert SeriesAlreadyCreated(address(this));
        }
        if (_mintedEditionIdMap[createEditionId(projectId, 1)]) {
            revert ProjectIsMintedAsSingleEdition(address(this));
        }

        _seriesMap[projectId] = Series(
            true,
            editionSize,
            0,
            tokenURIPrefix,
            tokenURISuffix,
            addEditionToTokenURISuffix,
            tokenURIExtension
        );

        emit SeriesCreated(projectId);
    }

    /**
     * @dev See {ITokengateManifoldExtension-setSeriesParams}.
     */
    function setSeriesParams(
        uint64 projectId,
        string calldata tokenURIPrefix,
        string calldata tokenURISuffix,
        bool addEditionToTokenURISuffix,
        string calldata tokenURIExtension
    ) external onlyRole(MINTER_ROLE) {
        if (!_seriesMap[projectId].hasEntry) {
            revert SeriesNotFound(address(this));
        }

        Series storage series = _seriesMap[projectId];
        series.tokenURIPrefix = tokenURIPrefix;
        series.tokenURISuffix = tokenURISuffix;
        series.addEditionToTokenURISuffix = addEditionToTokenURISuffix;
        series.tokenURIExtension = tokenURIExtension;

        emit SeriesParamsSet(projectId);
    }

    /**
     * @dev See {ITokengateManifoldExtension-getSeries}.
     */
    function getSeries(uint64 projectId) external view returns (Series memory) {
        return _seriesMap[projectId];
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSeries}.
     */
    function mintSeries(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 editionNumber,
        bool isFullTokenURI,
        string memory tokenURIData
    ) external onlyRole(MINTER_ROLE) {
        _mintSeries(
            creator,
            recipient,
            projectId,
            editionNumber,
            isFullTokenURI,
            tokenURIData
        );
    }

    /**
     * @dev Internal function used to mint editions of a series.
     */
    function _mintSeries(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 editionNumber,
        bool isFullTokenURI,
        string memory tokenURIData
    ) internal {
        if (recipient == address(0)) {
            revert ZeroAddressNotAllowed(address(this));
        }
        if (!_seriesMap[projectId].hasEntry) {
            revert SeriesNotFound(address(this));
        }

        if (editionNumber == 0) {
            revert EditionNumberMustBePositive(address(this));
        }

        if (editionNumber > _seriesMap[projectId].editionSize) {
            revert EditionNumberExceedsEditionSize(address(this));
        }

        uint96 editionId = createEditionId(projectId, editionNumber);
        if (_mintedEditionIdMap[editionId]) {
            revert EditionAlreadyMinted(address(this));
        }
        _mintedEditionIdMap[editionId] = true;
        _seriesMap[projectId].editionCount += 1;

        uint256 tokenId = IERC721CreatorCore(creator).mintExtension(recipient);

        if (bytes(tokenURIData).length != 0) {
            if (isFullTokenURI) {
                _fullTokenURIMap[creator][tokenId] = tokenURIData;
            } else {
                _tokenURISuffixMap[creator][tokenId] = tokenURIData;
            }
        }

        _editionIdMap[creator][tokenId] = editionId;

        emit EditionMinted(creator, tokenId, projectId, editionNumber);
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSeriesBatch1}.
     */
    function mintSeriesBatch1(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 startEditionNumber,
        uint32 nbEditions
    ) external onlyRole(MINTER_ROLE) {
        for (uint32 i = 0; i < nbEditions; ) {
            _mintSeries(
                creator,
                recipient,
                projectId,
                startEditionNumber + i,
                false,
                ""
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSeriesBatch1}.
     */
    function mintSeriesBatch1(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 startEditionNumber,
        uint32 nbEditions,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external onlyRole(MINTER_ROLE) {
        if (
            isFullTokenURIs.length != nbEditions ||
            tokenURIData.length != nbEditions
        ) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint32 i = 0; i < nbEditions; ) {
            _mintSeries(
                creator,
                recipient,
                projectId,
                startEditionNumber + i,
                isFullTokenURIs[i],
                tokenURIData[i]
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSeriesBatchN}.
     */
    function mintSeriesBatchN(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        uint32[] calldata editionNumbers
    ) external onlyRole(MINTER_ROLE) {
        uint256 batchSize = recipients.length;
        if (
            projectIds.length != batchSize || editionNumbers.length != batchSize
        ) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint256 i = 0; i < batchSize; ) {
            _mintSeries(
                creator,
                recipients[i],
                projectIds[i],
                editionNumbers[i],
                false,
                ""
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSeriesBatchN}.
     */
    function mintSeriesBatchN(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        uint32[] calldata editionNumbers,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external onlyRole(MINTER_ROLE) {
        uint256 batchSize = recipients.length;
        if (
            projectIds.length != batchSize ||
            editionNumbers.length != batchSize ||
            isFullTokenURIs.length != batchSize ||
            tokenURIData.length != batchSize
        ) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint256 i = 0; i < batchSize; ) {
            _mintSeries(
                creator,
                recipients[i],
                projectIds[i],
                editionNumbers[i],
                isFullTokenURIs[i],
                tokenURIData[i]
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSingle}.
     */
    function mintSingle(
        address creator,
        address recipient,
        uint64 projectId,
        bool isFullTokenURI,
        string memory tokenURIData
    ) public onlyRole(MINTER_ROLE) {
        if (recipient == address(0)) {
            revert ZeroAddressNotAllowed(address(this));
        }
        if (projectId == 0) {
            revert ProjectIdMustBePositive(address(this));
        }

        uint96 editionId = createEditionId(projectId, 1);

        if (_mintedEditionIdMap[editionId]) {
            revert EditionAlreadyMinted(address(this));
        }
        if (_seriesMap[projectId].hasEntry) {
            revert ProjectIsMintedAsSeries(address(this));
        }

        _mintedEditionIdMap[editionId] = true;

        uint256 tokenId = IERC721CreatorCore(creator).mintExtension(recipient);

        if (bytes(tokenURIData).length != 0) {
            if (isFullTokenURI) {
                _fullTokenURIMap[creator][tokenId] = tokenURIData;
            } else {
                _tokenURISuffixMap[creator][tokenId] = tokenURIData;
            }
        }

        _editionIdMap[creator][tokenId] = editionId;

        emit EditionMinted(creator, tokenId, projectId, 1);
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSingleBatch}.
     */
    function mintSingleBatch(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds
    ) external onlyRole(MINTER_ROLE) {
        uint256 batchSize = recipients.length;
        if (projectIds.length != batchSize) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint256 i = 0; i < batchSize; ) {
            mintSingle(creator, recipients[i], projectIds[i], false, "");

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-mintSingleBatch}.
     */
    function mintSingleBatch(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external onlyRole(MINTER_ROLE) {
        uint256 batchSize = recipients.length;
        if (
            projectIds.length != batchSize ||
            isFullTokenURIs.length != batchSize ||
            tokenURIData.length != batchSize
        ) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint256 i = 0; i < batchSize; ) {
            mintSingle(
                creator,
                recipients[i],
                projectIds[i],
                isFullTokenURIs[i],
                tokenURIData[i]
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-setBaseTokenURI}.
     */
    function setBaseTokenURI(string calldata baseTokenURI)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _baseTokenURI = baseTokenURI;
        emit BaseTokenURISet(_baseTokenURI);
    }

    /**
     * @dev See {ITokengateManifoldExtension-getBaseTokenURI}.
     */
    function getBaseTokenURI() external view returns (string memory) {
        return _baseTokenURI;
    }

    /**
     * @dev See {ITokengateManifoldExtension-setDefaultTokenURIPrefix}.
     */
    function setDefaultTokenURIPrefix(string calldata defaultTokenURIPrefix_)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _defaultTokenURIPrefix = defaultTokenURIPrefix_;
        emit DefaultTokenURIPrefixSet(_defaultTokenURIPrefix);
    }

    /**
     * @dev See {ITokengateManifoldExtension-getDefaultTokenURIPrefix}.
     */
    function getDefaultTokenURIPrefix() external view returns (string memory) {
        return _defaultTokenURIPrefix;
    }

    /**
     * @dev See {ITokengateManifoldExtension-setTokenURIData}.
     */
    function setTokenURIData(
        address creator,
        uint256 tokenId,
        bool isFullTokenURI,
        string calldata tokenURIData
    ) public onlyRole(MINTER_ROLE) {
        if (_editionIdMap[creator][tokenId] == 0) {
            revert TokenNotFound(address(this));
        }
        if (isFullTokenURI) {
            _fullTokenURIMap[creator][tokenId] = tokenURIData;
        } else {
            _tokenURISuffixMap[creator][tokenId] = tokenURIData;
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-setTokenURIDataBatch}.
     */
    function setTokenURIDataBatch(
        address creator,
        uint256[] calldata tokenIds,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external onlyRole(MINTER_ROLE) {
        uint256 batchSize = tokenIds.length;
        if (
            isFullTokenURIs.length != batchSize ||
            tokenURIData.length != batchSize
        ) {
            revert ArrayLengthMismatch(address(this));
        }

        for (uint256 i = 0; i < batchSize; ) {
            setTokenURIData(
                creator,
                tokenIds[i],
                isFullTokenURIs[i],
                tokenURIData[i]
            );

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev See {ITokengateManifoldExtension-getTokenURISuffix}.
     */
    function getTokenURISuffix(address creator, uint256 tokenId)
        external
        view
        returns (string memory)
    {
        return _tokenURISuffixMap[creator][tokenId];
    }

    /**
     * @dev See {ITokengateManifoldExtension-getFullTokenURI}.
     */
    function getFullTokenURI(address creator, uint256 tokenId)
        external
        view
        returns (string memory)
    {
        return _fullTokenURIMap[creator][tokenId];
    }

    /**
     * @dev See {ICreatorExtensionTokenURI-tokenURI}.
     *
     * Refer to the 'Token URI Generation' section in {ITokengateManifoldExtension} for more information.
     */
    function tokenURI(address creator, uint256 tokenId)
        external
        view
        returns (string memory)
    {
        string memory fullTokenURI = _fullTokenURIMap[creator][tokenId];
        if (bytes(fullTokenURI).length != 0) {
            return fullTokenURI;
        }

        (uint64 projectId, uint32 editionNumber) = splitEditionId(
            _editionIdMap[creator][tokenId]
        );

        Series memory series = _seriesMap[projectId];
        if (series.hasEntry) {
            return
                getSeriesTokenURI(creator, tokenId, projectId, editionNumber);
        }

        string memory tokenURISuffix = _tokenURISuffixMap[creator][tokenId];
        if (bytes(tokenURISuffix).length != 0) {
            return
                string(
                    abi.encodePacked(_defaultTokenURIPrefix, tokenURISuffix)
                );
        }

        return
            string(
                abi.encodePacked(
                    _baseTokenURI,
                    toString(creator),
                    "-",
                    tokenId.toString()
                )
            );
    }

    /**
     * @dev Internal function used to generate the tokenURI for an edition of a series.
     */
    function getSeriesTokenURI(
        address creator,
        uint256 tokenId,
        uint64 projectId,
        uint32 editionNumber
    ) internal view returns (string memory) {
        string memory suffix = _tokenURISuffixMap[creator][tokenId];
        Series memory series = _seriesMap[projectId];

        if (
            bytes(series.tokenURISuffix).length == 0 &&
            bytes(suffix).length == 0
        ) {
            return
                string(
                    abi.encodePacked(
                        _baseTokenURI,
                        toString(creator),
                        "-",
                        tokenId.toString()
                    )
                );
        }

        string memory tokenURIPrefix = getSeriesTokenURIPrefix(projectId);

        if (bytes(suffix).length != 0) {
            return string(abi.encodePacked(tokenURIPrefix, suffix));
        }

        if (series.addEditionToTokenURISuffix) {
            if (bytes(series.tokenURIExtension).length != 0) {
                return
                    string(
                        abi.encodePacked(
                            tokenURIPrefix,
                            series.tokenURISuffix,
                            uint256(editionNumber).toString(),
                            series.tokenURIExtension
                        )
                    );
            }

            return
                string(
                    abi.encodePacked(
                        tokenURIPrefix,
                        series.tokenURISuffix,
                        uint256(editionNumber).toString()
                    )
                );
        }

        return string(abi.encodePacked(tokenURIPrefix, series.tokenURISuffix));
    }

    /**
     * @dev Internal function used to determine the token URI prefix to use for an edition of a series.
     */
    function getSeriesTokenURIPrefix(uint64 projectId)
        internal
        view
        returns (string memory)
    {
        Series memory series = _seriesMap[projectId];

        if (bytes(series.tokenURIPrefix).length != 0) {
            return series.tokenURIPrefix;
        }

        return _defaultTokenURIPrefix;
    }

    /**
     * @dev See {ITokengateManifoldExtension-getTokenInfo}.
     */
    function getTokenInfo(address creator, uint256 tokenId)
        external
        view
        returns (uint64 projectId, uint32 editionNumber)
    {
        uint96 editionId = _editionIdMap[creator][tokenId];
        if (editionId == 0) {
            revert TokenNotFound(address(this));
        }
        (projectId, editionNumber) = splitEditionId(
            _editionIdMap[creator][tokenId]
        );
    }

    /**
     * @dev See {ITokengateManifoldExtension-isSeries}.
     */
    function isSeries(uint64 projectId) external view returns (bool) {
        return _seriesMap[projectId].hasEntry;
    }

    /**
     * @dev See {ITokengateManifoldExtension-isMinted}.
     */
    function isMinted(uint64 projectId, uint32 editionNumber)
        external
        view
        returns (bool)
    {
        return _mintedEditionIdMap[createEditionId(projectId, editionNumber)];
    }

    /**
     * @dev See {ITokengateManifoldExtension-createEditionId}.
     */
    function createEditionId(uint64 projectId, uint32 editionNumber)
        public
        pure
        returns (uint96)
    {
        uint96 editionId = projectId;
        editionId = editionId << 32;
        editionId = editionId + editionNumber;
        return editionId;
    }

    /**
     * @dev See {ITokengateManifoldExtension-splitEditionId}.
     */
    function splitEditionId(uint96 editionId)
        public
        pure
        returns (uint64 projectId, uint32 editionNumber)
    {
        projectId = uint64(editionId >> 32);
        editionNumber = uint32(editionId);
    }

    /**
     * @dev See {ITokengateManifoldExtension-getRoleMembers}.
     */
    function getRoleMembers(bytes32 role)
        public
        view
        returns (address[] memory)
    {
        uint256 roleCount = getRoleMemberCount(role);
        address[] memory members = new address[](roleCount);
        for (uint256 i = 0; i < roleCount; ) {
            members[i] = getRoleMember(role, i);

            unchecked {
                ++i;
            }
        }
        return members;
    }

    /**
     * @dev Convert an address to a string.
     */
    function toString(address addr) public pure returns (string memory) {
        uint256 data = uint256(uint160(addr));
        return data.toHexString();
    }
}
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;
    }
}
Strings.sol 67 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity 0.8.9;

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

/**
 * @title An extension for Manifold Creator contracts that allows the various NFT marketplaces run by
 * DSENT AG to mint tokens on behalf of a creator. The extension ensures that only a limited number of
 * editions can be minted for any given project. A project in the context of this extension serves as
 * a general term for a wide range of collectibles such as artworks, PFPs, club memberships, etc.
 * Projects can either be minted as single editions or editions of a series with a predefined maximum size.
 *
 * @author DSENT AG, www.dsent.com
 *
 * Token URI Generation:
 * ---------------------
 * The logic used to generate a URI for a specified token id uses one of three approaches
 * based on the provided token URI data. Token URI data is always specified using `isFullTokenURI` and
 * `tokenURIData` parameters. If `isFullTokenURI == true` then `tokenURIData` will be interpreted as a
 * complete token URI and no further concatenation logic will be applied. For `isFullTokenURI == false`
 * the `tokenURIData` is used as a URI suffix and is concatenated with a URI prefix to form a complete
 * token URI.
 *
 * Token URI data can either be specified during minting or afterwards using either `setTokenURIData()`
 * or its batch variant `setTokenURIDataBatch()`.
 *
 * The three approaches for URI generation work as follows:
 *
 * 1. If no specific token URI data is specified, the base token URI is concatenated with the creator
 * address and the token id to form a complete URI.
 *
 * 2. If a token URI suffix is specified for a given token, then that suffix gets concatenated with
 * a token URI prefix to form a complete URI.
 *
 * For all single editions the token URI prefix is read from the default token URI prefix variable.
 * Editions of a series on the other hand can use an optional override of the default prefix that
 * can be specified during `createSeries()` or `setSeriesParams()` calls.
 *
 * A token URI suffix can be defined directly on the token level using the URI data parameters
 * of the minting or `setTokenURIData()`/`setTokenURIDataBatch()` calls. Alternatively, it is also possible to
 * to define a suffix that applies to all editions of a series. Such a suffix can be specified during
 * `createSeries()` or `setSeriesParams()` calls. In the case of a series it is further possible to set
 * `addEditionToTokenURISuffix = true`, which will cause the concatenation logic to append the
 * edition number to the specified suffix. If necessary, an optional `tokenURIExtension` can be
 * specified, in order to append an additional extension after the edition number. This is useful if
 * an endpoint has no proper support for Content-Type headers.
 *
 * It is important to understand that a suffix defined on the token level will always take precedence
 * over one defined on a series level.
 *
 * 3. If a full token URI is specified for a given token, then that full URI takes precedence over
 * everything else. It will override a suffix that might be specified for that token.
 *
 * A full token URI must be defined directly on the token level using the URI data parameters
 * of the minting or `setTokenURIData()`/`setTokenURIDataBatch()` calls.
 */
interface ITokengateManifoldExtension is IERC165 {
    /**
     * @dev Event that is emitted when a new single edition or an edition of a series is minted.
     */
    event EditionMinted(
        address indexed creator,
        uint256 indexed tokenId,
        uint64 indexed projectId,
        uint32 editionNumber
    );

    /**
     * @dev Event that is emitted when a new series with a predefined edition size is created.
     */
    event SeriesCreated(uint64 indexed projectId);

    /**
     * @dev Event that is emitted when the parameters of a series are changed that drive the
     * token URI generation of the editions belonging to that series.
     */
    event SeriesParamsSet(uint64 indexed projectId);

    /**
     * @dev Event that is emitted when the base token URI is changed that is used during tokenURI
     * generation. The base token URI is used for editions that have neither a token URI suffix nor
     * a full token URI specified.
     */
    event BaseTokenURISet(string baseTokenURI);

    /**
     * @dev Event that is emitted when the default token URI prefix is changed that is used during
     * token URI generation. The default token URI prefix is used for editions that have a token URI suffix
     * defined.
     */
    event DefaultTokenURIPrefixSet(string defaultTokenURIPrefix);

    /**
     * @dev Error that occurs when specifying a project id of zero.
     */
    error ProjectIdMustBePositive(address emitter);

    /**
     * @dev Error that occurs when creating a series that does not consist of at least two editions.
     */
    error EditionSizeMustBeGreaterThanOne(address emitter);

    /**
     * @dev Error that occurs when minting a token of a series with an edition number of zero.
     */
    error EditionNumberMustBePositive(address emitter);

    /**
     * @dev Error that occurs when minting a token of a series with an edition number that is larger
     * than the maximum allowed size for that series.
     */
    error EditionNumberExceedsEditionSize(address emitter);

    /**
     * @dev Error that occurs when creating a series with a project id that belongs to an already
     * existing series.
     */
    error SeriesAlreadyCreated(address emitter);

    /**
     * @dev Error that occurs when creating a series for a project id that was already used to mint
     * a single edition.
     */
    error ProjectIsMintedAsSingleEdition(address emitter);

    /**
     * @dev Error that occurs when minting an edition for a project id and edition number
     * that is already used by another single edition or an edition of a series.
     */
    error EditionAlreadyMinted(address emitter);

    /**
     * @dev Error that occurs when minting a single edition for a project id that was already used
     * to create a series.
     */
    error ProjectIsMintedAsSeries(address emitter);

    /**
     * @dev Error that occurs when specifying a project id that does not belong to any of the
     * created series.
     */
    error SeriesNotFound(address emitter);

    /**
     * @dev Error that occurs when specifying the 0x0 address.
     */
    error ZeroAddressNotAllowed(address emitter);

    /**
     * @dev Error that occurs when the length of the parameter arrays used in a batch operation
     * do not match.
     */
    error ArrayLengthMismatch(address emitter);

    /**
     * @dev Error that occurs when specifying a token id that does not exist.
     */
    error TokenNotFound(address emitter);

    /**
     * @dev Custom type that is used to represent a series of limited edition tokens belonging to
     * a certain project. Series are always referenced by the project id that they belong to.
     */
    struct Series {
        /**
         * @dev Variable that stores whether a series was created for a given project id.
         */
        bool hasEntry;
        /**
         * @dev Variable that stores the maximum number of editions that can be minted for the series.
         */
        uint32 editionSize;
        /**
         * @dev Variable that stores the number of editions that have been minted for the series.
         */
        uint32 editionCount;
        /**
         * @dev Variable that stores an optional override for the default token URI prefix that is used
         * for all editions of the series.
         *
         * Refer to the 'Token URI Generation' section above for more information on this variable.
         */
        string tokenURIPrefix;
        /**
         * @dev Variable that stores an optional token URI suffix that is used for all editions of
         * the series.
         *
         * Refer to the 'Token URI Generation' section above for more information on this variable.
         */
        string tokenURISuffix;
        /**
         * @dev Variable that controls whether the edition number is to be added to the token URI suffix
         * during token URI generation or not.
         *
         * Refer to the 'Token URI Generation' section above for more information on this variable.
         */
        bool addEditionToTokenURISuffix;
        /**
         * @dev Variable that stores an optional extension to add to the edition number during
         * token URI generation. Setting this value only makes sense when `addEditionToTokenURISuffix == true`.
         *
         * Refer to the 'Token URI Generation' section above for more information on this variable.
         */
        string tokenURIExtension;
    }

    /**
     * @dev Create a new series of limited edition tokens belonging to a certain project.
     */
    function createSeries(
        uint64 projectId,
        uint32 editionSize,
        string calldata tokenURIPrefix,
        string calldata tokenURISuffix,
        bool addEditionToTokenURISuffix,
        string calldata tokenURIExtension
    ) external;

    /**
     * @dev Set the parameters of a series that drive the token URI generation of its editions.
     */
    function setSeriesParams(
        uint64 projectId,
        string calldata tokenURIPrefix,
        string calldata tokenURISuffix,
        bool addEditionToTokenURISuffix,
        string calldata tokenURIExtension
    ) external;

    /**
     * @dev Get the custom type that stores all the state variables for the specified series.
     */
    function getSeries(uint64 projectId) external view returns (Series memory);

    /**
     * @dev Mint a new edition for the series specified by the project id.
     *
     * Note: If no custom token URI data is required, use `isFullTokenURI = false` and `tokenURIData = ''`
     */
    function mintSeries(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 editionNumber,
        bool isFullTokenURI,
        string calldata tokenURIData
    ) external;

    /**
     * @dev Batch mint new editions to a single recipient for the series specified by the project id.
     * This function overload does not take any custom token URI data for the editions to mint.
     */
    function mintSeriesBatch1(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 startEditionNumber,
        uint32 nbEditions
    ) external;

    /**
     * @dev Batch mint new editions to a single recipient for the series specified by the project id.
     * This function overload takes custom token URI data for the editions to mint.
     */
    function mintSeriesBatch1(
        address creator,
        address recipient,
        uint64 projectId,
        uint32 startEditionNumber,
        uint32 nbEditions,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external;

    /**
     * @dev Batch mint new editions to multiple recipients for the series specified by the project id.
     * This function overload does not take any custom token URI data for the editions to mint.
     */
    function mintSeriesBatchN(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        uint32[] calldata editionNumbers
    ) external;

    /**
     * @dev Batch mint new editions to multiple recipients for the series specified by the project id.
     * This function overload takes custom token URI data for the editions to mint.
     */
    function mintSeriesBatchN(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        uint32[] calldata editionNumbers,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external;

    /**
     * @dev Mint a new single edition for the specified project id.
     *
     * Note: If no custom token URI data is required, use `isFullTokenURI = false` and `tokenURIData = ''`
     */
    function mintSingle(
        address creator,
        address recipient,
        uint64 projectId,
        bool isFullTokenURI,
        string calldata tokenURIData
    ) external;

    /**
     * @dev Batch mint new single editions to multiple recipients for the specified project ids.
     * This function overload does not take any custom token URI data for the editions to mint.
     */
    function mintSingleBatch(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds
    ) external;

    /**
     * @dev Batch mint new single editions to multiple recipients for the specified project ids.
     * This function overload takes custom token URI data for the editions to mint.
     */
    function mintSingleBatch(
        address creator,
        address[] calldata recipients,
        uint64[] calldata projectIds,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external;

    /**
     * @dev Set the base token URI that is used during token URI generation. The base token URI is
     * used for editions that have neither a token URI suffix nor a full token URI specified.
     */
    function setBaseTokenURI(string calldata baseTokenURI) external;

    /**
     * @dev Get the base token URI that is used during token URI generation. The base token URI is
     * used for editions that have neither a token URI suffix nor a full token URI specified.
     */
    function getBaseTokenURI() external view returns (string memory);

    /**
     * @dev Set the default token URI prefix that is used during token URI generation. The default
     * token URI prefix is used for editions that have a token URI suffix defined.
     */
    function setDefaultTokenURIPrefix(string calldata defaultTokenURIPrefix)
        external;

    /**
     * @dev Get the default token URI prefix that is used during token URI generation. The default
     * token URI prefix is used for editions that have a token URI suffix defined.
     */
    function getDefaultTokenURIPrefix() external view returns (string memory);

    /**
     * @dev Set either a full token URI (if `isFullTokenURI == true`) or a token URI suffix (if `isFullTokenURI == false`)
     * for the specified token id.
     *
     * Note: Specifying a full token URI always takes precedence over any other token URI generation
     * logic. If a currently active full token URI is to be replaced by a token URI suffix, make sure
     * to reset the full token URI value before by specifying an empty string '' in `tokenURIData`.
     *
     * Refer to the 'Token URI Generation' section above for more information.
     */
    function setTokenURIData(
        address creator,
        uint256 tokenId,
        bool isFullTokenURI,
        string calldata tokenURIData
    ) external;

    /**
     * @dev Batch set either full token URIs (if `isFullTokenURI == true`) or token URI suffixes (if `isFullTokenURI == false`)
     * for the specified token ids.
     *
     * Note: Specifying a full token URI always takes precedence over any other token URI generation
     * logic. If a currently active full token URI is to be replaced by a token URI suffix, make sure
     * to reset the full token URI value before by specifying an empty string '' in `tokenURIData`.
     *
     * Refer to the 'Token URI Generation' section above for more information.
     */
    function setTokenURIDataBatch(
        address creator,
        uint256[] calldata tokenIds,
        bool[] calldata isFullTokenURIs,
        string[] calldata tokenURIData
    ) external;

    /**
     * @dev Get the token URI suffix for the specified token id if one has been set.
     */
    function getTokenURISuffix(address creator, uint256 tokenId)
        external
        view
        returns (string memory);

    /**
     * @dev Get the full token URI for the specified token id if one has been set.
     */
    function getFullTokenURI(address creator, uint256 tokenId)
        external
        view
        returns (string memory);

    /**
     * @dev Get the token info consisting of the project id and edition number for the specified token id.
     */
    function getTokenInfo(address creator, uint256 tokenId)
        external
        view
        returns (uint64 projectId, uint32 editionNumber);

    /**
     * @dev Check whether a series has been created for the specified project id.
     */
    function isSeries(uint64 projectId) external view returns (bool);

    /**
     * @dev Check whether an edition has been minted for the specified project id and edition number.
     */
    function isMinted(uint64 projectId, uint32 editionNumber)
        external
        view
        returns (bool);

    /**
     * @dev Create an edition id by bit shifting a 64-bit project id with a 32-bit edition number into a 96-bit uint.
     */
    function createEditionId(uint64 projectId, uint32 editionNumber)
        external
        pure
        returns (uint96);

    /**
     * @dev Split an edition id by bit shifting the 96-bit uint into a 64-bit project id and a 32-bit edition number.
     */
    function splitEditionId(uint96 editionId)
        external
        pure
        returns (uint64 projectId, uint32 editionNumber);

    /**
     * @dev Get all addresses that are granted the specified role.
     */
    function getRoleMembers(bytes32 role)
        external
        view
        returns (address[] memory);
}
AccessControl.sol 223 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

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

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

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

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

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}
IAccessControl.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

        assembly {
            result := store
        }

        return result;
    }
}
AccessControlEnumerable.sol 64 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

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

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }
}
IAccessControlEnumerable.sol 31 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
ICreatorCore.sol 149 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

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

/**
 * @dev Core creator interface
 */
interface ICreatorCore is IERC165 {

    event ExtensionRegistered(address indexed extension, address indexed sender);
    event ExtensionUnregistered(address indexed extension, address indexed sender);
    event ExtensionBlacklisted(address indexed extension, address indexed sender);
    event MintPermissionsUpdated(address indexed extension, address indexed permissions, address indexed sender);
    event RoyaltiesUpdated(uint256 indexed tokenId, address payable[] receivers, uint256[] basisPoints);
    event DefaultRoyaltiesUpdated(address payable[] receivers, uint256[] basisPoints);
    event ExtensionRoyaltiesUpdated(address indexed extension, address payable[] receivers, uint256[] basisPoints);
    event ExtensionApproveTransferUpdated(address indexed extension, bool enabled);

    /**
     * @dev gets address of all extensions
     */
    function getExtensions() external view returns (address[] memory);

    /**
     * @dev add an extension.  Can only be called by contract owner or admin.
     * extension address must point to a contract implementing ICreatorExtension.
     * Returns True if newly added, False if already added.
     */
    function registerExtension(address extension, string calldata baseURI) external;

    /**
     * @dev add an extension.  Can only be called by contract owner or admin.
     * extension address must point to a contract implementing ICreatorExtension.
     * Returns True if newly added, False if already added.
     */
    function registerExtension(address extension, string calldata baseURI, bool baseURIIdentical) external;

    /**
     * @dev add an extension.  Can only be called by contract owner or admin.
     * Returns True if removed, False if already removed.
     */
    function unregisterExtension(address extension) external;

    /**
     * @dev blacklist an extension.  Can only be called by contract owner or admin.
     * This function will destroy all ability to reference the metadata of any tokens created
     * by the specified extension. It will also unregister the extension if needed.
     * Returns True if removed, False if already removed.
     */
    function blacklistExtension(address extension) external;

    /**
     * @dev set the baseTokenURI of an extension.  Can only be called by extension.
     */
    function setBaseTokenURIExtension(string calldata uri) external;

    /**
     * @dev set the baseTokenURI of an extension.  Can only be called by extension.
     * For tokens with no uri configured, tokenURI will return "uri+tokenId"
     */
    function setBaseTokenURIExtension(string calldata uri, bool identical) external;

    /**
     * @dev set the common prefix of an extension.  Can only be called by extension.
     * If configured, and a token has a uri set, tokenURI will return "prefixURI+tokenURI"
     * Useful if you want to use ipfs/arweave
     */
    function setTokenURIPrefixExtension(string calldata prefix) external;

    /**
     * @dev set the tokenURI of a token extension.  Can only be called by extension that minted token.
     */
    function setTokenURIExtension(uint256 tokenId, string calldata uri) external;

    /**
     * @dev set the tokenURI of a token extension for multiple tokens.  Can only be called by extension that minted token.
     */
    function setTokenURIExtension(uint256[] memory tokenId, string[] calldata uri) external;

    /**
     * @dev set the baseTokenURI for tokens with no extension.  Can only be called by owner/admin.
     * For tokens with no uri configured, tokenURI will return "uri+tokenId"
     */
    function setBaseTokenURI(string calldata uri) external;

    /**
     * @dev set the common prefix for tokens with no extension.  Can only be called by owner/admin.
     * If configured, and a token has a uri set, tokenURI will return "prefixURI+tokenURI"
     * Useful if you want to use ipfs/arweave
     */
    function setTokenURIPrefix(string calldata prefix) external;

    /**
     * @dev set the tokenURI of a token with no extension.  Can only be called by owner/admin.
     */
    function setTokenURI(uint256 tokenId, string calldata uri) external;

    /**
     * @dev set the tokenURI of multiple tokens with no extension.  Can only be called by owner/admin.
     */
    function setTokenURI(uint256[] memory tokenIds, string[] calldata uris) external;

    /**
     * @dev set a permissions contract for an extension.  Used to control minting.
     */
    function setMintPermissions(address extension, address permissions) external;

    /**
     * @dev Configure so transfers of tokens created by the caller (must be extension) gets approval
     * from the extension before transferring
     */
    function setApproveTransferExtension(bool enabled) external;

    /**
     * @dev get the extension of a given token
     */
    function tokenExtension(uint256 tokenId) external view returns (address);

    /**
     * @dev Set default royalties
     */
    function setRoyalties(address payable[] calldata receivers, uint256[] calldata basisPoints) external;

    /**
     * @dev Set royalties of a token
     */
    function setRoyalties(uint256 tokenId, address payable[] calldata receivers, uint256[] calldata basisPoints) external;

    /**
     * @dev Set royalties of an extension
     */
    function setRoyaltiesExtension(address extension, address payable[] calldata receivers, uint256[] calldata basisPoints) external;

    /**
     * @dev Get royalites of a token.  Returns list of receivers and basisPoints
     */
    function getRoyalties(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
    
    // Royalty support for various other standards
    function getFeeRecipients(uint256 tokenId) external view returns (address payable[] memory);
    function getFeeBps(uint256 tokenId) external view returns (uint[] memory);
    function getFees(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
    function royaltyInfo(uint256 tokenId, uint256 value) external view returns (address, uint256);

}
IERC721CreatorCore.sol 68 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "./ICreatorCore.sol";

/**
 * @dev Core ERC721 creator interface
 */
interface IERC721CreatorCore is ICreatorCore {

    /**
     * @dev mint a token with no extension. Can only be called by an admin.
     * Returns tokenId minted
     */
    function mintBase(address to) external returns (uint256);

    /**
     * @dev mint a token with no extension. Can only be called by an admin.
     * Returns tokenId minted
     */
    function mintBase(address to, string calldata uri) external returns (uint256);

    /**
     * @dev batch mint a token with no extension. Can only be called by an admin.
     * Returns tokenId minted
     */
    function mintBaseBatch(address to, uint16 count) external returns (uint256[] memory);

    /**
     * @dev batch mint a token with no extension. Can only be called by an admin.
     * Returns tokenId minted
     */
    function mintBaseBatch(address to, string[] calldata uris) external returns (uint256[] memory);

    /**
     * @dev mint a token. Can only be called by a registered extension.
     * Returns tokenId minted
     */
    function mintExtension(address to) external returns (uint256);

    /**
     * @dev mint a token. Can only be called by a registered extension.
     * Returns tokenId minted
     */
    function mintExtension(address to, string calldata uri) external returns (uint256);

    /**
     * @dev batch mint a token. Can only be called by a registered extension.
     * Returns tokenIds minted
     */
    function mintExtensionBatch(address to, uint16 count) external returns (uint256[] memory);

    /**
     * @dev batch mint a token. Can only be called by a registered extension.
     * Returns tokenId minted
     */
    function mintExtensionBatch(address to, string[] calldata uris) external returns (uint256[] memory);

    /**
     * @dev burn a token. Can only be called by token owner or approved address.
     * On burn, calls back to the registered extension's onBurn method
     */
    function burn(uint256 tokenId) external;

}
CreatorExtension.sol 30 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

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

/**
 * @dev Base creator extension variables
 */
abstract contract CreatorExtension is ERC165 {

    /**
     * @dev Legacy extension interface identifiers
     *
     * {IERC165-supportsInterface} needs to return 'true' for this interface
     * in order backwards compatible with older creator contracts
     */
    bytes4 constant internal LEGACY_EXTENSION_INTERFACE = 0x7005caad;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
        return interfaceId == LEGACY_EXTENSION_INTERFACE
            || super.supportsInterface(interfaceId);
    }
    
}
ICreatorExtensionTokenURI.sol 18 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

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

/**
 * @dev Implement this if you want your extension to have overloadable URI's
 */
interface ICreatorExtensionTokenURI is IERC165 {

    /**
     * Get the uri for a given creator/tokenId
     */
    function tokenURI(address creator, uint256 tokenId) external view returns (string memory);
}

Read Contract

DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
MINTER_ROLE 0xd5391393 → bytes32
createEditionId 0x268a9fbc → uint96
getBaseTokenURI 0xbdc32be0 → string
getDefaultTokenURIPrefix 0x940d430a → string
getFullTokenURI 0xba1b5674 → string
getRoleAdmin 0x248a9ca3 → bytes32
getRoleMember 0x9010d07c → address
getRoleMemberCount 0xca15c873 → uint256
getRoleMembers 0xa3246ad3 → address[]
getSeries 0x8fdc057f → tuple
getTokenInfo 0xbbd7d209 → uint64, uint32
getTokenURISuffix 0x8dfd3bd3 → string
hasRole 0x91d14854 → bool
isMinted 0xb8e5ff48 → bool
isSeries 0xcba1c51d → bool
splitEditionId 0x9a976f41 → uint64, uint32
supportsInterface 0x01ffc9a7 → bool
toString 0x56ca623e → string
tokenURI 0xe9dc6375 → string

Write Contract 17 functions

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

createSeries 0xaf5ec6af
uint64 projectId
uint32 editionSize
string tokenURIPrefix
string tokenURISuffix
bool addEditionToTokenURISuffix
string tokenURIExtension
grantRole 0x2f2ff15d
bytes32 role
address account
mintSeries 0x969fa11d
address creator
address recipient
uint64 projectId
uint32 editionNumber
bool isFullTokenURI
string tokenURIData
mintSeriesBatch1 0x4829084d
address creator
address recipient
uint64 projectId
uint32 startEditionNumber
uint32 nbEditions
mintSeriesBatch1 0x7ec01135
address creator
address recipient
uint64 projectId
uint32 startEditionNumber
uint32 nbEditions
bool[] isFullTokenURIs
string[] tokenURIData
mintSeriesBatchN 0x72380c0b
address creator
address[] recipients
uint64[] projectIds
uint32[] editionNumbers
mintSeriesBatchN 0xe6787123
address creator
address[] recipients
uint64[] projectIds
uint32[] editionNumbers
bool[] isFullTokenURIs
string[] tokenURIData
mintSingle 0xfcbfd87d
address creator
address recipient
uint64 projectId
bool isFullTokenURI
string tokenURIData
mintSingleBatch 0x24cbece2
address creator
address[] recipients
uint64[] projectIds
bool[] isFullTokenURIs
string[] tokenURIData
mintSingleBatch 0xf4ccc2cd
address creator
address[] recipients
uint64[] projectIds
renounceRole 0x36568abe
bytes32 role
address account
revokeRole 0xd547741f
bytes32 role
address account
setBaseTokenURI 0x30176e13
string baseTokenURI
setDefaultTokenURIPrefix 0xc60f3ed8
string defaultTokenURIPrefix_
setSeriesParams 0xf22abf3d
uint64 projectId
string tokenURIPrefix
string tokenURISuffix
bool addEditionToTokenURISuffix
string tokenURIExtension
setTokenURIData 0xeffeeba9
address creator
uint256 tokenId
bool isFullTokenURI
string tokenURIData
setTokenURIDataBatch 0xa0aae475
address creator
uint256[] tokenIds
bool[] isFullTokenURIs
string[] tokenURIData

Recent Transactions

No transactions found for this address