Address Contract Partially Verified
Address
0xc31d52A017E523164Ba0A09ac19385c10bc2B890
Balance
0 ETH
Nonce
2
Code Size
20580 bytes
Creator
0xd8d7cc55...870f at tx 0x54c5b5fc...ac8908
Indexed Transactions
0
Contract Bytecode
20580 bytes
0x60806040526004361015610015575b366142c257005b60003560e01c8063012b87291461036557806301ffc9a71461036057806304634d8d1461035b57806306fdde0314610356578063081812fc14610351578063095ea7b31461034c57806318160ddd146103475780631ee8b41b14610342578063212f69121461033d57806323b872dd146103385780632419f51b14610333578063248a9ca31461032e5780632a55205a146103295780632f2ff15d1461032457806333cfcb9f1461031f57806336568abe1461031a57806340c10f191461031557806342842e0e1461031057806342966c681461030b578063492e224b146103065780634a00cc4814610301578063572b6c05146102fc5780635944c753146102f75780636352211e146102f257806363b45e2d146102ed57806370a08231146102e85780637a70a895146102e35780637c3b1137146102de5780637da0a877146102d957806383040532146102d457806384b0196e146102cf57806391d14854146102ca578063938e3d7b146102c557806395d89b41146102c05780639fc4d68f146102bb578063a05112fc146102b6578063a217fddf146102b1578063a22cb465146102ac578063ac9650d8146102a7578063b88d4fde146102a2578063c22707ee1461029d578063c4376dd714610298578063c54c07e114610293578063c87b56dd1461028e578063ce0b601314610289578063ce80564214610284578063d37c353b1461027f578063d547741f1461027a578063e05688fe14610275578063e715032214610270578063e8a3d4851461026b578063e985e9c5146102665763ee7d2adf0361000e57612660565b612614565b612570565b612519565b612362565b612320565b612152565b61204b565b61200c565b611fed565b611daf565b611bdb565b611bb3565b611b62565b611afd565b611a15565b6119f9565b6119bc565b6118a8565b6117d5565b6116db565b611697565b61159f565b61156e565b6114cc565b6114a3565b6113ab565b6112d4565b6112b6565b611287565b6111a4565b611150565b610f94565b610e73565b610d3f565b610d1c565b610ca2565b610c4d565b610c0a565b610bc8565b610b1e565b610aef565b610ac9565b610ab7565b610a4f565b6109cc565b6109a9565b610900565b610889565b6107ad565b6106b4565b6105af565b610508565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761039b57604052565b61036a565b606081019081106001600160401b0382111761039b57604052565b602081019081106001600160401b0382111761039b57604052565b60c081019081106001600160401b0382111761039b57604052565b90601f801991011681019081106001600160401b0382111761039b57604052565b6040519061041f82610380565b565b6001600160401b03811161039b57601f01601f191660200190565b92919261044882610421565b9161045660405193846103f1565b829481845281830111610473578281602093846000960137010152565b600080fd5b9080601f83011215610473578160206104939335910161043c565b90565b602060031982011261047357600435906001600160401b0382116104735761049391600401610478565b60005b8381106104d35750506000910152565b81810151838201526020016104c3565b906020916104fc815180928185528580860191016104c0565b601f01601f1916010190565b346104735760208061052161051c36610496565b6147b9565b01519060409182519180830181845282518091528484019180868360051b8701019401926000965b8388106105565786860387f35b9091929394838061058c600193603f198b820301875285838b5163ffffffff60e01b8151168452015191818582015201906104e3565b970193019701969093929193610549565b6001600160e01b031981160361047357565b34610473576020366003190112610473576106166004356105cf8161059d565b6001600160e01b031981166301ffc9a760e01b811491908215610666575b8215610655575b8215610644575b821561061a575b505060405190151581529081906020820190565b0390f35b63152a902d60e11b1491508115610634575b503880610602565b61063e91506137e5565b3861062c565b915061064f816137e5565b916105fb565b635b5e139f60e01b811492506105f4565b6380ac58cd60e01b811492506105ed565b6001600160a01b0381160361047357565b602435906001600160601b038216820361047357565b604435906001600160601b038216820361047357565b34610473576040366003190112610473576004356106d181610677565b6106d9610688565b906106e26127ee565b6001600160601b03821661271080821161077e5750506001600160a01b038116156107615761073861075f92610728610719610412565b6001600160a01b039094168452565b6001600160601b03166020830152565b805160209091015160a01b6001600160a01b0319166001600160a01b039190911617600955565b005b604051635b6cc80560e11b815260006004820152602490fd5b0390fd5b6044925060405191636f483d0960e01b835260048301526024820152fd5b9060206104939281815201906104e3565b346104735760008060031936011261088657604051816002546107cf816118ec565b8084529060019081811690811561085e5750600114610805575b610616846107f9818803826103f1565b6040519182918261079c565b60028352602094507f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b82841061084b5750505081610616936107f992820101936107e9565b805485850187015292850192810161082f565b61061696506107f99450602092508593915060ff191682840152151560051b820101936107e9565b80fd5b34610473576020366003190112610473576004356000548110806108e4575b156108d2576000908152600660209081526040918290205491516001600160a01b03909216825290f35b6040516333d1c03960e21b8152600490fd5b50600081815260046020526040902054600160e01b16156108a8565b60403660031901126104735760043561091881610677565b6024356001600160a01b038061092d83613d57565b1690813303610978575b600093838552600660205261094f8160408720613811565b16907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258480a480f35b81600052600760205260ff61099133604060002061277c565b5416610937576040516367d9dca160e11b8152600490fd5b346104735760003660031901126104735760206000546001549003604051908152f35b34610473576000366003190112610473576040517f000000000000000000000000dc2550635745d4400343721c934c353a69c5737c6001600160a01b03168152602090f35b906040610a3c610a2a84516060855260608501906104e3565b602085015184820360208601526104e3565b928101516001600160a01b031691015290565b3461047357602036600319011261047357610616610a77600435610a728161059d565b6148bd565b604051918291602083526020830190610a11565b606090600319011261047357600435610aa381610677565b90602435610ab081610677565b9060443590565b61075f610ac336610a8b565b91613dc6565b34610473576020366003190112610473576020610ae7600435612ef4565b604051908152f35b346104735760203660031901126104735760043560005260086020526020600160406000200154604051908152f35b3461047357604036600319011261047357600435600052600a602052604060002060405190610b4c82610380565b546001600160a01b03811680835260a09190911c602083015215610bba575b6020810151610b9e9061271090610b8d906001600160601b0316602435612a41565b92519204916001600160a01b031690565b604080516001600160a01b039290921682526020820192909252f35b50610bc3612a05565b610b6b565b346104735760403660031901126104735761075f602435600435610beb82610677565b806000526008602052610c056001604060002001546128f0565b612912565b34610473576040366003190112610473576024356001600160401b03811161047357610c3d61075f913690600401610478565b610c456127ee565b60043561340a565b3461047357604036600319011261047357602435610c6a81610677565b6001600160a01b0380610c7b613864565b1690821603610c905761075f90600435612995565b60405163334bd91960e11b8152600490fd5b3461047357604036600319011261047357600435610cbf81610677565b602435610cca61285e565b600054818101809111610d175760125410610ce85761075f9161420f565b60405162461bcd60e51b815260206004820152600760248201526621546f6b656e7360c81b6044820152606490fd5b612a2b565b61075f610d2836610a8b565b9060405192610d36846103bb565b600084526140dd565b3461047357602036600319011261047357600435610d5c81613d57565b60008281526006602052604090208054916001600160a01b03811691338085149084141715610d8a565b1590565b610e3d575b600093610d9b84613f10565b610e34575b50610daa82612762565b80546001600160801b030190554260a01b8217600360e01b17610dcc85611511565b55600160e11b811615610e01575b506000805160206150188339815191528280a461075f610dfc60015460010190565b600155565b60018401610e0e81611511565b5415610e1b575b50610dda565b83548114610e1557610e2c90611511565b553880610e15565b83905538610da0565b610e5c610d86610e5533610e5087612748565b61277c565b5460ff1690565b15610d8f57604051632ce44b5f60e11b8152600490fd5b34610473576020366003190112610473576020610e91600435613169565b6040519015158152f35b805190610eb060409283855283850190610a11565b90602080910151938181840391015283519182815281810182808560051b8401019601946000925b858410610ee9575050505050505090565b909192939495968580610f21600193601f1986820301885286838d5163ffffffff60e01b8151168452015191818582015201906104e3565b990194019401929594939190610ed8565b602080820190808352835180925260408301928160408460051b8301019501936000915b848310610f665750505050505090565b9091929394958480610f84600193603f198682030187528a51610e9b565b9801930193019194939290610f56565b346104735760008060031936011261088657604051630940198960e31b81529080826004817f000000000000000000000000dc2550635745d4400343721c934c353a69c5737c6001600160a01b03165afa91821561114b578192611127575b50815191610fff614f83565b80519280815b8681106110f257506110236110289161101e88886130eb565b612a63565b6145f0565b948193825b82811061109757505050915b83831061104e57604051806106168782610f32565b61108b6110919161107061106b6110658787612b7d565b51614640565b61478d565b61107a8289612b7d565b526110858188612b7d565b506130dd565b926130dd565b91611039565b6110af610d866110a78385612b7d565b515151614de4565b6110c2575b6110bd906130dd565b61102d565b946110ea6110bd916110d48885612b7d565b516110df828c612b7d565b52611085818b612b7d565b9590506110b4565b6110ff6110a78287612b7d565b611112575b61110d906130dd565b611005565b9061111f61110d916130dd565b919050611104565b6111449192503d8084833e61113c81836103f1565b81019061451a565b9038610ff3565b613ae4565b3461047357602036600319011261047357602060043561116f81610677565b6040519060018060a01b03807f000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a81169116148152f35b34610473576060366003190112610473576004356024356111c481610677565b6111cc61069e565b6111d46127ee565b6127106001600160601b0382168181116112635750506001600160a01b038216156112435761075f9261122e61123e9261121e61120f610412565b6001600160a01b039096168652565b6001600160601b03166020850152565b600052600a602052604060002090565b613830565b604051634b4f842960e11b81526004810184905260006024820152604490fd5b60649185916040519263dfd1fc1b60e01b8452600484015260248301526044820152fd5b346104735760203660031901126104735760206001600160a01b036112ad600435613d57565b16604051908152f35b34610473576000366003190112610473576020600f54604051908152f35b34610473576020366003190112610473576004356112f181610677565b6001600160a01b0316801561132157600052600560205260206001600160401b0360406000205416604051908152f35b6040516323d3ad8160e21b8152600490fd5b9181601f84011215610473578235916001600160401b038311610473576020838186019501011161047357565b90600319604081840112610473576004356001600160401b03918282116104735760809082860301126104735760040192602435918211610473576113a791600401611333565b9091565b34610473576113b936611360565b91906113d36113cb6060840184612adf565b8101906133eb565b926001600160a01b0390911691831561147857600054916113f485846130eb565b60125410610ce857610616957fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69261142b926139b5565b93611436818561420f565b61144d611441613864565b6001600160a01b031690565b60408051948552602085019290925292a36040516001600160a01b0390911681529081906020820190565b60405162461bcd60e51b815260206004820152600360248201526271747960e81b6044820152606490fd5b346104735760206001600160a01b0360406114c061051c36610496565b51015116604051908152f35b34610473576000366003190112610473576040517f000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a816001600160a01b03168152602090f35b6000526004602052604060002090565b7f8502233096d909befbda0999bb8ea2f3a6be3c138b9fbf003752a4c8bce86f6c60005260086020527f85da32aea425a83be1133c4e958580985fa04602ddf03ad35008a70703b20eb790565b34610473576020366003190112610473576004356000526011602052602060ff604060002054166040519015158152f35b346104735760008060031936011261088657611649906115de7f5369676e6174757265416374696f6e000000000000000000000000000000000f612d00565b6116077f3100000000000000000000000000000000000000000000000000000000000001612df8565b9160405191611615836103bb565b818352604051948594600f60f81b865261163b60209360e08589015260e08801906104e3565b9086820360408801526104e3565b904660608601523060808601528260a086015284820360c08601528080855193848152019401925b82811061168057505050500390f35b835185528695509381019392810192600101611671565b3461047357604036600319011261047357602060ff6116cf6024356116bb81610677565b60043560005260088452604060002061277c565b54166040519015158152f35b34610473576116e936610496565b6116f16127ee565b80516001600160401b03811161039b57611715816117106014546118ec565b61319b565b602080601f831160011461175257508192600092611747575b5050600019600383901b1c191660019190911b17601455005b01519050388061172e565b6014600052601f198316939091907fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec926000905b8682106117bd57505083600195106117a4575b505050811b01601455005b015160001960f88460031b161c19169055388080611799565b80600185968294968601518155019501930190611786565b346104735760008060031936011261088657604051816003546117f7816118ec565b8084529060019081811690811561085e575060011461182057610616846107f9818803826103f1565b60038352602094507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8284106118665750505081610616936107f992820101936107e9565b805485850187015292850192810161184a565b9060406003198301126104735760043591602435906001600160401b038211610473576113a791600401611333565b34610473576106166118c26118bc36611879565b91613033565b6040519182916020835260208301906104e3565b634e487b7160e01b600052600060045260246000fd5b90600182811c9216801561191c575b602083101461190657565b634e487b7160e01b600052602260045260246000fd5b91607f16916118fb565b9060009291805491611937836118ec565b9182825260019384811690816000146119995750600114611959575b50505050565b90919394506000526020928360002092846000945b838610611985575050505001019038808080611953565b80548587018301529401938590820161196e565b9294505050602093945060ff191683830152151560051b01019038808080611953565b3461047357602036600319011261047357600435600052600e6020526106166119f26118c2604060002060405192838092611926565b03826103f1565b3461047357600036600319011261047357602060405160008152f35b3461047357604036600319011261047357600435611a3281610677565b6024359081151580920361047357336000526007602052611a5781604060002061277c565b60ff1981541660ff841617905560405191825260018060a01b0316907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b602080820190808352835180925260408301928160408460051b8301019501936000915b848310611acf5750505050505090565b9091929394958480611aed600193603f198682030187528a516104e3565b9801930193019194939290611abf565b34610473576020366003190112610473576001600160401b036004358181116104735736602382011215610473578060040135918211610473573660248360051b8301011161047357610616916024611b569201612b91565b60405191829182611a9b565b608036600319011261047357600435611b7a81610677565b602435611b8681610677565b606435916001600160401b03831161047357611ba961075f933690600401610478565b91604435916140dd565b3461047357610616611bc761051c36610496565b604051918291602083526020830190610e9b565b34610473576040611bf4611bee36611360565b916138bd565b825191151582526001600160a01b03166020820152f35b6001600160401b03811161039b5760051b60200190565b81601f8201121561047357803590611c3982611c0b565b92604092611c49845195866103f1565b808552602093848087019260051b8501019383851161047357858101925b858410611c78575050505050505090565b6001600160401b0384358181116104735783019184601f198489030112610473578451611ca481610380565b89840135611cb18161059d565b81528584013592831161047357611ccf888b80969581960101610478565b83820152815201930192611c67565b60031990602081830112610473576004908135916001600160401b038084116104735760408585850301126104735760405194611d1a86610380565b848301358281116104735760609086019182860301126104735760405190611d41826103a0565b83810135838111610473578585611d5a92840101610478565b825260248101359083821161047357611d798686604494840101610478565b60208401520135611d8981610677565b60408201528552602484013590811161047357611da7930101611c22565b602082015290565b3461047357611dbd36611cde565b611dcd611dc8614358565b614301565b805151611de4611ddc82614685565b541515614bbe565b611e006002611df283614640565b01546001600160a01b031690565b82516040908101516001600160a01b0393919284169190611e25908516831415614c1c565b611e388551611e3383614640565b6149ee565b6003611e4d81611e4784614640565b01614705565b805190611e6383611e5d86614640565b01614cc3565b60005b828110611fbc57505050602094858701918251519760005b898110611e8757005b80855190611e9491612b7d565b51611e9e90614a91565b8982875190611eac91612b7d565b5101518951808c81019283611ec091612b2d565b03601f1981018252611ed290826103f1565b5190206001600160e01b031991611eee91831690831614614a9f565b825182875190611efd91612b7d565b51611f0790614a91565b611f1090614885565b90611f1a916149ee565b83611f2488614640565b0182875190611f3291612b7d565b51611f3c91614b72565b82518901516001600160a01b0316908883885190611f5991612b7d565b51611f6390614a91565b918c858a5190611f7291612b7d565b51015192888d5192839216951693611f8a908261079c565b037f7f4649aa14a7e9abd7f21a02ea35b32c907d59bb701c52c0e028ddf57533c74c91a4611fb7906130dd565b611e7e565b80611fe3611fde611fd9611fd3611fe89587612b7d565b51614a91565b614885565b614d19565b6130dd565b611e66565b34610473576020366003190112610473576106166118c2600435613590565b3461047357602036600319011261047357602060043561202b8161059d565b6001600160a01b0390604090612040906148bd565b015116604051908152f35b346104735761205936611879565b9161206261285e565b61206b81612ef4565b612076848483613033565b92612090604051612086816103bb565b600081528361321a565b61209a848361340a565b6120a2613864565b906000194301438111610d17576120ff60946106169861211395604051948286936020850198893783019144602084015260018060601b03199060601b1660408301524260548301524060748201520360748101845201826103f1565b519020916000526013602052604060002090565b557f6df1d8db2a036436ffe0b2d1833f2c5f1e624818dfce2578c0faa4b83ef9998d60405180612143858261079c565b0390a26040519182918261079c565b34610473576060366003190112610473576001600160401b0360043560243582811161047357612186903690600401611333565b9091604435848111610473576121a0903690600401611333565b9390946121ab61285e565b846122b7575b50811561228a57601254926121c736828461043c565b9280850195868611610d1757600f5497600160401b89101561039b5761226a612265612277946122567f2a0365091ef1a40953c670dce28177e37520648a6fdc91506bffac0ab045570d996122438d6106169f80600161222a9201600f55612eb2565b90919082549060031b91821b91600019901b1916179055565b8c60005260106020526040600020613343565b61225f8b601255565b896130eb565b612a54565b9360405195869586613317565b0390a26040519081529081906020820190565b60405162461bcd60e51b81526020600482015260056024820152640c08185b5d60da1b6044820152606490fd5b848601604087820312610473578635918211610473576122d8918701610478565b51151580612313575b6122ec575b386121b1565b601254828101809111610d175761230e9061230836878961043c565b9061321a565b6122e6565b50602085013515156122e1565b346104735760403660031901126104735761075f60243560043561234382610677565b80600052600860205261235d6001604060002001546128f0565b612995565b346104735761237036611cde565b61237b611dc8614358565b80515161238f61238a82614d5a565b614990565b61239d8251611e3383614640565b81516040908101516001600160a01b0392906123bc9084161515614a24565b602092838501908151519560005b8781106123d357005b808461245161243f6124328a6124146124228e806124038a6123fc611fd36125149e8d51612b7d565b9a51612b7d565b510151935192839182018095612b2d565b03601f1981018352826103f1565b5190206001600160e01b03191690565b6001600160e01b03191690565b6001600160e01b031992831614614a9f565b61247361246d6114416002611df2611fd9611fd3888d51612b7d565b15614b07565b6124898451611e33611fd9611fd3868b51612b7d565b6124aa600361249789614640565b016124a3848951612b7d565b5190614b72565b83518801516001600160a01b0316907fb5a3e9571e367979a4a14de42b248d0837c26fd8e879846062abcf7cee1712736124e8611fd3858a51612b7d565b918b6124f5868b51612b7d565b5101519261250c898d51938493169616948261079c565b0390a36130dd565b6123ca565b34610473576040366003190112610473576001600160401b036004358181116104735761254a903690600401610478565b90602435908111610473576106169161256a6118c2923690600401611333565b916130f8565b34610473576000806003193601126108865760405181601454612592816118ec565b8084529060019081811690811561085e57506001146125bb57610616846107f9818803826103f1565b60148352602094507fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec5b8284106126015750505081610616936107f992820101936107e9565b80548585018701529285019281016125e5565b3461047357604036600319011261047357602060ff6116cf60043561263881610677565b6024359061264582610677565b6001600160a01b03166000908152600785526040902061277c565b346104735761266e36610496565b612679611dc8614358565b61268a61268582614eb6565b614bbe565b6126986002611df283614640565b6126b76126b26126ac6003611e4786614640565b93614640565b614d37565b8151906001600160a01b031660005b8281106126cf57005b806126e0611fd36127439387612b7d565b837f5968261591c9d57680edfe0bed3bb6a37ab7fb354578affd1e5be8ce18e6c9d361272d6020612711868b612b7d565b5101516040516001600160e01b0319909516949182918261079c565b0390a3611fe3611fde611fd9611fd38489612b7d565b6126c6565b6001600160a01b0316600090815260076020526040902090565b6001600160a01b0316600090815260056020526040902090565b9060018060a01b0316600052602052604060002090565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6600052600860205260ff906127e9907f51a495916474fe1a0c0fcfb65a8a97682b84a054118858cdd1f5dfd7fc0919eb61277c565b541690565b6127f6613864565b60008052600860205260ff61282b827f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c761277c565b5416156128355750565b60405163e2517d3f60e01b81526001600160a01b03909116600482015260006024820152604490fd5b612866613864565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a66000819052600860205260ff6128bd837f51a495916474fe1a0c0fcfb65a8a97682b84a054118858cdd1f5dfd7fc0919eb61277c565b5416156128c8575050565b60405163e2517d3f60e01b81526001600160a01b039092166004830152602482015260449150fd5b6128f8613864565b9080600052600860205260ff6128bd83604060002061277c565b600090808252600860205260ff61292c846040852061277c565b541661298f578082526008602052612947836040842061277c565b805460ff191660011790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d6001600160a01b0380612984613864565b1694169280a4600190565b50905090565b600090808252600860205260ff6129af846040852061277c565b54161561298f5780825260086020526129cb836040842061277c565b805460ff191690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b6001600160a01b0380612984613864565b60405190612a1282610380565b6009546001600160a01b038116835260a01c6020830152565b634e487b7160e01b600052601160045260246000fd5b81810292918115918404141715610d1757565b600019810191908211610d1757565b91908203918211610d1757565b90612a7a82611c0b565b612a8760405191826103f1565b8281528092612a98601f1991611c0b565b019060005b828110612aa957505050565b806060602080938501015201612a9d565b6000198114610d175760010190565b634e487b7160e01b600052603260045260246000fd5b903590601e198136030182121561047357018035906001600160401b0382116104735760200191813603831361047357565b90821015612b28576113a79160051b810190612adf565b612ac9565b90612b40602092828151948592016104c0565b0190565b6020908161041f939594612b718760405198899585870137840191838301600081528151948592016104c0565b010380855201836103f1565b8051821015612b285760209160051b010190565b6001600160a01b03612ba1613864565b1633149160008093600014612c215750604051612bbd816103bb565b83815283368137905b612bcf81612a70565b935b818110612bdf575050505090565b80612c01612bfb85612bf5612c1c95878a612b11565b90612b44565b30612c84565b612c0b8288612b7d565b52612c168187612b7d565b50612aba565b612bd1565b36601319810194508411610d175760405190612c3c82610380565b60148252601481956020840137603482015290612bc6565b3d15612c7f573d90612c6582610421565b91612c7360405193846103f1565b82523d6000602084013e565b606090565b60008061049393602081519101845af4612c9c612c54565b9190612cc45750805115612cb257805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580612cf7575b612cd5575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15612ccd565b60ff8114612d3e5760ff811690601f8211612d2c5760405191612d2283610380565b8252602082015290565b604051632cd44ac360e21b8152600490fd5b50604051600b54816000612d51836118ec565b808352600193808516908115612dd75750600114612d77575b50610493925003826103f1565b600b60009081527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db994602093509091905b818310612dbf575050610493935082010138612d6a565b85548784018501529485019486945091830191612da8565b905061049394506020925060ff191682840152151560051b82010138612d6a565b60ff8114612e1a5760ff811690601f8211612d2c5760405191612d2283610380565b50604051600c54816000612e2d836118ec565b808352600193808516908115612dd75750600114612e525750610493925003826103f1565b600c60009081527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c794602093509091905b818310612e9a575050610493935082010138612d6a565b85548784018501529485019486945091830191612e83565b600f54811015612b2857600f60005260206000200190600090565b600080516020614ff88339815191528054821015612b285760005260206000200190600090565b600f54811015612f2957600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac802015490565b60405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b6044820152606490fd5b90929192612f6b81610421565b91612f7960405193846103f1565b82948284528282011161047357602061041f9301906104c0565b91906040838203126104735782516001600160401b03811161047357830181601f82011215610473576020918183612fcd93519101612f5e565b92015190565b926020809594612feb839594828151948592016104c0565b01918237019081520190565b15612ffe57565b60405162461bcd60e51b815260206004820152600d60248201526c496e636f7272656374206b657960981b6044820152606490fd5b92919092600052600e6020526119f2613056604060002060405192838092611926565b8051156130a4578161241461309b61308661041f9561307f866020808c99518301019101612f93565b96906130f8565b96604051928391602083019546918b88612fd3565b51902014612ff7565b60405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81d1bc81c995d99585b607a1b6044820152606490fd5b9060018201809211610d1757565b91908201809211610d1757565b909291928151916040918251956020938486890101815285885260005b8681106131255750505050505050565b815186810190848683376131498482878101868c820152038a8101845201826103f1565b5190208682870101511886828b0101528581018091111561311557612a2b565b600052600e60205261317f6040600020546118ec565b151590565b81811061318f575050565b60008155600101613184565b90601f82116131a8575050565b61041f9160146000526020600020906020601f840160051c830193106131d6575b601f0160051c0190613184565b90915081906131c9565b9190601f81116131ef57505050565b61041f926000526020600020906020601f840160051c830193106131d657601f0160051c0190613184565b9190916000526020600e81526040600020908351906001600160401b03821161039b576132518261324b85546118ec565b856131e0565b80601f831160011461328b575081929394600092613280575b50508160011b916000199060031b1c1916179055565b01519050388061326a565b90601f198316956132a185600052602060002090565b926000905b8882106132de575050836001959697106132c5575b505050811b019055565b015160001960f88460031b161c191690553880806132bb565b806001859682949686015181550195019301906132a6565b908060209392818452848401376000828201840152601f01601f1916010190565b939161049395936133359286526060602087015260608601916132f6565b9260408185039101526132f6565b91909182516001600160401b03811161039b5761336a8161336484546118ec565b846131e0565b602080601f831160011461339a5750819293946000926132805750508160011b916000199060031b1c1916179055565b90601f198316956133b085600052602060002090565b926000905b8882106133d3575050836001959697106132c557505050811b019055565b806001859682949686015181550195019301906133b5565b9190826040910312610473576020823561340481610677565b92013590565b80600052601160205260ff60406000205416613473576134587f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c928260005260106020526040600020613343565b613461816134f7565b604080519182526020820192909252a1565b60405162461bcd60e51b815260206004820152600c60248201526b2130ba31b410333937bd32b760a11b6044820152606490fd5b60405190600f548083528260209182820190600f60005283600020936000905b8282106134dd5750505061041f925003836103f1565b8554845260019586019588955093810193909101906134c7565b600f54906135036134a7565b600091825b8481106135465760405162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a590818985d18da1259608a1b6044820152606490fd5b6135508184612b7d565b5182146135655761356090612aba565b613508565b91929350508061357457505090565b9091506000198101908111610d175761358c91612b7d565b5190565b6135998161367b565b906135a3836136e0565b926135ad82613169565b156135dc575050506104936135cf916124146040519384926020840190612b2d565b600360fc1b815260010190565b906135e79291613761565b6040519060a08201604052608082019060008252905b6000190190600a9060308282060183530490816135fd57905061363f92613645610493936080601f199485810192030181526040519586936020850190612b2d565b90612b2d565b039081018352826103f1565b60609060208152600f60208201526e125b9d985b1a59081d1bdad95b9259608a1b60408201520190565b90600f54916136886134a7565b9060005b8481106136ac5760405162461bcd60e51b81528061077a60048201613651565b6136b68184612b7d565b5182106136ce57600181018091111561368c57612a2b565b935050826136db91612b7d565b519190565b90600f54916136ed6134a7565b600091825b8581106137125760405162461bcd60e51b81528061077a60048201613651565b61371c8184612b7d565b5182106137345760018101809111156136f257612a2b565b6119f294955061374991506104939392612b7d565b51815260106020526040809120905192838092611926565b919080600052601360205260406000205480156137df57600092806137c0575b5082820391808311610d175783146137aa578190068301809311610d17576104939206906130eb565b634e487b7160e01b600052601260045260246000fd5b9092506000198101908111610d17576137d890612ef4565b9138613781565b50505090565b63ffffffff60e01b16637965db0b60e01b8114908115613803575090565b6301ffc9a760e01b14919050565b80546001600160a01b0319166001600160a01b03909216919091179055565b90602060018060a01b03916138488382511685613811565b0151825490911660a09190911b6001600160a01b031916179055565b337f000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a816001600160a01b031614806138b2575b156138ae576013193601368111610d17573560601c90565b3390565b506014361015613896565b91610e5561397b610d869261397561398c9561396d6138db896139a1565b6138e760208b016139a1565b906139056138fe60408d01359c6060810190612adf565b369161043c565b602081519101206040519160208301937f242b17107e6aef17754836dd680cb66bbf39e46a5f20952950acbbae68643d02855260018060801b0380921660408501521660608301528b608083015260a082015260a08152613965816103d6565b519020613c17565b92369161043c565b90613a92565b93600052600d602052604060002090565b806139945791565b5061399e81612793565b91565b356001600160801b03811681036104735790565b92916139c191846138bd565b929015613a63576139d1816139a1565b426001600160801b03909116118015613a40575b613a1157613a04604061041f920135600052600d602052604060002090565b805460ff19166001179055565b60405162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b6044820152606490fd5b50613a5c613a50602083016139a1565b6001600160801b031690565b42116139e5565b60405162461bcd60e51b8152602060048201526007602482015266125b9d985b1a5960ca1b6044820152606490fd5b61049391613a9f91613aa8565b90929192613b8a565b8151919060418303613ad957613ad292506020820151906060604084015193015160001a90613af0565b9192909190565b505060009160029190565b6040513d6000823e3d90fd5b91906fa2a8918ca85bafe22016d0b997e4df60600160ff1b038411613b5e57926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa1561114b5780516001600160a01b03811615613b5557918190565b50809160019190565b50505060009160039190565b60041115613b7457565b634e487b7160e01b600052602160045260246000fd5b613b9381613b6a565b80613b9c575050565b613ba581613b6a565b60018103613bbf5760405163f645eedf60e01b8152600490fd5b613bc881613b6a565b60028103613be95760405163fce698f760e01b815260048101839052602490fd5b80613bf5600392613b6a565b14613bfd5750565b6040516335e2f38360e21b81526004810191909152602490fd5b604290613c22613c3d565b906040519161190160f01b8352600283015260228201522090565b307f000000000000000000000000c31d52a017e523164ba0a09ac19385c10bc2b8906001600160a01b03161480613d2e575b15613c98577f456a661c7c14e451543280845a80df85e137443e4ac9282e9d52c106ce8b101490565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f9d4ffaa97baca5653914910a43096d3910699e440d067aae25095cef149d7c4e60408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152613d28816103d6565b51902090565b507f00000000000000000000000000000000000000000000000000000000000000014614613c6f565b8060009081548110613d76575b604051636f96cda160e11b8152600490fd5b81526004906020918083526040928383205494600160e01b861615613d9d57505050613d64565b93929190935b8515613db157505050505090565b60001901808352818552838320549550613da3565b90613dd083613d57565b6001600160a01b0383811692828216849003613eff57600086815260066020526040902080549092613e116001600160a01b03881633908114908414171590565b613ed5575b8216958615613ec357613e4993613e3c92613e31858461404b565b613eb9575b50612762565b8054600019019055612762565b80546001019055600160e11b4260a01b84178117613e6686611511565b55811615613e85575b50600080516020615018833981519152600080a4565b60018401613e9281611511565b5415613e9f575b50613e6f565b6000548114613e9957613eb190611511565b553880613e99565b6000905538613e36565b604051633a954ecd60e21b8152600490fd5b613ee8610d86610e5533610e508b612748565b15613e1657604051632ce44b5f60e11b8152600490fd5b60405162a1148160e81b8152600490fd5b613f18611521565b9060009182805260205260ff6040832054161580613fb5575b80613fae575b613f3f575050565b613f4d60ff91610e50611521565b5416159081613f8f575b50613f5e57565b60405162461bcd60e51b815260206004820152600960248201526810aa2920a729a322a960b91b6044820152606490fd5b60ff9150604090613f9e611521565b8180526020522054161538613f57565b5081613f37565b506001600160a01b0381161515613f31565b613fcf611521565b60009081805260205260ff6040822054161580614044575b80614032575b613ff5575050565b604060ff91614002611521565b8180526020522054161590816140195750613f5e57565b60ff915061402990610e50611521565b54161538613f57565b506001600160a01b0382161515613fed565b5080613fe7565b614053611521565b6000805260205261406c60ff6040600020541615151590565b806140cb575b806140b9575b614080575050565b610d86610e5561409292610e50611521565b908161409f5750613f5e57565b6140b39150610e55610d8691610e50611521565b38613f57565b506001600160a01b0382161515614078565b506001600160a01b0381161515614072565b9291906140eb828286613dc6565b803b6140f75750505050565b61410093614166565b1561410e5738808080611953565b6040516368d2bf6b60e11b8152600490fd5b9081602091031261047357516104938161059d565b6001600160a01b039182168152911660208201526040810191909152608060608201819052610493929101906104e3565b9260209161418f936000604051809681958294630a85bd0160e11b9a8b85523360048601614135565b03926001600160a01b03165af1600091816141df575b506141d1576141b2612c54565b805190816141cc576040516368d2bf6b60e11b8152600490fd5b602001fd5b6001600160e01b0319161490565b61420191925060203d8111614208575b6141f981836103f1565b810190614120565b90386141a5565b503d6141ef565b9060009081549281156142b05761422581613fc7565b61422e81612762565b80546001600160401b0184020190556001600160a01b0316906001904260a01b81831460e11b17831761426086611511565b558401938160008051602061501883398151915291808587858180a4015b8581036142a157505050156142905755565b604051622e076360e81b8152600490fd5b8083918587858180a40161427e565b60405163b562e8dd60e01b8152600490fd5b600080806001600160a01b0360406142e483356001600160e01b0319166148bd565b015116368280378136915af43d82803e156142fd573d90f35b3d90fd5b1561430857565b60405162461bcd60e51b815260206004820152602260248201527f42617365526f757465723a2063616c6c6572206e6f7420617574686f72697a65604482015261321760f11b6064820152608490fd5b60ff6127e9614365613864565b6000805260086020527f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c761277c565b9080601f8301121561047357815161049392602001612f5e565b91909160608184031261047357604051906143c8826103a0565b81938151916001600160401b039283811161047357826143e9918301614394565b84526020810151928311610473576144076040939284938301614394565b602085015201519161441883610677565b0152565b9190604092838183031261047357835161443581610380565b80948251936001600160401b039485811161047357816144569186016143ae565b8352602093848101519086821161047357019181601f840112156104735782519061448082611c0b565b9661448d825198896103f1565b828852868089019360051b8601019484861161047357878101935b8685106144ba57505050505050500152565b84518381116104735782019084601f198389030112610473578451906144df82610380565b8a8301516144ec8161059d565b825285830151918583116104735761450b898d80969581960101614394565b838201528152019401936144a8565b9060209081838203126104735782516001600160401b0393848211610473570181601f8201121561047357805161455081611c0b565b9461455e60405196876103f1565b818652848087019260051b8401019380851161047357858401925b85841061458a575050505050505090565b83518381116104735787916145a4848480948a010161441c565b815201930192614579565b604051906145bc826103a0565b600060408360608152606060208201520152565b604051906145dd82610380565b60606020836145ea6145af565b81520152565b906145fa82611c0b565b61460760405191826103f1565b8281528092614618601f1991611c0b565b019060005b82811061462957505050565b6020906146346145d0565b8282850101520161461d565b60206146599181604051938285809451938492016104c0565b81017f1a039940024227c284ceea7ab90e5603ce17de27c93816eef22d65b14ee0087581520301902090565b602061469e9181604051938285809451938492016104c0565b810160008051602061503883398151915281520301902090565b906040516146c5816103a0565b6040819381516146d9816119f28185611926565b835281516146ee816119f28160018601611926565b6020840152600201546001600160a01b0316910152565b90815461471181611c0b565b92604093614721855191826103f1565b828152809460208092019260005281600020906000935b85851061474757505050505050565b600284600192845161475881610380565b865460e01b6001600160e01b0319168152855161477b816119f281898c01611926565b83820152815201930194019391614738565b9060405161479a81610380565b60206147b4600383956147ac816146b8565b855201614705565b910152565b6147c16145d0565b506147cb81614685565b54600090156147e1575061106b61049391614640565b80614800926040518094819263611383f760e11b83526004830161079c565b03817f000000000000000000000000dc2550635745d4400343721c934c353a69c5737c6001600160a01b03165afa91821561114b57819261484057505090565b909291503d8084833e61485381836103f1565b810190602081830312614881578051906001600160401b03821161487d576104939394500161441c565b8480fd5b8380fd5b63ffffffff60e01b166000527f1a039940024227c284ceea7ab90e5603ce17de27c93816eef22d65b14ee00876602052604060002090565b6148c56145af565b506148d76148d282614885565b6146b8565b604081015190916001600160a01b0391600091908316156148f85750505090565b81929350602460405180958193631097b48960e11b835263ffffffff60e01b1660048301527f000000000000000000000000dc2550635745d4400343721c934c353a69c5737c165afa91821561114b57819261495357505090565b909291503d8084833e61496681836103f1565b810190602081830312614881578051906001600160401b03821161487d57610493939450016143ae565b1561499757565b60405162461bcd60e51b815260206004820152602960248201527f457874656e73696f6e53746174653a20657874656e73696f6e20616c726561646044820152683c9032bc34b9ba399760b91b6064820152608490fd5b600261041f926149ff815184613343565b614a10602082015160018501613343565b604001516001600160a01b03169101613811565b15614a2b57565b60405162461bcd60e51b815260206004820152603860248201527f457874656e73696f6e53746174653a20616464696e6720657874656e73696f6e604482015277103bb4ba3437baba1034b6b83632b6b2b73a30ba34b7b71760411b6064820152608490fd5b516001600160e01b03191690565b15614aa657565b60405162461bcd60e51b815260206004820152603360248201527f457874656e73696f6e53746174653a20666e2073656c6563746f7220616e642060448201527239b4b3b730ba3ab9329036b4b9b6b0ba31b41760691b6064820152608490fd5b15614b0e57565b60405162461bcd60e51b815260206004820152603660248201527f457874656e73696f6e53746174653a20657874656e73696f6e20616c726561646044820152753c9032bc34b9ba39903337b910333ab731ba34b7b71760511b6064820152608490fd5b908154600160401b81101561039b5760018101808455811015612b2857602060019161041f946000528160002090831b0192805160e01c63ffffffff1985541617845501519101613343565b15614bc557565b60405162461bcd60e51b815260206004820152602960248201527f457874656e73696f6e53746174653a20657874656e73696f6e20646f6573206e60448201526837ba1032bc34b9ba1760b91b6064820152608490fd5b15614c2357565b60405162461bcd60e51b815260206004820152602960248201527f457874656e73696f6e53746174653a2072652d616464696e672073616d6520656044820152683c3a32b739b4b7b71760b91b6064820152608490fd5b614c8481546118ec565b9081614c8e575050565b81601f60009311600114614ca0575055565b908083918252614cbf601f60208420940160051c840160018501613184565b5555565b805460009182815581614cd557505050565b6001600160ff1b0382168203610d175782526020822091600191821b8301925b838110614d025750505050565b808260029255614d13848201614c7a565b01614cf5565b6002600091614d2781614c7a565b614d3360018201614c7a565b0155565b600361041f91611e5d81614d19565b9190614d555761041f91613343565b6118d6565b614d6381614685565b54614dde57600080516020614ff88339815191528054600160401b81101561039b5760018101808355811015612b285781614dac84602093614dc1956000528460002001613343565b549281604051938285809451938492016104c0565b810160008051602061503883398151915281520301902055600190565b50600090565b6020614dfd9181604051938285809451938492016104c0565b810160008051602061503883398151915281520301902054151590565b600080516020614ff883398151915280548015614ea0576000190190614e3f82612ecd565b614d5557614e4d81546118ec565b9081614e5857505055565b601f8211600114614e6b57600091505555565b614e8d614e9d92826000526001601f6020600020920160051c82019101613184565b6000908082528160208120915555565b55565b634e487b7160e01b600052603160045260246000fd5b6040516020818351614ecb81838588016104c0565b60008051602061503883398151915290820190815203019020548015614f7c576000199181830191808311610d1757600080516020614ff883398151915254938401938411610d17578383614f339460009603614f39575b505050614f2e614e1a565b614685565b55600190565b614f2e614f6191614f6d614f4f614f7395612ecd565b5091614f686040518096818096611926565b03846103f1565b612ecd565b90614d46565b55388080614f23565b5050600090565b600080516020614ff8833981519152908154614f9e81611c0b565b92604093614fae855191826103f1565b828152809460208092019260005281600020906000935b858510614fd457505050505050565b60018481928451614fe9816119f2818a611926565b815201930194019391614fc556fe1a039940024227c284ceea7ab90e5603ce17de27c93816eef22d65b14ee00873ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1a039940024227c284ceea7ab90e5603ce17de27c93816eef22d65b14ee00874a164736f6c6343000814000a
Verified Source Code Partial Match
Compiler: v0.8.20+commit.a1b79de6
EVM: paris
Optimization: Yes (20 runs)
Petopia.sol 276 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import "@openzeppelin/contracts/utils/Multicall.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "erc721a/contracts/extensions/ERC721ABurnable.sol";
import "lib/dynamic-contracts/src/presets/BaseRouter.sol";
import "./SignatureAction.sol";
import "./DelayedReveal.sol";
import "./LazyMint.sol";
contract Petopia is
ERC721ABurnable,
AccessControl,
ERC2771Context,
ERC2981,
Multicall,
BaseRouter,
SignatureAction,
DelayedReveal,
LazyMint
{
bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 private constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
mapping(uint256 => bytes32) private tokenIdOffset;
/// @notice Emitted when tokens are claimed via `claimWithSignature`.
event TokensClaimed(
address indexed claimer,
address indexed receiver,
uint256 startTokenId,
uint256 quantityClaimed
);
/*///////////////////////////////////////////////////////////////
Constructor and Initializer logic
//////////////////////////////////////////////////////////////*/
constructor(
address _defaultAdmin,
string memory _name,
string memory _symbol,
string memory _contractURI,
address _royaltyRecipient,
uint16 _royaltyBps,
Extension[] memory _extensions,
address _trustedForwarders
)
BaseRouter(_extensions)
ERC2771Context(_trustedForwarders)
ERC721A(_name, _symbol)
{
_baseContractURI = _contractURI;
_setupRoles(_defaultAdmin);
_setDefaultRoyalty(_royaltyRecipient, _royaltyBps);
}
function _setupRoles(address _defaultAdmin) internal {
_setRoleAdmin(DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(TRANSFER_ROLE, DEFAULT_ADMIN_ROLE);
_grantRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
_grantRole(MINTER_ROLE, _defaultAdmin);
_grantRole(TRANSFER_ROLE, address(0));
}
function mint(address to, uint quantity) external onlyRole(MINTER_ROLE) {
uint256 tokenIdToMint = _nextTokenId();
if (tokenIdToMint + quantity > nextTokenIdToLazyMint) {
revert("!Tokens");
}
_mint(to, quantity);
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
function _beforeTokenTransfers(
address from,
address to,
uint startTokenId,
uint quantity
) internal virtual override {
super._beforeTokenTransfers(from, to, startTokenId, quantity);
// if transfer is restricted on the contract, we still want to allow burning and minting
if (!hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) {
if (!hasRole(TRANSFER_ROLE, from) && !hasRole(TRANSFER_ROLE, to)) {
revert("!TRANSFER");
}
}
}
function _canSetExtension() internal view virtual override returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function _isAuthorizedSigner(address _signer) internal view override returns (bool) {
return hasRole(MINTER_ROLE, _signer);
}
/*///////////////////////////////////////////////////////////////
Lazy minting + delayed-reveal logic
//////////////////////////////////////////////////////////////*
/**
* @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs.
* The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`.
*/
function lazyMint(
uint256 _amount,
string calldata _baseURIForTokens,
bytes calldata _data
) external onlyRole(MINTER_ROLE) returns (uint256 batchId) {
if (_data.length > 0) {
(bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
if (encryptedURI.length != 0 && provenanceHash != "") {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
}
}
return _lazyMint(_amount, _baseURIForTokens, _data);
}
function claimWithSignature(GenericRequest calldata _req, bytes calldata _signature)
external
returns (address signer)
{
(
address to,
uint256 quantity
) = abi.decode(_req.data, (address, uint256));
if (quantity == 0) {
revert("qty");
}
uint256 tokenIdToMint = _nextTokenId();
if (tokenIdToMint + quantity > nextTokenIdToLazyMint) {
revert("!Tokens");
}
// Verify and process payload.
signer = _processRequest(_req, _signature);
// Mint tokens.
_mint(to, quantity);
emit TokensClaimed(_msgSender(), to, tokenIdToMint, quantity);
}
/// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
function reveal(uint256 _index, bytes calldata _key)
external
onlyRole(MINTER_ROLE)
returns (string memory revealedURI)
{
uint256 batchId = getBatchIdAtIndex(_index);
revealedURI = getRevealURI(batchId, _key);
_setEncryptedData(batchId, "");
_setBaseURI(batchId, revealedURI);
_scrambleOffset(batchId, _key);
emit TokenURIRevealed(_index, revealedURI);
}
/*///////////////////////////////////////////////////////////////
Metadata, EIP 165 / 721 / 2981 / 2771 logic
//////////////////////////////////////////////////////////////*/
/// @dev Returns the URI for a given tokenId.
function tokenURI(uint256 _tokenId) public view virtual override(ERC721A, IERC721A) returns (string memory) {
(uint256 batchId,uint256 index) = _getBatchId(_tokenId);
string memory batchUri = _getBaseURI(_tokenId);
if (isEncryptedBatch(batchId)) {
return string(abi.encodePacked(batchUri, "0"));
} else {
uint256 fairMetadataId = _getFairMetadataId(_tokenId, batchId, index);
return string(abi.encodePacked(batchUri, _toString(fairMetadataId)));
}
}
/// @dev Returns the fair metadata ID for a given tokenId.
function _getFairMetadataId(
uint256 _tokenId,
uint256 _batchId,
uint256 _indexOfBatchId
) private view returns (uint256 fairMetadataId) {
bytes32 bytesRandom = tokenIdOffset[_batchId];
if (bytesRandom == bytes32(0)) {
return _tokenId;
}
uint256 randomness = uint256(bytesRandom);
uint256 prevBatchId;
if (_indexOfBatchId > 0) {
prevBatchId = getBatchIdAtIndex(_indexOfBatchId - 1);
}
uint256 batchSize = _batchId - prevBatchId;
uint256 offset = randomness % batchSize;
fairMetadataId = prevBatchId + (_tokenId + offset) % batchSize;
}
string private _baseContractURI;
function contractURI() public view returns (string memory) {
return _baseContractURI;
}
function setContractURI(string memory _uri) public onlyRole(DEFAULT_ADMIN_ROLE) {
_baseContractURI = _uri;
}
function setBaseURI(uint256 _batchId, string memory _baseURI) public onlyRole(DEFAULT_ADMIN_ROLE) {
_setBaseURI(_batchId, _baseURI);
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(AccessControl, ERC721A, IERC721A, ERC2981)
returns (bool)
{
return ERC721A.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId);
}
/// @dev Scrambles tokenId offset for a given batchId.
function _scrambleOffset(uint256 _batchId, bytes calldata _seed) private {
tokenIdOffset[_batchId] = keccak256(abi.encodePacked(_seed, block.prevrandao, _msgSender(), block.timestamp, blockhash(block.number - 1)));
}
function setDefaultRoyalty(address receiver, uint96 feeNumerator)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
_setDefaultRoyalty(receiver, feeNumerator);
}
function setTokenRoyalty(
uint tokenId,
address receiver,
uint96 feeNumerator
) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setTokenRoyalty(tokenId, receiver, feeNumerator);
}
function _msgSender()
internal
view
virtual
override(Context, ERC2771Context)
returns (address sender)
{
return ERC2771Context._msgSender();
}
function _msgData()
internal
view
virtual
override(Context, ERC2771Context)
returns (bytes calldata)
{
return ERC2771Context._msgData();
}
function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint) {
return ERC2771Context._contextSuffixLength();
}
}
LazyMint.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/ILazyMint.sol";
import "./BatchMintMetadata.sol";
/**
* The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
* at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
* minting a non-zero balance of NFTs of those tokenIds.
*/
abstract contract LazyMint is ILazyMint, BatchMintMetadata {
/// @notice The tokenId assigned to the next new NFT to be lazy minted.
uint256 internal nextTokenIdToLazyMint;
/**
* @notice Lets an authorized address lazy mint a given amount of NFTs.
*
* @param _amount The number of NFTs to lazy mint.
* @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
* of those NFTs is `${baseURIForTokens}/${tokenId}`.
* @param _data Additional bytes data to be used at the discretion of the consumer of the contract.
* @return batchId A unique integer identifier for the batch of NFTs lazy minted together.
*/
function _lazyMint(
uint256 _amount,
string calldata _baseURIForTokens,
bytes calldata _data
) internal returns (uint256 batchId) {
if (_amount == 0) {
revert("0 amt");
}
uint256 startId = nextTokenIdToLazyMint;
(nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
return batchId;
}
}
DelayedReveal.sol 81 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IDelayedReveal.sol";
abstract contract DelayedReveal is IDelayedReveal {
/// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.
mapping(uint256 => bytes) public encryptedData;
/// @dev Sets the delayed reveal data for a batchId.
function _setEncryptedData(uint256 _batchId, bytes memory _encryptedData) internal {
encryptedData[_batchId] = _encryptedData;
}
function getRevealURI(uint256 _batchId, bytes calldata _key) public view returns (string memory revealedURI) {
bytes memory data = encryptedData[_batchId];
if (data.length == 0) {
revert("Nothing to reveal");
}
(bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(data, (bytes, bytes32));
revealedURI = string(encryptDecrypt(encryptedURI, _key));
require(keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) == provenanceHash, "Incorrect key");
}
/**
* @notice Encrypt/decrypt data on chain.
* @dev Encrypt/decrypt given `data` with `key`. Uses inline assembly.
* See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
*
* @param data Bytes of data to encrypt/decrypt.
* @param key Secure key used by caller for encryption/decryption.
*
* @return result Output after encryption/decryption of given data.
*/
function encryptDecrypt(bytes memory data, bytes calldata key) public pure override returns (bytes memory result) {
// Store data length on stack for later use
uint256 length = data.length;
// solhint-disable-next-line no-inline-assembly
assembly {
// Set result to free memory pointer
result := mload(0x40)
// Increase free memory pointer by lenght + 32
mstore(0x40, add(add(result, length), 32))
// Set result length
mstore(result, length)
}
// Iterate over the data stepping by 32 bytes
for (uint256 i = 0; i < length; i += 32) {
// Generate hash of the key and offset
bytes32 hash = keccak256(abi.encodePacked(key, i));
bytes32 chunk;
// solhint-disable-next-line no-inline-assembly
assembly {
// Read 32-bytes data chunk
chunk := mload(add(data, add(i, 32)))
}
// XOR the chunk with hash
chunk ^= hash;
// solhint-disable-next-line no-inline-assembly
assembly {
// Write 32-byte encrypted chunk
mstore(add(result, add(i, 32)), chunk)
}
}
}
/**
* @notice Returns whether the relvant batch of NFTs is subject to a delayed reveal.
* @dev Returns `true` if `_batchId`'s base URI is encrypted.
* @param _batchId ID of a batch of NFTs.
*/
function isEncryptedBatch(uint256 _batchId) public view returns (bool) {
return encryptedData[_batchId].length > 0;
}
}
SignatureAction.sol 69 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./interfaces/ISignatureAction.sol";
abstract contract SignatureAction is EIP712, ISignatureAction {
using ECDSA for bytes32;
bytes32 private constant TYPEHASH =
keccak256("GenericRequest(uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid,bytes data)");
/// @dev Mapping from a signed request UID => whether the request is processed.
mapping(bytes32 => bool) private executed;
constructor()
EIP712("SignatureAction", "1")
{}
/// @dev Verifies that a request is signed by an authorized account.
function verify(GenericRequest calldata _req, bytes calldata _signature)
public
view
override
returns (bool success, address signer)
{
signer = _recoverAddress(_req, _signature);
success = !executed[_req.uid] && _isAuthorizedSigner(signer);
}
/// @dev Returns whether a given address is authorized to sign requests.
function _isAuthorizedSigner(address _signer) internal view virtual returns (bool);
/// @dev Verifies a request and marks the request as processed.
function _processRequest(GenericRequest calldata _req, bytes calldata _signature)
internal
returns (address signer)
{
bool success;
(success, signer) = verify(_req, _signature);
if (!success) {
revert("Invalid");
}
if (_req.validityStartTimestamp > block.timestamp || block.timestamp > _req.validityEndTimestamp) {
revert("expired");
}
executed[_req.uid] = true;
}
/// @dev Returns the address of the signer of the request.
function _recoverAddress(GenericRequest calldata _req, bytes calldata _signature) internal view returns (address) {
return _hashTypedDataV4(keccak256(_encodeRequest(_req))).recover(_signature);
}
/// @dev Encodes a request for recovery of the signer in `recoverAddress`.
function _encodeRequest(GenericRequest calldata _req) internal pure returns (bytes memory) {
return
abi.encode(
TYPEHASH,
_req.validityStartTimestamp,
_req.validityEndTimestamp,
_req.uid,
keccak256(_req.data)
);
}
}
ERC721A.sol 1091 lines
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import './IERC721A.sol';
/**
* @dev Interface of ERC721 token receiver.
*/
interface ERC721A__IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
/**
* @title ERC721A
*
* @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
* Non-Fungible Token Standard, including the Metadata extension.
* Optimized for lower gas during batch mints.
*
* Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
* starting from `_startTokenId()`.
*
* Assumptions:
*
* - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
* - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721A is IERC721A {
// Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
struct TokenApprovalRef {
address value;
}
// =============================================================
// CONSTANTS
// =============================================================
// Mask of an entry in packed address data.
uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
// The bit position of `numberMinted` in packed address data.
uint256 private constant _BITPOS_NUMBER_MINTED = 64;
// The bit position of `numberBurned` in packed address data.
uint256 private constant _BITPOS_NUMBER_BURNED = 128;
// The bit position of `aux` in packed address data.
uint256 private constant _BITPOS_AUX = 192;
// Mask of all 256 bits in packed address data except the 64 bits for `aux`.
uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
// The bit position of `startTimestamp` in packed ownership.
uint256 private constant _BITPOS_START_TIMESTAMP = 160;
// The bit mask of the `burned` bit in packed ownership.
uint256 private constant _BITMASK_BURNED = 1 << 224;
// The bit position of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
// The bit mask of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
// The bit position of `extraData` in packed ownership.
uint256 private constant _BITPOS_EXTRA_DATA = 232;
// Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
// The mask of the lower 160 bits for addresses.
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
// The maximum `quantity` that can be minted with {_mintERC2309}.
// This limit is to prevent overflows on the address data entries.
// For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
// is required to cause an overflow, which is unrealistic.
uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
// The `Transfer` event signature is given by:
// `keccak256(bytes("Transfer(address,address,uint256)"))`.
bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
// =============================================================
// STORAGE
// =============================================================
// The next token ID to be minted.
uint256 private _currentIndex;
// The number of tokens burned.
uint256 private _burnCounter;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to ownership details
// An empty struct value does not necessarily mean the token is unowned.
// See {_packedOwnershipOf} implementation for details.
//
// Bits Layout:
// - [0..159] `addr`
// - [160..223] `startTimestamp`
// - [224] `burned`
// - [225] `nextInitialized`
// - [232..255] `extraData`
mapping(uint256 => uint256) private _packedOwnerships;
// Mapping owner address to address data.
//
// Bits Layout:
// - [0..63] `balance`
// - [64..127] `numberMinted`
// - [128..191] `numberBurned`
// - [192..255] `aux`
mapping(address => uint256) private _packedAddressData;
// Mapping from token ID to approved address.
mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
}
// =============================================================
// TOKEN COUNTING OPERATIONS
// =============================================================
/**
* @dev Returns the starting token ID.
* To change the starting token ID, please override this function.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev Returns the next token ID to be minted.
*/
function _nextTokenId() internal view virtual returns (uint256) {
return _currentIndex;
}
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() public view virtual override returns (uint256) {
// Counter underflow is impossible as _burnCounter cannot be incremented
// more than `_currentIndex - _startTokenId()` times.
unchecked {
return _currentIndex - _burnCounter - _startTokenId();
}
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view virtual returns (uint256) {
// Counter underflow is impossible as `_currentIndex` does not decrement,
// and it is initialized to `_startTokenId()`.
unchecked {
return _currentIndex - _startTokenId();
}
}
/**
* @dev Returns the total number of tokens burned.
*/
function _totalBurned() internal view virtual returns (uint256) {
return _burnCounter;
}
// =============================================================
// ADDRESS DATA OPERATIONS
// =============================================================
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
if (owner == address(0)) revert BalanceQueryForZeroAddress();
return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _numberMinted(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/
function _numberBurned(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getAux(address owner) internal view returns (uint64) {
return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
}
/**
* Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setAux(address owner, uint64 aux) internal virtual {
uint256 packed = _packedAddressData[owner];
uint256 auxCasted;
// Cast `aux` with assembly to avoid redundant masking.
assembly {
auxCasted := aux
}
packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
_packedAddressData[owner] = packed;
}
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// The interface IDs are constants representing the first 4 bytes
// of the XOR of all function selectors in the interface.
// See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
// (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
return
interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
}
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the token collection symbol.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
string memory baseURI = _baseURI();
return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, it can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return '';
}
// =============================================================
// OWNERSHIPS OPERATIONS
// =============================================================
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return address(uint160(_packedOwnershipOf(tokenId)));
}
/**
* @dev Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around over time.
*/
function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnershipOf(tokenId));
}
/**
* @dev Returns the unpacked `TokenOwnership` struct at `index`.
*/
function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnerships[index]);
}
/**
* @dev Initializes the ownership slot minted at `index` for efficiency purposes.
*/
function _initializeOwnershipAt(uint256 index) internal virtual {
if (_packedOwnerships[index] == 0) {
_packedOwnerships[index] = _packedOwnershipOf(index);
}
}
/**
* Returns the packed ownership data of `tokenId`.
*/
function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
uint256 curr = tokenId;
unchecked {
if (_startTokenId() <= curr)
if (curr < _currentIndex) {
uint256 packed = _packedOwnerships[curr];
// If not burned.
if (packed & _BITMASK_BURNED == 0) {
// Invariant:
// There will always be an initialized ownership slot
// (i.e. `ownership.addr != address(0) && ownership.burned == false`)
// before an unintialized ownership slot
// (i.e. `ownership.addr == address(0) && ownership.burned == false`)
// Hence, `curr` will not underflow.
//
// We can directly compare the packed value.
// If the address is zero, packed will be zero.
while (packed == 0) {
packed = _packedOwnerships[--curr];
}
return packed;
}
}
}
revert OwnerQueryForNonexistentToken();
}
/**
* @dev Returns the unpacked `TokenOwnership` struct from `packed`.
*/
function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
ownership.addr = address(uint160(packed));
ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
ownership.burned = packed & _BITMASK_BURNED != 0;
ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
}
/**
* @dev Packs ownership data into a single uint256.
*/
function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
}
}
/**
* @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
*/
function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
// For branchless setting of the `nextInitialized` flag.
assembly {
// `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
}
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) public payable virtual override {
address owner = ownerOf(tokenId);
if (_msgSenderERC721A() != owner)
if (!isApprovedForAll(owner, _msgSenderERC721A())) {
revert ApprovalCallerNotOwnerNorApproved();
}
_tokenApprovals[tokenId].value = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
return _tokenApprovals[tokenId].value;
}
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_operatorApprovals[_msgSenderERC721A()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
}
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted. See {_mint}.
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return
_startTokenId() <= tokenId &&
tokenId < _currentIndex && // If within bounds,
_packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
}
/**
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
) private pure returns (bool result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
msgSender := and(msgSender, _BITMASK_ADDRESS)
// `msgSender == owner || msgSender == approvedAddress`.
result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
}
}
/**
* @dev Returns the storage slot and value for the approved address of `tokenId`.
*/
function _getApprovedSlotAndAddress(uint256 tokenId)
private
view
returns (uint256 approvedAddressSlot, address approvedAddress)
{
TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
// The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
assembly {
approvedAddressSlot := tokenApproval.slot
approvedAddress := sload(approvedAddressSlot)
}
}
// =============================================================
// TRANSFER OPERATIONS
// =============================================================
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
if (to == address(0)) revert TransferToZeroAddress();
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// We can directly increment and decrement the balances.
--_packedAddressData[from]; // Updates: `balance -= 1`.
++_packedAddressData[to]; // Updates: `balance += 1`.
// Updates:
// - `address` to the next owner.
// - `startTimestamp` to the timestamp of transfering.
// - `burned` to `false`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
to,
_BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
emit Transfer(from, to, tokenId);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
safeTransferFrom(from, to, tokenId, '');
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public payable virtual override {
transferFrom(from, to, tokenId);
if (to.code.length != 0)
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
}
/**
* @dev Hook that is called before a set of serially-ordered token IDs
* are about to be transferred. This includes minting.
* And also called before burning one token.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Hook that is called after a set of serially-ordered token IDs
* have been transferred. This includes minting.
* And also called after one token has been burned.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `tokenId` - Token ID to be transferred.
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
bytes4 retval
) {
return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
// =============================================================
// MINT OPERATIONS
// =============================================================
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event for each mint.
*/
function _mint(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (quantity == 0) revert MintZeroQuantity();
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// `balance` and `numberMinted` have a maximum limit of 2**64.
// `tokenId` has a maximum limit of 2**256.
unchecked {
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
uint256 toMasked;
uint256 end = startTokenId + quantity;
// Use assembly to loop and emit the `Transfer` event for gas savings.
// The duplicated `log4` removes an extra check and reduces stack juggling.
// The assembly, together with the surrounding Solidity code, have been
// delicately arranged to nudge the compiler into producing optimized opcodes.
assembly {
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
toMasked := and(to, _BITMASK_ADDRESS)
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
startTokenId // `tokenId`.
)
// The `iszero(eq(,))` check ensures that large values of `quantity`
// that overflows uint256 will make the loop run out of gas.
// The compiler will optimize the `iszero` away for performance.
for {
let tokenId := add(startTokenId, 1)
} iszero(eq(tokenId, end)) {
tokenId := add(tokenId, 1)
} {
// Emit the `Transfer` event. Similar to above.
log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
}
}
if (toMasked == 0) revert MintToZeroAddress();
_currentIndex = end;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* This function is intended for efficient minting only during contract creation.
*
* It emits only one {ConsecutiveTransfer} as defined in
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
* instead of a sequence of {Transfer} event(s).
*
* Calling this function outside of contract creation WILL make your contract
* non-compliant with the ERC721 standard.
* For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
* {ConsecutiveTransfer} event is only permissible during contract creation.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {ConsecutiveTransfer} event.
*/
function _mintERC2309(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (to == address(0)) revert MintToZeroAddress();
if (quantity == 0) revert MintZeroQuantity();
if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are unrealistic due to the above check for `quantity` to be below the limit.
unchecked {
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
_currentIndex = startTokenId + quantity;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* See {_mint}.
*
* Emits a {Transfer} event for each mint.
*/
function _safeMint(
address to,
uint256 quantity,
bytes memory _data
) internal virtual {
_mint(to, quantity);
unchecked {
if (to.code.length != 0) {
uint256 end = _currentIndex;
uint256 index = end - quantity;
do {
if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
} while (index < end);
// Reentrancy protection.
if (_currentIndex != end) revert();
}
}
}
/**
* @dev Equivalent to `_safeMint(to, quantity, '')`.
*/
function _safeMint(address to, uint256 quantity) internal virtual {
_safeMint(to, quantity, '');
}
// =============================================================
// BURN OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/
function _burn(uint256 tokenId) internal virtual {
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
address from = address(uint160(prevOwnershipPacked));
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
if (approvalCheck) {
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
}
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// Updates:
// - `balance -= 1`.
// - `numberBurned += 1`.
//
// We can directly decrement the balance, and increment the number burned.
// This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
_packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
// Updates:
// - `address` to the last owner.
// - `startTimestamp` to the timestamp of burning.
// - `burned` to `true`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
from,
(_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
unchecked {
_burnCounter++;
}
}
// =============================================================
// EXTRA DATA OPERATIONS
// =============================================================
/**
* @dev Directly sets the extra data for the ownership data `index`.
*/
function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
uint256 packed = _packedOwnerships[index];
if (packed == 0) revert OwnershipNotInitializedForExtraData();
uint256 extraDataCasted;
// Cast `extraData` with assembly to avoid redundant masking.
assembly {
extraDataCasted := extraData
}
packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
_packedOwnerships[index] = packed;
}
/**
* @dev Called during each token transfer to set the 24bit `extraData` field.
* Intended to be overridden by the cosumer contract.
*
* `previousExtraData` - the value of `extraData` before transfer.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _extraData(
address from,
address to,
uint24 previousExtraData
) internal view virtual returns (uint24) {}
/**
* @dev Returns the next extra data for the packed ownership data.
* The returned result is shifted into position.
*/
function _nextExtraData(
address from,
address to,
uint256 prevOwnershipPacked
) private view returns (uint256) {
uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
}
// =============================================================
// OTHER OPERATIONS
// =============================================================
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/
function _msgSenderERC721A() internal view virtual returns (address) {
return msg.sender;
}
/**
* @dev Converts a uint256 to its ASCII string decimal representation.
*/
function _toString(uint256 value) internal pure virtual returns (string memory str) {
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
let m := add(mload(0x40), 0xa0)
// Update the free memory pointer to allocate.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
}
IERC721A.sol 282 lines
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721A {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
/**
* The `quantity` minted with ERC2309 exceeds the safety limit.
*/
error MintERC2309QuantityExceedsLimit();
/**
* The `extraData` cannot be set on an unintialized ownership slot.
*/
error OwnershipNotInitializedForExtraData();
// =============================================================
// STRUCTS
// =============================================================
struct TokenOwnership {
// The address of the owner.
address addr;
// Stores the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
// Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
uint24 extraData;
}
// =============================================================
// TOKEN COUNTERS
// =============================================================
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() external view returns (uint256);
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
// =============================================================
// IERC721
// =============================================================
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables
* (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`,
* checking first that contract recipients are aware of the ERC721 protocol
* to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move
* this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external payable;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
// =============================================================
// IERC2309
// =============================================================
/**
* @dev Emitted when tokens in `fromTokenId` to `toTokenId`
* (inclusive) is transferred from `from` to `to`, as defined in the
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
*
* See {_mintERC2309} for more details.
*/
event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}
BatchMintMetadata.sol 126 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Batch-mint Metadata
* @notice The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
* using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
* base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
*/
contract BatchMintMetadata {
/// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99}
uint256[] private batchIds;
/// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
mapping(uint256 => string) private baseURI;
/// @dev Mapping from id of a batch of tokens => to whether the base URI for the respective batch of tokens is frozen.
mapping(uint256 => bool) public batchFrozen;
/// @dev This event emits when the metadata of all tokens are frozen.
/// While not currently supported by marketplaces, this event allows
/// future indexing if desired.
event MetadataFrozen();
// @dev This event emits when the metadata of a range of tokens is updated.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFTs.
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
/**
* @notice Returns the count of batches of NFTs.
* @dev Each batch of tokens has an in ID and an associated `baseURI`.
* See {batchIds}.
*/
function getBaseURICount() public view returns (uint256) {
return batchIds.length;
}
/**
* @notice Returns the ID for the batch of tokens at the given index.
* @dev See {getBaseURICount}.
* @param _index Index of the desired batch in batchIds array.
*/
function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
if (_index >= getBaseURICount()) {
revert("Invalid index");
}
return batchIds[_index];
}
/// @dev Returns the id for the batch of tokens the given tokenId belongs to.
function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
if (_tokenId < indices[i]) {
index = i;
batchId = indices[i];
return (batchId, index);
}
}
revert("Invalid tokenId");
}
/// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
if (_tokenId < indices[i]) {
return baseURI[indices[i]];
}
}
revert("Invalid tokenId");
}
/// @dev returns the starting tokenId of a given batchId.
function _getBatchStartId(uint256 _batchID) internal view returns (uint256) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i = 0; i < numOfTokenBatches; i++) {
if (_batchID == indices[i]) {
if (i > 0) {
return indices[i - 1];
}
return 0;
}
}
revert("Invalid batchId");
}
/// @dev Sets the base URI for the batch of tokens with the given batchId.
function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
require(!batchFrozen[_batchId], "Batch frozen");
baseURI[_batchId] = _baseURI;
emit BatchMetadataUpdate(_getBatchStartId(_batchId), _batchId);
}
/// @dev Freezes the base URI for the batch of tokens with the given batchId.
function _freezeBaseURI(uint256 _batchId) internal {
string memory baseURIForBatch = baseURI[_batchId];
require(bytes(baseURIForBatch).length > 0, "Invalid batch");
batchFrozen[_batchId] = true;
emit MetadataFrozen();
}
/// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
function _batchMintMetadata(
uint256 _startId,
uint256 _amountToMint,
string memory _baseURIForTokens
) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
batchId = _startId + _amountToMint;
nextTokenIdToMint = batchId;
batchIds.push(batchId);
baseURI[batchId] = _baseURIForTokens;
}
}
ILazyMint.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILazyMint {
event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
}
IDelayedReveal.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IDelayedReveal {
event TokenURIRevealed(uint256 indexed index, string revealedURI);
function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI);
function encryptDecrypt(bytes memory data, bytes calldata key) external pure returns (bytes memory result);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
Strings.sol 94 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
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_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
ISignatureAction.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISignatureAction {
/**
* @notice The payload that must be signed by an authorized wallet.
*
* @param validityStartTimestamp The UNIX timestamp at and after which a signature is valid.
* @param validityEndTimestamp The UNIX timestamp at and after which a signature is invalid/expired.
* @param uid A unique non-repeatable ID for the payload.
* @param data Arbitrary bytes data to be used at the discretion of the contract.
*/
struct GenericRequest {
uint128 validityStartTimestamp;
uint128 validityEndTimestamp;
bytes32 uid;
bytes data;
}
/// @notice Emitted when a payload is verified and executed.
event RequestExecuted(address indexed user, address indexed signer, GenericRequest _req);
/**
* @notice Verfies that a payload is signed by an authorized wallet.
*
* @param req The payload signed by the authorized wallet.
* @param signature The signature produced by the authorized wallet signing the given payload.
*
* @return success Whether the payload is signed by the authorized wallet.
* @return signer The address of the signer.
*/
function verify(GenericRequest calldata req, bytes calldata signature)
external
view
returns (bool success, address signer);
}
Router.sol 46 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
import "../interfaces/IRouter.sol";
abstract contract Router is IRouter {
fallback() external payable virtual {
/// @dev delegate calls the appropriate implementation smart contract for a given function.
address implementation = getImplementationForFunction(msg.sig);
_delegate(implementation);
}
receive() external payable virtual {}
/// @dev delegateCalls an `implementation` smart contract.
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/// @dev Unimplemented. Returns the implementation contract address for a given function signature.
function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address);
}
Multicall.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "./Address.sol";
import {Context} from "./Context.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {_msgSender} are not propagated to subcalls.
*/
abstract contract Multicall is Context {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}
Math.sol 415 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
StorageSlot.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
ShortStrings.sol 123 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}
IERC2981.sol 23 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
AccessControl.sol 209 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../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:
*
* ```solidity
* 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}:
*
* ```solidity
* 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. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @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 returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @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 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.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual 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.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual 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 `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
ERC2981.sol 137 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/common/ERC2981.sol)
pragma solidity ^0.8.20;
import {IERC2981} from "../../interfaces/IERC2981.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
*
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
*
* Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
* fee is specified in basis points by default.
*
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
*/
abstract contract ERC2981 is IERC2981, ERC165 {
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo private _defaultRoyaltyInfo;
mapping(uint256 tokenId => RoyaltyInfo) private _tokenRoyaltyInfo;
/**
* @dev The default royalty set is invalid (eg. (numerator / denominator) >= 1).
*/
error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);
/**
* @dev The default royalty receiver is invalid.
*/
error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);
/**
* @dev The royalty set for an specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
*/
error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);
/**
* @dev The royalty receiver for `tokenId` is invalid.
*/
error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @inheritdoc IERC2981
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual returns (address, uint256) {
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
if (royalty.receiver == address(0)) {
royalty = _defaultRoyaltyInfo;
}
uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
return (royalty.receiver, royaltyAmount);
}
/**
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
* override.
*/
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/**
* @dev Sets the royalty information that all ids in this contract will default to.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
// Royalty fee will exceed the sale price
revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
}
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Removes default royalty information.
*/
function _deleteDefaultRoyalty() internal virtual {
delete _defaultRoyaltyInfo;
}
/**
* @dev Sets the royalty information for a specific token id, overriding the global default.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
uint256 denominator = _feeDenominator();
if (feeNumerator > denominator) {
// Royalty fee will exceed the sale price
revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
}
if (receiver == address(0)) {
revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
}
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Resets royalty information for the token id back to the global default.
*/
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
delete _tokenRoyaltyInfo[tokenId];
}
}
ERC721ABurnable.sol 26 lines
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import './IERC721ABurnable.sol';
import '../ERC721A.sol';
/**
* @title ERC721ABurnable.
*
* @dev ERC721A token that can be irreversibly burned (destroyed).
*/
abstract contract ERC721ABurnable is ERC721A, IERC721ABurnable {
/**
* @dev Burns `tokenId`. See {ERC721A-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) public virtual override {
_burn(tokenId, true);
}
}
IRouter.sol 11 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
interface IRouter {
fallback() external payable;
receive() external payable;
function getImplementationForFunction(bytes4 _functionSelector) external view returns (address);
}
BaseRouter.sol 154 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
// Interface
import "../interfaces/IBaseRouter.sol";
// Core
import "../core/Router.sol";
// Utils
import "./utils/StringSet.sol";
import "./utils/DefaultExtensionSet.sol";
import "./utils/ExtensionState.sol";
abstract contract BaseRouter is IBaseRouter, Router, ExtensionState {
using StringSet for StringSet.Set;
/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*/
/// @notice The DefaultExtensionSet that stores default extensions of the router.
address public immutable defaultExtensionSet;
/*///////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor(Extension[] memory _extensions) {
DefaultExtensionSet map = new DefaultExtensionSet();
defaultExtensionSet = address(map);
uint256 len = _extensions.length;
for (uint256 i = 0; i < len; i += 1) {
map.setExtension(_extensions[i]);
}
}
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @dev Adds a new extension to the router.
function addExtension(Extension memory _extension) external {
require(_canSetExtension(), "BaseRouter: caller not authorized.");
_addExtension(_extension);
}
/// @dev Updates an existing extension in the router, or overrides a default extension.
function updateExtension(Extension memory _extension) external {
require(_canSetExtension(), "BaseRouter: caller not authorized.");
_updateExtension(_extension);
}
/// @dev Removes an existing extension from the router.
function removeExtension(string memory _extensionName) external {
require(_canSetExtension(), "BaseRouter: caller not authorized.");
_removeExtension(_extensionName);
}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/**
* @notice Returns all extensions stored. Override default lugins stored in router are
* given precedence over default extensions in DefaultExtensionSet.
*/
function getAllExtensions() external view returns (Extension[] memory allExtensions) {
Extension[] memory defaultExtensions = IDefaultExtensionSet(defaultExtensionSet).getAllExtensions();
uint256 defaultExtensionsLen = defaultExtensions.length;
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
string[] memory names = data.extensionNames.values();
uint256 namesLen = names.length;
uint256 overrides = 0;
for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
if (data.extensionNames.contains(defaultExtensions[i].metadata.name)) {
overrides += 1;
}
}
uint256 total = (namesLen + defaultExtensionsLen) - overrides;
allExtensions = new Extension[](total);
uint256 idx = 0;
for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
string memory name = defaultExtensions[i].metadata.name;
if (!data.extensionNames.contains(name)) {
allExtensions[idx] = defaultExtensions[i];
idx += 1;
}
}
for (uint256 i = 0; i < namesLen; i += 1) {
allExtensions[idx] = data.extensions[names[i]];
idx += 1;
}
}
/// @dev Returns the extension metadata and functions for a given extension.
function getExtension(string memory _extensionName) public view returns (Extension memory) {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
bool isLocalExtension = data.extensionNames.contains(_extensionName);
return isLocalExtension ? data.extensions[_extensionName] : IDefaultExtensionSet(defaultExtensionSet).getExtension(_extensionName);
}
/// @dev Returns the extension's implementation smart contract address.
function getExtensionImplementation(string memory _extensionName) external view returns (address) {
return getExtension(_extensionName).metadata.implementation;
}
/// @dev Returns all functions that belong to the given extension contract.
function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
return getExtension(_extensionName).functions;
}
/// @dev Returns the extension metadata for a given function.
function getExtensionForFunction(bytes4 _functionSelector) public view returns (ExtensionMetadata memory) {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
bool isLocalExtension = metadata.implementation != address(0);
return isLocalExtension ? metadata : IDefaultExtensionSet(defaultExtensionSet).getExtensionForFunction(_functionSelector);
}
/// @dev Returns the extension implementation address stored in router, for the given function.
function getImplementationForFunction(bytes4 _functionSelector)
public
view
override
returns (address extensionAddress)
{
return getExtensionForFunction(_functionSelector).implementation;
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @dev Returns whether a extension can be set in the given execution context.
function _canSetExtension() internal view virtual returns (bool);
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @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.
*/
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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
ERC2771Context.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Context variant with ERC2771 support.
*
* WARNING: Avoid using this pattern in contracts that rely in a specific calldata length as they'll
* be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC2771
* specification adding the address size in bytes (20) to the calldata size. An example of an unexpected
* behavior could be an unintended fallback (or another function) invocation while trying to invoke the `receive`
* function only accessible if `msg.data.length == 0`.
*
* WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
* Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
* recovery.
*/
abstract contract ERC2771Context is Context {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _trustedForwarder;
/**
* @dev Initializes the contract with a trusted forwarder, which will be able to
* invoke functions on this contract on behalf of other accounts.
*
* NOTE: The trusted forwarder can be replaced by overriding {trustedForwarder}.
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address trustedForwarder_) {
_trustedForwarder = trustedForwarder_;
}
/**
* @dev Returns the address of the trusted forwarder.
*/
function trustedForwarder() public view virtual returns (address) {
return _trustedForwarder;
}
/**
* @dev Indicates whether any particular address is the trusted forwarder.
*/
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == trustedForwarder();
}
/**
* @dev Override for `msg.sender`. Defaults to the original `msg.sender` whenever
* a call is not performed by the trusted forwarder or the calldata length is less than
* 20 bytes (an address length).
*/
function _msgSender() internal view virtual override returns (address) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
} else {
return super._msgSender();
}
}
/**
* @dev Override for `msg.data`. Defaults to the original `msg.data` whenever
* a call is not performed by the trusted forwarder or the calldata length is less than
* 20 bytes (an address length).
*/
function _msgData() internal view virtual override returns (bytes calldata) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return msg.data[:calldataLength - contextSuffixLength];
} else {
return super._msgData();
}
}
/**
* @dev ERC-2771 specifies the context as being a single address (20 bytes).
*/
function _contextSuffixLength() internal view virtual override returns (uint256) {
return 20;
}
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
IERC721ABurnable.sol 21 lines
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import '../IERC721A.sol';
/**
* @dev Interface of ERC721ABurnable.
*/
interface IERC721ABurnable is IERC721A {
/**
* @dev Burns `tokenId`. See {ERC721A-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) external;
}
IExtension.sol 63 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
interface IExtension {
/*///////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
/**
* @notice A extension's metadata.
*
* @param name The unique name of the extension.
* @param metadataURI The URI where the metadata for the extension lives.
* @param implementation The implementation smart contract address of the extension.
*/
struct ExtensionMetadata {
string name;
string metadataURI;
address implementation;
}
/**
* @notice An interface to describe a extension's function.
*
* @param functionSelector The 4 byte selector of the function.
* @param functionSignature Function signature as a string. E.g. "transfer(address,address,uint256)"
*/
struct ExtensionFunction {
bytes4 functionSelector;
string functionSignature;
}
/**
* @notice An interface to describe an extension.
*
* @param metadata The extension's metadata; it's name, metadata URI and implementation contract address.
* @param functions The functions that belong to the extension.
*/
struct Extension {
ExtensionMetadata metadata;
ExtensionFunction[] functions;
}
/*///////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @dev Emitted when a extension is added; emitted for each function of the extension.
event ExtensionAdded(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
/// @dev Emitted when extension is updated; emitted for each function of the extension.
event ExtensionUpdated(
address indexed oldExtensionAddress,
address indexed newExtensionAddress,
bytes4 indexed functionSelector,
string functionSignature
);
/// @dev Emitted when a extension is removed; emitted for each function of the extension.
event ExtensionRemoved(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
}
ECDSA.sol 174 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
IBaseRouter.sol 21 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
import "./IDefaultExtensionSet.sol";
interface IBaseRouter is IDefaultExtensionSet {
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @dev Adds a new extension to the router.
function addExtension(Extension memory extension) external;
/// @dev Updates an existing extension in the router, or overrides a default extension.
function updateExtension(Extension memory extension) external;
/// @dev Removes an existing extension from the router.
function removeExtension(string memory extensionName) external;
}
EIP712.sol 160 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}
StringSet.sol 172 lines
// SPDX-License-Identifier: Apache 2.0
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
library StringSet {
struct Set {
// Storage of set values
string[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(string => 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, string memory 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, string memory 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) {
string memory 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, string memory 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 (string memory) {
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 (string[] memory) {
return set._values;
}
/**
* @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, string memory value) internal returns (bool) {
return _add(set, 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(Set storage set, string memory value) internal returns (bool) {
return _remove(set, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Set storage set, string memory value) internal view returns (bool) {
return _contains(set, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Set storage set) internal view returns (uint256) {
return _length(set);
}
/**
* @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) internal view returns (string memory) {
return _at(set, 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) internal view returns (string[] memory) {
return _values(set);
}
}
ERC165.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @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);
}
ExtensionState.sol 134 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
// Interface
import "../../interfaces/IExtension.sol";
// Extensions
import "./StringSet.sol";
library ExtensionStateStorage {
bytes32 public constant EXTENSION_STATE_STORAGE_POSITION = keccak256("extension.state.storage");
struct Data {
/// @dev Set of names of all extensions stored.
StringSet.Set extensionNames;
/// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions.
mapping(string => IExtension.Extension) extensions;
/// @dev Mapping from function selector => extension metadata of the extension the function belongs to.
mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata;
}
function extensionStateStorage() internal pure returns (Data storage extensionStateData) {
bytes32 position = EXTENSION_STATE_STORAGE_POSITION;
assembly {
extensionStateData.slot := position
}
}
}
contract ExtensionState is IExtension {
using StringSet for StringSet.Set;
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @dev Stores a new extension in the contract.
function _addExtension(Extension memory _extension) internal {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
string memory name = _extension.metadata.name;
require(data.extensionNames.add(name), "ExtensionState: extension already exists.");
data.extensions[name].metadata = _extension.metadata;
require(_extension.metadata.implementation != address(0), "ExtensionState: adding extension without implementation.");
uint256 len = _extension.functions.length;
for (uint256 i = 0; i < len; i += 1) {
require(
_extension.functions[i].functionSelector ==
bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
"ExtensionState: fn selector and signature mismatch."
);
require(
data.extensionMetadata[_extension.functions[i].functionSelector].implementation == address(0),
"ExtensionState: extension already exists for function."
);
data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
data.extensions[name].functions.push(_extension.functions[i]);
emit ExtensionAdded(
_extension.metadata.implementation,
_extension.functions[i].functionSelector,
_extension.functions[i].functionSignature
);
}
}
/// @dev Updates / overrides an existing extension in the contract.
function _updateExtension(Extension memory _extension) internal {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
string memory name = _extension.metadata.name;
require(data.extensionNames.contains(name), "ExtensionState: extension does not exist.");
address oldImplementation = data.extensions[name].metadata.implementation;
require(_extension.metadata.implementation != oldImplementation, "ExtensionState: re-adding same extension.");
data.extensions[name].metadata = _extension.metadata;
ExtensionFunction[] memory oldFunctions = data.extensions[name].functions;
uint256 oldFunctionsLen = oldFunctions.length;
delete data.extensions[name].functions;
for (uint256 i = 0; i < oldFunctionsLen; i += 1) {
delete data.extensionMetadata[oldFunctions[i].functionSelector];
}
uint256 len = _extension.functions.length;
for (uint256 i = 0; i < len; i += 1) {
require(
_extension.functions[i].functionSelector ==
bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
"ExtensionState: fn selector and signature mismatch."
);
data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
data.extensions[name].functions.push(_extension.functions[i]);
emit ExtensionUpdated(
oldImplementation,
_extension.metadata.implementation,
_extension.functions[i].functionSelector,
_extension.functions[i].functionSignature
);
}
}
/// @dev Removes an existing extension from the contract.
function _removeExtension(string memory _extensionName) internal {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
require(data.extensionNames.remove(_extensionName), "ExtensionState: extension does not exist.");
address implementation = data.extensions[_extensionName].metadata.implementation;
ExtensionFunction[] memory extensionFunctions = data.extensions[_extensionName].functions;
delete data.extensions[_extensionName];
uint256 len = extensionFunctions.length;
for (uint256 i = 0; i < len; i += 1) {
emit ExtensionRemoved(
implementation,
extensionFunctions[i].functionSelector,
extensionFunctions[i].functionSignature
);
delete data.extensionMetadata[extensionFunctions[i].functionSelector];
}
}
}
IDefaultExtensionSet.sol 27 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
import "./IExtension.sol";
interface IDefaultExtensionSet is IExtension {
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @dev Returns all extensions stored.
function getAllExtensions() external view returns (Extension[] memory);
/// @dev Returns all functions that belong to the given extension contract.
function getAllFunctionsOfExtension(string memory extensionName) external view returns (ExtensionFunction[] memory);
/// @dev Returns the extension metadata for a given function.
function getExtensionForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory);
/// @dev Returns the extension's implementation smart contract address.
function getExtensionImplementation(string memory extensionName) external view returns (address);
/// @dev Returns the extension metadata and functions for a given extension.
function getExtension(string memory extensionName) external view returns (Extension memory);
}
MessageHashUtils.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
DefaultExtensionSet.sol 82 lines
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
pragma solidity ^0.8.0;
// Interface
import "../../interfaces/IDefaultExtensionSet.sol";
// Extensions
import "./ExtensionState.sol";
contract DefaultExtensionSet is IDefaultExtensionSet, ExtensionState {
using StringSet for StringSet.Set;
/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*/
/// @notice The deployer of DefaultExtensionSet.
address private deployer;
/*///////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor() {
deployer = msg.sender;
}
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Stores a extension in the DefaultExtensionSet.
function setExtension(Extension memory _extension) external {
require(msg.sender == deployer, "DefaultExtensionSet: unauthorized caller.");
_addExtension(_extension);
}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns all extensions stored.
function getAllExtensions() external view returns (Extension[] memory allExtensions) {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
string[] memory names = data.extensionNames.values();
uint256 len = names.length;
allExtensions = new Extension[](len);
for (uint256 i = 0; i < len; i += 1) {
allExtensions[i] = data.extensions[names[i]];
}
}
/// @notice Returns the extension metadata and functions for a given extension.
function getExtension(string memory _extensionName) public view returns (Extension memory) {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
require(data.extensionNames.contains(_extensionName), "DefaultExtensionSet: extension does not exist.");
return data.extensions[_extensionName];
}
/// @notice Returns the extension's implementation smart contract address.
function getExtensionImplementation(string memory _extensionName) external view returns (address) {
return getExtension(_extensionName).metadata.implementation;
}
/// @notice Returns all functions that belong to the given extension contract.
function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
return getExtension(_extensionName).functions;
}
/// @notice Returns the extension metadata for a given function.
function getExtensionForFunction(bytes4 _functionSelector) external view returns (ExtensionMetadata memory) {
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
require(metadata.implementation != address(0), "DefaultExtensionSet: no extension for function.");
return metadata;
}
}
Read Contract
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
balanceOf 0x70a08231 → uint256
batchFrozen 0x83040532 → bool
contractURI 0xe8a3d485 → string
defaultExtensionSet 0x1ee8b41b → address
eip712Domain 0x84b0196e → bytes1, string, string, uint256, address, bytes32, uint256[]
encryptDecrypt 0xe7150322 → bytes
encryptedData 0xa05112fc → bytes
getAllExtensions 0x4a00cc48 → tuple[]
getAllFunctionsOfExtension 0x012b8729 → tuple[]
getApproved 0x081812fc → address
getBaseURICount 0x63b45e2d → uint256
getBatchIdAtIndex 0x2419f51b → uint256
getExtension 0xc22707ee → tuple
getExtensionForFunction 0x212f6912 → tuple
getExtensionImplementation 0x7c3b1137 → address
getImplementationForFunction 0xce0b6013 → address
getRevealURI 0x9fc4d68f → string
getRoleAdmin 0x248a9ca3 → bytes32
hasRole 0x91d14854 → bool
isApprovedForAll 0xe985e9c5 → bool
isEncryptedBatch 0x492e224b → bool
isTrustedForwarder 0x572b6c05 → bool
name 0x06fdde03 → string
ownerOf 0x6352211e → address
royaltyInfo 0x2a55205a → address, uint256
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
tokenURI 0xc87b56dd → string
totalSupply 0x18160ddd → uint256
trustedForwarder 0x7da0a877 → address
verify 0x90267fbf → bool, address
Write Contract 21 functions
These functions modify contract state and require a wallet transaction to execute.
addExtension 0xfa0d3bb8
tuple _extension
approve 0x095ea7b3
address to
uint256 tokenId
burn 0x42966c68
uint256 tokenId
claimWithSignature 0x51511639
tuple _req
bytes _signature
returns: address
grantRole 0x2f2ff15d
bytes32 role
address account
lazyMint 0xd37c353b
uint256 _amount
string _baseURIForTokens
bytes _data
returns: uint256
mint 0x40c10f19
address to
uint256 quantity
multicall 0xac9650d8
bytes[] data
returns: bytes[]
removeExtension 0xee7d2adf
string _extensionName
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
reveal 0xce805642
uint256 _index
bytes _key
returns: string
revokeRole 0xd547741f
bytes32 role
address account
safeTransferFrom 0x42842e0e
address from
address to
uint256 tokenId
safeTransferFrom 0xb88d4fde
address from
address to
uint256 tokenId
bytes _data
setApprovalForAll 0xa22cb465
address operator
bool approved
setBaseURI 0x33cfcb9f
uint256 _batchId
string _baseURI
setContractURI 0x938e3d7b
string _uri
setDefaultRoyalty 0x04634d8d
address receiver
uint96 feeNumerator
setTokenRoyalty 0x5944c753
uint256 tokenId
address receiver
uint96 feeNumerator
transferFrom 0x23b872dd
address from
address to
uint256 tokenId
updateExtension 0x7ac9ab4a
tuple _extension
Recent Transactions
No transactions found for this address