Address Contract Verified
Address
0xe16D4E7002A6DE6D489e8EAD01bDF1eb0ADA29db
Balance
0 ETH
Nonce
1
Code Size
9078 bytes
Creator
0x343978CF...5A16 at tx 0x6d3d42fd...d997ea
Indexed Transactions
0
Contract Bytecode
9078 bytes
0x6080604052600436106101ed5760003560e01c806357f7789e1161010d578063a0bcfc7f116100a0578063f04e283e1161006f578063f04e283e146105a2578063f242432a146105b5578063f2fde38b146105d5578063f5298aca146105e8578063fee81cf41461060857600080fd5b8063a0bcfc7f1461052d578063a22cb4651461054d578063d53913931461056d578063e985e9c51461058257600080fd5b8063731133e9116100dc578063731133e9146104a75780638da5cb5b146104c757806395d89b41146104e05780639d28fb861461050d57600080fd5b806357f7789e1461042757806358c2225b146104475780636b20c4541461047f578063715018a61461049f57600080fd5b806325692962116101855780634a4ee7b1116101545780634a4ee7b1146103a85780634e1273f4146103bb578063514e62fc146103e857806354d1f13d1461041f57600080fd5b80632569296214610338578063282c51f3146103405780632de94807146103555780632eb2c2d61461038857600080fd5b8063183a4f6e116101c1578063183a4f6e146102b95780631c10893f146102ce5780631cd64df4146102e15780631f7fdffa1461031857600080fd5b8062fdd58e146101f257806301ffc9a71461022557806306fdde03146102555780630e89341c14610299575b600080fd5b3480156101fe57600080fd5b5061021261020d3660046119c1565b61063b565b6040519081526020015b60405180910390f35b34801561023157600080fd5b50610245610240366004611a01565b61066c565b604051901515815260200161021c565b34801561026157600080fd5b506040805180820190915260128152714d657461526562656c7a205265776172647360701b60208201525b60405161021c9190611a6e565b3480156102a557600080fd5b5061028c6102b4366004611a81565b6106be565b6102cc6102c7366004611a81565b61079f565b005b6102cc6102dc3660046119c1565b6107ac565b3480156102ed57600080fd5b506102456102fc3660046119c1565b638b78c6d8600c90815260009290925260209091205481161490565b34801561032457600080fd5b506102cc610333366004611ae5565b6107c2565b6102cc61081b565b34801561034c57600080fd5b50610212600281565b34801561036157600080fd5b50610212610370366004611bbc565b638b78c6d8600c908152600091909152602090205490565b34801561039457600080fd5b506102cc6103a3366004611c79565b61086a565b6102cc6103b63660046119c1565b610882565b3480156103c757600080fd5b506103db6103d6366004611d2d565b610894565b60405161021c9190611d98565b3480156103f457600080fd5b506102456104033660046119c1565b638b78c6d8600c90815260009290925260209091205416151590565b6102cc610975565b34801561043357600080fd5b506102cc610442366004611ddc565b6109b1565b34801561045357600080fd5b50600154610467906001600160a01b031681565b6040516001600160a01b03909116815260200161021c565b34801561048b57600080fd5b506102cc61049a366004611e22565b6109d6565b6102cc6109ff565b3480156104b357600080fd5b506102cc6104c2366004611ea2565b610a13565b3480156104d357600080fd5b50638b78c6d81954610467565b3480156104ec57600080fd5b50604080518082019091526004815263292ba22d60e11b602082015261028c565b34801561051957600080fd5b506102cc610528366004611bbc565b610a31565b34801561053957600080fd5b506102cc610548366004611f02565b610a5b565b34801561055957600080fd5b506102cc610568366004611f4c565b610a6f565b34801561057957600080fd5b50610212600181565b34801561058e57600080fd5b5061024561059d366004611f83565b610a89565b6102cc6105b0366004611bbc565b610ab0565b3480156105c157600080fd5b506102cc6105d0366004611fb6565b610aed565b6102cc6105e3366004611bbc565b610afa565b3480156105f457600080fd5b506102cc610603366004612027565b610b21565b34801561061457600080fd5b50610212610623366004611bbc565b63389a75e1600c908152600091909152602090205490565b60006001600160a01b03831661065b5761065b6323d3ad8160e21b610b46565b6106658383610b50565b9392505050565b60006301ffc9a760e01b6001600160e01b03198316148061069d5750636cdb3d1360e11b6001600160e01b03198316145b806106b857506303a24d0760e21b6001600160e01b03198316145b92915050565b6000818152600260205260408120805460609291906106dc9061205a565b80601f01602080910402602001604051908101604052809291908181526020018280546107089061205a565b80156107555780601f1061072a57610100808354040283529160200191610755565b820191906000526020600020905b81548152906001019060200180831161073857829003601f168201915b5050505050905060008151111561076c5792915050565b600061077784610b89565b604051602001610788929190612094565b604051602081830303815290604052915050919050565b6107a93382610bcd565b50565b6107b4610bd9565b6107be8282610bf4565b5050565b60016107cd81610c00565b610811888888888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610c2692505050565b5050505050505050565b60006202a3006001600160401b03164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b61087987878787878787610e3e565b50505050505050565b61088a610bd9565b6107be8282610bcd565b60608382146108ad576108ad63512509d360e11b610b46565b6000846001600160401b038111156108c7576108c7611bd7565b6040519080825280602002602001820160405280156108f0578160200160208202803683370190505b50905060005b8581101561096b576109468787838181106109135761091361211b565b90506020020160208101906109289190611bbc565b86868481811061093a5761093a61211b565b9050602002013561063b565b8282815181106109585761095861211b565b60209081029190910101526001016108f6565b5095945050505050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6109b9610bd9565b60008281526002602052604090206109d18282612179565b505050565b60026109e181610c00565b6109ea86611049565b6109f78686868686611067565b505050505050565b610a07610bd9565b610a1160006111e8565b565b6001610a1e81610c00565b610a2a85858585611226565b5050505050565b610a39610bd9565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b610a63610bd9565b60006107be8282612179565b8015610a7f57610a7f823361137a565b6107be828261142d565b6001600160a01b0391821660009081529116600d60fc1b1760205260408120908190525490565b610ab8610bd9565b63389a75e1600c52806000526020600c208054421115610ae057636f5e88186000526004601cfd5b600090556107a9816111e8565b610a2a8585858585611485565b610b02610bd9565b8060601b610b1857637448fbae6000526004601cfd5b6107a9816111e8565b6002610b2c81610c00565b610b3584611049565b610b408484846115eb565b50505050565b8060005260046000fd5b60008160031c8360601b60041c17600760fd1b1760005261ffff6007831660051b1b602060002054166007831660051b1c905092915050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480610ba4575050819003601f19909101908152919050565b6107be828260006116c9565b638b78c6d819543314610a11576382b429006000526004601cfd5b6107be828260016116c9565b638b78c6d8600c5233600052806020600c2054166107a9576382b429006000526004601cfd5b6001600160a01b038616610c4357610c43622e076360e81b610b46565b838214610c5a57610c5a63512509d360e11b610b46565b33610c6c816000898989898989611722565b60008060005b87811015610d9257888882818110610c8c57610c8c61211b565b905060200201359250868682818110610ca757610ca761211b565b90506020020135915060016001605f1b03831115610ccf57610ccf63467777f160e11b610b46565b81600003610ce757610ce763b562e8dd60e01b610b46565b6000610cf38b85610b50565b905082810161ffff811115610d1257610d12630b6cdf5d60e41b610b46565b81811015610d2a57610d2a630b6cdf5d60e41b610b46565b610d358c868361172c565b6000610d418d8761176d565b905084810161ffff811115610d6057610d60630b6cdf5d60e41b610b46565b81811015610d7857610d78630b6cdf5d60e41b610b46565b610d838e88836117ab565b84600101945050505050610c72565b5060405160408152876020026060016020820152876040820152876020028960608301378588602002606083010152856020028789602002608084010137896000857f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8960400260800185a4506001600160a01b0389163b15610e3357610e1f60008a8a8a8a8a8a6117f1565b610e3357610e33639c05499b60e01b610b46565b505050505050505050565b6001600160a01b038616610e5c57610e5c633a954ecd60e21b610b46565b838214610e7357610e7363512509d360e11b610b46565b6001600160a01b0387163314610ea157610e8d8733610a89565b610ea157610ea1632ce44b5f60e11b610b46565b33610eb28189898989898989611722565b60005b85811015610faa576000878783818110610ed157610ed161211b565b9050602002013590506000868684818110610eee57610eee61211b565b90506020020135905060016001605f1b03821115610f1657610f1663467777f160e11b610b46565b6000610f228c84610b50565b905080821115610f3c57610f3c63169b037b60e01b610b46565b8a6001600160a01b03168c6001600160a01b031614610f9c576000610f618c85610b50565b91839003918301905061ffff811115610f8457610f84630b6cdf5d60e41b610b46565b610f8f8d858461172c565b610f9a8c858361172c565b505b836001019350505050610eb5565b50604051604081528560200260600160208201528560408201528560200287606083013783866020026060830101528360200285876020026080840101378789837f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8760400260800185a4506001600160a01b0387163b1561081157611035888888888888886117f1565b61081157610811639c05499b60e01b610b46565b6110538133610a89565b6107a9576107a9632ce44b5f60e11b610b46565b6001600160a01b0385166110855761108563b817eee760e01b610b46565b82811461109c5761109c63512509d360e11b610b46565b60003390506110c1818760008888888860405180602001604052806000815250611722565b60005b848110156111675760008686838181106110e0576110e061211b565b90506020020135905060008585848181106110fd576110fd61211b565b90506020020135905060016001605f1b038211156111255761112563467777f160e11b610b46565b60006111318a84610b50565b90508082111561114b5761114b63588569f760e01b610b46565b8190036111598a848361172c565b8360010193505050506110c4565b5060405160408152846020026060016020820152846040820152846020028660608301378285602002606083010152826020028486602002608084010137600087837f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8660400260800185a4506040805160208101909152600090526109f7565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b60016001605f1b038311156112455761124563467777f160e11b610b46565b6001600160a01b03841661126257611262622e076360e81b610b46565b8160000361127a5761127a63b562e8dd60e01b610b46565b3361128a816000878787876118dd565b60006112968686610b50565b905083810161ffff8111156112b5576112b5630b6cdf5d60e41b610b46565b818110156112cd576112cd630b6cdf5d60e41b610b46565b6112d887878361172c565b60006112e4888861176d565b905085810161ffff81111561130357611303630b6cdf5d60e41b610b46565b8181101561131b5761131b630b6cdf5d60e41b610b46565b6113268989836117ab565b604051888152876020820152896000877fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62604085a4506001600160a01b0389163b15610e3357610e1f60008a8a8a8a6118e7565b6001600160a01b038216156107be576001546001600160a01b0316156107be57600154604051636a27ec7760e11b81526001600160a01b03848116600483015283811660248301529091169063d44fd8ee90604401602060405180830381865afa1580156113ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114109190612238565b6107be57604051638a10919360e01b815260040160405180910390fd5b336000528160601b60601c600d60fc1b17602052604060002060005280602052602051600051558160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31602080a35050565b60016001605f1b038311156114a4576114a463467777f160e11b610b46565b6001600160a01b0384166114c2576114c2633a954ecd60e21b610b46565b6001600160a01b03851633146114f0576114dc8533610a89565b6114f0576114f0632ce44b5f60e11b610b46565b336114ff8187878787876118dd565b600061150b8786610b50565b9050808411156115255761152563169b037b60e01b610b46565b856001600160a01b0316876001600160a01b03161461158557600061154a8787610b50565b91859003918501905061ffff81111561156d5761156d630b6cdf5d60e41b610b46565b61157888878461172c565b61158387878361172c565b505b6040518581528460208201528688847fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62604085a4506001600160a01b0386163b15610879576115d787878787876118e7565b61087957610879639c05499b60e01b610b46565b60016001605f1b0382111561160a5761160a63467777f160e11b610b46565b6001600160a01b0383166116285761162863b817eee760e01b610b46565b600033905061164b818560008686604051806020016040528060008152506118dd565b60006116578585610b50565b9050808311156116715761167163588569f760e01b610b46565b82900361167f85858361172c565b604051848152836020820152600086847fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62604085a450604080516020810190915260009052610a2a565b638b78c6d8600c52826000526020600c208054838117836116eb575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b610811888861137a565b8160031c8360601b60041c17600760fd1b176000526020600020600052806007831660051b1b6000515461ffff6007851660051b1b19161760005155505050565b60008160031c8360601b60041c17600760fd1b1760005263ffff00006007831660051b1b602060002054166007831660051b1c60101c905092915050565b8160031c8360601b60041c17600760fd1b1760005260206000206000528060101b6007831660051b1b6000515463ffff00006007851660051b1b19161760005155505050565b60405163bc197c8160e01b81526000906001600160a01b0388169063bc197c819061182c9033908c908b908b908b908b908b90600401612287565b6020604051808303816000875af1925050508015611867575060408051601f3d908101601f19168201909252611864918101906122e9565b60015b6118bc573d808015611895576040519150601f19603f3d011682016040523d82523d6000602084013e61189a565b606091505b5080516000036118b4576118b4639c05499b60e01b610b46565b805181602001fd5b6001600160e01b03191663bc197c8160e01b1490505b979650505050505050565b6109f7868661137a565b60405163f23a6e6160e01b81526000906001600160a01b0386169063f23a6e619061191e9033908a90899089908990600401612306565b6020604051808303816000875af1925050508015611959575060408051601f3d908101601f19168201909252611956918101906122e9565b60015b611987573d808015611895576040519150601f19603f3d011682016040523d82523d6000602084013e61189a565b6001600160e01b03191663f23a6e6160e01b14905095945050505050565b80356001600160a01b03811681146119bc57600080fd5b919050565b600080604083850312156119d457600080fd5b6119dd836119a5565b946020939093013593505050565b6001600160e01b0319811681146107a957600080fd5b600060208284031215611a1357600080fd5b8135610665816119eb565b60005b83811015611a39578181015183820152602001611a21565b50506000910152565b60008151808452611a5a816020860160208601611a1e565b601f01601f19169290920160200192915050565b6020815260006106656020830184611a42565b600060208284031215611a9357600080fd5b5035919050565b60008083601f840112611aac57600080fd5b5081356001600160401b03811115611ac357600080fd5b6020830191508360208260051b8501011115611ade57600080fd5b9250929050565b60008060008060008060006080888a031215611b0057600080fd5b611b09886119a5565b965060208801356001600160401b0380821115611b2557600080fd5b611b318b838c01611a9a565b909850965060408a0135915080821115611b4a57600080fd5b611b568b838c01611a9a565b909650945060608a0135915080821115611b6f57600080fd5b818a0191508a601f830112611b8357600080fd5b813581811115611b9257600080fd5b8b6020828501011115611ba457600080fd5b60208301945080935050505092959891949750929550565b600060208284031215611bce57600080fd5b610665826119a5565b634e487b7160e01b600052604160045260246000fd5b600082601f830112611bfe57600080fd5b81356001600160401b0380821115611c1857611c18611bd7565b604051601f8301601f19908116603f01168101908282118183101715611c4057611c40611bd7565b81604052838152866020858801011115611c5957600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600080600060a0888a031215611c9457600080fd5b611c9d886119a5565b9650611cab602089016119a5565b955060408801356001600160401b0380821115611cc757600080fd5b611cd38b838c01611a9a565b909750955060608a0135915080821115611cec57600080fd5b611cf88b838c01611a9a565b909550935060808a0135915080821115611d1157600080fd5b50611d1e8a828b01611bed565b91505092959891949750929550565b60008060008060408587031215611d4357600080fd5b84356001600160401b0380821115611d5a57600080fd5b611d6688838901611a9a565b90965094506020870135915080821115611d7f57600080fd5b50611d8c87828801611a9a565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015611dd057835183529284019291840191600101611db4565b50909695505050505050565b60008060408385031215611def57600080fd5b8235915060208301356001600160401b03811115611e0c57600080fd5b611e1885828601611bed565b9150509250929050565b600080600080600060608688031215611e3a57600080fd5b611e43866119a5565b945060208601356001600160401b0380821115611e5f57600080fd5b611e6b89838a01611a9a565b90965094506040880135915080821115611e8457600080fd5b50611e9188828901611a9a565b969995985093965092949392505050565b60008060008060808587031215611eb857600080fd5b611ec1856119a5565b9350602085013592506040850135915060608501356001600160401b03811115611eea57600080fd5b611ef687828801611bed565b91505092959194509250565b600060208284031215611f1457600080fd5b81356001600160401b03811115611f2a57600080fd5b611f3684828501611bed565b949350505050565b80151581146107a957600080fd5b60008060408385031215611f5f57600080fd5b611f68836119a5565b91506020830135611f7881611f3e565b809150509250929050565b60008060408385031215611f9657600080fd5b611f9f836119a5565b9150611fad602084016119a5565b90509250929050565b600080600080600060a08688031215611fce57600080fd5b611fd7866119a5565b9450611fe5602087016119a5565b9350604086013592506060860135915060808601356001600160401b0381111561200e57600080fd5b61201a88828901611bed565b9150509295509295909350565b60008060006060848603121561203c57600080fd5b612045846119a5565b95602085013595506040909401359392505050565b600181811c9082168061206e57607f821691505b60208210810361208e57634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546120a28161205a565b600182811680156120ba57600181146120cf576120fe565b60ff19841687528215158302870194506120fe565b8860005260208060002060005b858110156120f55781548a8201529084019082016120dc565b50505082870194505b505050508351612112818360208801611a1e565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b601f8211156109d1576000816000526020600020601f850160051c8101602086101561215a5750805b601f850160051c820191505b818110156109f757828155600101612166565b81516001600160401b0381111561219257612192611bd7565b6121a6816121a0845461205a565b84612131565b602080601f8311600181146121db57600084156121c35750858301515b600019600386901b1c1916600185901b1785556109f7565b600085815260208120601f198616915b8281101561220a578886015182559484019460019091019084016121eb565b50858210156122285787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561224a57600080fd5b815161066581611f3e565b81835260006001600160fb1b0383111561226e57600080fd5b8260051b80836020870137939093016020019392505050565b6001600160a01b0388811682528716602082015260a0604082018190526000906122b49083018789612255565b82810360608401526122c7818688612255565b905082810360808401526122db8185611a42565b9a9950505050505050505050565b6000602082840312156122fb57600080fd5b8151610665816119eb565b6001600160a01b03868116825285166020820152604081018490526060810183905260a0608082018190526000906118d290830184611a4256fea26469706673582212207531154d029cba7256e6d1dfce43308939ddb6a4548a9d5ddae3e80048861f8664736f6c63430008170033
Verified Source Code Full Match
Compiler: v0.8.23+commit.f704f362
EVM: paris
Optimization: Yes (200 runs)
MetaRebelzRewards.sol 100 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {ERC1155P} from "erc1155p/ERC1155P.sol";
import {LibString} from "solady/utils/LibString.sol";
import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {IOperatorRegistry} from "./interfaces/IOperatorRegistry.sol";
contract MetaRebelzRewards is ERC1155P, OwnableRoles {
using LibString for uint256;
uint256 public constant MINTER_ROLE = _ROLE_0;
uint256 public constant BURNER_ROLE = _ROLE_1;
string private _baseUri;
IOperatorRegistry public operatorRegistry;
mapping(uint256 => string) private _tokenUris;
constructor(address _operatorRegistry, string memory baseUri) {
_initializeOwner(tx.origin);
operatorRegistry = IOperatorRegistry(_operatorRegistry);
_baseUri = baseUri;
}
function name() public pure override returns (string memory) {
return "MetaRebelz Rewards";
}
function symbol() public pure override returns (string memory) {
return "RWDZ";
}
function uri(uint256 id) public view override returns (string memory) {
string memory overrideUri = _tokenUris[id];
if (bytes(overrideUri).length > 0) {
return overrideUri;
}
return string(abi.encodePacked(_baseUri, id.toString()));
}
function setBaseUri(string memory baseUri) external onlyOwner {
_baseUri = baseUri;
}
function setTokenUri(uint256 id, string memory tokenUri) external onlyOwner {
_tokenUris[id] = tokenUri;
}
function setOperatorRegistry(address _operatorRegistry) external onlyOwner {
operatorRegistry = IOperatorRegistry(_operatorRegistry);
}
function mint(address account, uint256 id, uint256 amount, bytes memory data) external onlyRoles(MINTER_ROLE) {
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external onlyRoles(MINTER_ROLE) {
_mintBatch(to, ids, amounts, data);
}
function burn(address account, uint256 id, uint256 amount) external onlyRoles(BURNER_ROLE) {
_ensureApprovedSender(account);
_burn(account, id, amount);
}
function burnBatch(address account, uint256[] calldata ids, uint256[] calldata amounts) external onlyRoles(BURNER_ROLE) {
_ensureApprovedSender(account);
_burnBatch(account, ids, amounts);
}
function setApprovalForAll(address operator, bool approved) public override {
if (approved) {
_ensureAllowedOperator(operator, msg.sender);
}
super.setApprovalForAll(operator, approved);
}
function _ensureApprovedSender(address from) private view {
if (!isApprovedForAll(from, msg.sender)) _revert(TransferCallerNotOwnerNorApproved.selector);
}
function _beforeTokenTransfer(address operator, address from, address, uint256, uint256, bytes memory) internal view override {
_ensureAllowedOperator(operator, from);
}
function _beforeBatchTokenTransfer(address operator, address from, address, uint256[] calldata, uint256[] calldata, bytes memory) internal view override {
_ensureAllowedOperator(operator, from);
}
function _ensureAllowedOperator(address operator, address from) private view {
if (operator != address(0)) {
if (address(operatorRegistry) != address(0)) {
if (!operatorRegistry.isAllowedOperator(operator, from)) {
revert IOperatorRegistry.OperatorNotAllowed();
}
}
}
}
}
Ownable.sol 278 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
LibString.sol 1194 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
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.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `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)
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)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the ...
// [truncated — 51935 bytes total]
ERC1155P.sol 907 lines
// SPDX-License-Identifier: MIT
// ERC1155P Contracts v1.1
// Creator: 0xjustadev/0xth0mas
pragma solidity ^0.8.20;
import "./IERC1155P.sol";
/**
* @dev Interface of ERC1155 token receiver.
*/
interface ERC1155P__IERC1155Receiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
/**
* @dev Interface for IERC1155MetadataURI.
*/
interface ERC1155P__IERC1155MetadataURI {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}
/**
* @title ERC1155P
*
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155 including the Metadata extension.
* Optimized for lower gas for users collecting multiple tokens.
*
* Assumptions:
* - An owner cannot have more than 2**16 - 1 of a single token
* - The maximum token ID cannot exceed 2**100 - 1
*/
abstract contract ERC1155P is IERC1155P, ERC1155P__IERC1155MetadataURI {
/**
* @dev MAX_ACCOUNT_TOKEN_BALANCE is 2^16-1 because token balances are
* are being packed into 16 bits within each bucket.
*/
uint256 private constant MAX_ACCOUNT_TOKEN_BALANCE = 0xFFFF;
uint256 private constant BALANCE_STORAGE_OFFSET =
0xE000000000000000000000000000000000000000000000000000000000000000;
uint256 private constant APPROVAL_STORAGE_OFFSET =
0xD000000000000000000000000000000000000000000000000000000000000000;
/**
* @dev MAX_TOKEN_ID is derived from custom storage pointer location for
* account/token balance data. Wallet address is shifted 92 bits left
* and leaves 92 bits for bucket #'s. Each bucket holds 8 token balances
* 2^92*8-1 = MAX_TOKEN_ID
*/
uint256 private constant MAX_TOKEN_ID = 0x07FFFFFFFFFFFFFFFFFFFFFFF;
// The `TransferSingle` event signature is given by:
// `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
bytes32 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
// The `TransferBatch` event signature is given by:
// `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
bytes32 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
// The `ApprovalForAll` event signature is given by:
// `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
bytes32 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/// @dev Returns the name of the token.
function name() public view virtual returns(string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns(string memory);
/**
* @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 == 0xd9b67a26 || // ERC165 interface ID for ERC1155.
interfaceId == 0x0e89341c; // ERC165 interface ID for ERC1155MetadataURI.
}
/// @dev Returns the URI for token `id`.
///
/// You can either return the same templated URI for all token IDs,
/// (e.g. "https://example.com/api/{id}.json"),
/// or return a unique URI for each `id`.
///
/// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
function uri(uint256 id) public view virtual returns (string memory);
/**
* @dev See {IERC1155-balanceOf}.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
if(account == address(0)) { _revert(BalanceQueryForZeroAddress.selector); }
return getBalance(account, id);
}
/**
* @dev Gets the amount of tokens minted by an account for a given token id
*/
function _numberMinted(address account, uint256 id) internal view returns (uint256) {
if(account == address(0)) { _revert(BalanceQueryForZeroAddress.selector); }
return getMinted(account, id);
}
/**
* @dev Gets the balance of an account's token id from packed token data
*
*/
function getBalance(address account, uint256 id) private view returns (uint256 _balance) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, or(BALANCE_STORAGE_OFFSET, or(shr(4, shl(96, account)), shr(3, id))))
_balance := shr(shl(5, and(id, 0x07)), and(sload(keccak256(0x00, 0x20)), shl(shl(5, and(id, 0x07)), 0x0000FFFF)))
}
}
/**
* @dev Sets the balance of an account's token id in packed token data
*
*/
function setBalance(address account, uint256 id, uint256 amount) private {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, or(BALANCE_STORAGE_OFFSET, or(shr(4, shl(96, account)), shr(3, id))))
mstore(0x00, keccak256(0x00, 0x20))
sstore(mload(0x00), or(and(not(shl(shl(5, and(id, 0x07)), 0x0000FFFF)), sload(mload(0x00))), shl(shl(5, and(id, 0x07)), amount)))
}
}
/**
* @dev Gets the number minted of an account's token id from packed token data
*
*/
function getMinted(address account, uint256 id) private view returns (uint256 _minted) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, or(BALANCE_STORAGE_OFFSET, or(shr(4, shl(96, account)), shr(3, id))))
_minted := shr(16, shr(shl(5, and(id, 0x07)), and(sload(keccak256(0x00, 0x20)), shl(shl(5, and(id, 0x07)), 0xFFFF0000))))
}
}
/**
* @dev Sets the number minted of an account's token id in packed token data
*
*/
function setMinted(address account, uint256 id, uint256 amount) private {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, or(BALANCE_STORAGE_OFFSET, or(shr(4, shl(96, account)), shr(3, id))))
mstore(0x00, keccak256(0x00, 0x20))
sstore(mload(0x00), or(and(not(shl(shl(5, and(id, 0x07)), 0xFFFF0000)), sload(mload(0x00))), shl(shl(5, and(id, 0x07)), shl(16, amount))))
}
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) public view virtual override returns (uint256[] memory) {
if(accounts.length != ids.length) { _revert(ArrayLengthMismatch.selector); }
uint256[] memory batchBalances = new uint256[](accounts.length);
for(uint256 i = 0; i < accounts.length;) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
unchecked {
++i;
}
}
return batchBalances;
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual override returns (bool _approved) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, shr(96, shl(96, account)))
mstore(0x20, or(APPROVAL_STORAGE_OFFSET, shr(96, shl(96, operator))))
mstore(0x00, keccak256(0x00, 0x40))
_approved := sload(mload(0x00))
}
return _approved;
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual override {
_safeTransferFrom(from, to, id, amount, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory data
) public virtual override {
_safeBatchTransferFrom(from, to, ids, amounts, data);
}
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
if(to == address(0)) { _revert(TransferToZeroAddress.selector); }
if(from != _msgSenderERC1155P())
if (!isApprovedForAll(from, _msgSenderERC1155P())) _revert(TransferCallerNotOwnerNorApproved.selector);
address operator = _msgSenderERC1155P();
_beforeTokenTransfer(operator, from, to, id, amount, data);
uint256 fromBalance = getBalance(from, id);
if(amount > fromBalance) { _revert(TransferExceedsBalance.selector); }
if(from != to) {
uint256 toBalance = getBalance(to, id);
unchecked {
fromBalance -= amount;
toBalance += amount;
}
if(toBalance > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
setBalance(from, id, fromBalance);
setBalance(to, id, toBalance);
}
/// @solidity memory-safe-assembly
assembly {
// Emit the `TransferSingle` event.
let memOffset := mload(0x40)
mstore(memOffset, id)
mstore(add(memOffset, 0x20), amount)
log4(
memOffset, // Start of data .
0x40, // Length of data.
_TRANSFER_SINGLE_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
from, // `from`.
to // `to`.
)
}
_afterTokenTransfer(operator, from, to, id, amount, data);
if(to.code.length != 0)
if(!_checkContractOnERC1155Received(from, to, id, amount, data)) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory data
) internal virtual {
if(to == address(0)) { _revert(TransferToZeroAddress.selector); }
if(ids.length != amounts.length) { _revert(ArrayLengthMismatch.selector); }
if(from != _msgSenderERC1155P())
if (!isApprovedForAll(from, _msgSenderERC1155P())) _revert(TransferCallerNotOwnerNorApproved.selector);
address operator = _msgSenderERC1155P();
_beforeBatchTokenTransfer(operator, from, to, ids, amounts, data);
for (uint256 i = 0; i < ids.length;) {
uint256 id = ids[i];
uint256 amount = amounts[i];
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
uint256 fromBalance = getBalance(from, id);
if(amount > fromBalance) { _revert(TransferExceedsBalance.selector); }
if(from != to) {
uint256 toBalance = getBalance(to, id);
unchecked {
fromBalance -= amount;
toBalance += amount;
}
if(toBalance > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
setBalance(from, id, fromBalance);
setBalance(to, id, toBalance);
}
unchecked {
++i;
}
}
/// @solidity memory-safe-assembly
assembly {
let memOffset := mload(0x40)
mstore(memOffset, 0x40)
mstore(add(memOffset,0x20), add(0x60, mul(0x20,ids.length)))
mstore(add(memOffset,0x40), ids.length)
calldatacopy(add(memOffset,0x60), ids.offset, mul(0x20,ids.length))
mstore(add(add(memOffset,0x60),mul(0x20,ids.length)), amounts.length)
calldatacopy(add(add(memOffset,0x80),mul(0x20,ids.length)), amounts.offset, mul(0x20,amounts.length))
log4(
memOffset,
add(0x80,mul(0x40,amounts.length)),
_TRANSFER_BATCH_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
from, // `from`.
to // `to`.
)
}
_afterBatchTokenTransfer(operator, from, to, ids, amounts, data);
if(to.code.length != 0)
if(!_checkContractOnERC1155BatchReceived(from, to, ids, amounts, data)) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
if(to == address(0)) { _revert(MintToZeroAddress.selector); }
if(amount == 0) { _revert(MintZeroQuantity.selector); }
address operator = _msgSenderERC1155P();
_beforeTokenTransfer(operator, address(0), to, id, amount, data);
uint256 toBalanceBefore = getBalance(to, id);
uint256 toBalanceAfter;
unchecked {
toBalanceAfter = toBalanceBefore + amount;
}
if(toBalanceAfter > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
if(toBalanceAfter < toBalanceBefore) { _revert(ExceedsMaximumBalance.selector); } // catches overflow
setBalance(to, id, toBalanceAfter);
uint256 toMintedBefore = getMinted(to, id);
uint256 toMintedAfter;
unchecked {
toMintedAfter = toMintedBefore + amount;
}
if(toMintedAfter > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
if(toMintedAfter < toMintedBefore) { _revert(ExceedsMaximumBalance.selector); } // catches overflow
setMinted(to, id, toMintedAfter);
/// @solidity memory-safe-assembly
assembly {
// Emit the `TransferSingle` event.
let memOffset := mload(0x40)
mstore(memOffset, id)
mstore(add(memOffset, 0x20), amount)
log4(
memOffset, // Start of data .
0x40, // Length of data.
_TRANSFER_SINGLE_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
0, // `from`.
to // `to`.
)
}
_afterTokenTransfer(operator, address(0), to, id, amount, data);
if(to.code.length != 0)
if(!_checkContractOnERC1155Received(address(0), to, id, amount, data)) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory data
) internal virtual {
if(to == address(0)) { _revert(MintToZeroAddress.selector); }
if(ids.length != amounts.length) { _revert(ArrayLengthMismatch.selector); }
address operator = _msgSenderERC1155P();
_beforeBatchTokenTransfer(operator, address(0), to, ids, amounts, data);
uint256 id;
uint256 amount;
for (uint256 i = 0; i < ids.length;) {
id = ids[i];
amount = amounts[i];
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
if(amount == 0) { _revert(MintZeroQuantity.selector); }
uint256 toBalanceBefore = getBalance(to, id);
uint256 toBalanceAfter;
unchecked {
toBalanceAfter = toBalanceBefore + amount;
}
if(toBalanceAfter > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
if(toBalanceAfter < toBalanceBefore) { _revert(ExceedsMaximumBalance.selector); } // catches overflow
setBalance(to, id, toBalanceAfter);
uint256 toMintedBefore = getMinted(to, id);
uint256 toMintedAfter;
unchecked {
toMintedAfter = toMintedBefore + amount;
}
if(toMintedAfter > MAX_ACCOUNT_TOKEN_BALANCE) { _revert(ExceedsMaximumBalance.selector); }
if(toMintedAfter < toMintedBefore) { _revert(ExceedsMaximumBalance.selector); } // catches overflow
setMinted(to, id, toMintedAfter);
unchecked {
++i;
}
}
/// @solidity memory-safe-assembly
assembly {
let memOffset := mload(0x40)
mstore(memOffset, 0x40)
mstore(add(memOffset,0x20), add(0x60, mul(0x20,ids.length)))
mstore(add(memOffset,0x40), ids.length)
calldatacopy(add(memOffset,0x60), ids.offset, mul(0x20,ids.length))
mstore(add(add(memOffset,0x60),mul(0x20,ids.length)), amounts.length)
calldatacopy(add(add(memOffset,0x80),mul(0x20,ids.length)), amounts.offset, mul(0x20,amounts.length))
log4(
memOffset,
add(0x80,mul(0x40,amounts.length)),
_TRANSFER_BATCH_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
0, // `from`.
to // `to`.
)
}
_afterBatchTokenTransfer(operator, address(0), to, ids, amounts, data);
if(to.code.length != 0)
if(!_checkContractOnERC1155BatchReceived(address(0), to, ids, amounts, data)) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
}
/**
* @dev Destroys `amount` tokens of token type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens of token type `id`.
*/
function _burn(address from, uint256 id, uint256 amount) internal virtual {
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
if(from == address(0)) { _revert(BurnFromZeroAddress.selector); }
address operator = _msgSenderERC1155P();
_beforeTokenTransfer(operator, from, address(0), id, amount, "");
uint256 fromBalance = getBalance(from, id);
if(amount > fromBalance) { _revert(BurnExceedsBalance.selector); }
unchecked {
fromBalance -= amount;
}
setBalance(from, id, fromBalance);
/// @solidity memory-safe-assembly
assembly {
// Emit the `TransferSingle` event.
let memOffset := mload(0x40)
mstore(memOffset, id)
mstore(add(memOffset, 0x20), amount)
log4(
memOffset, // Start of data.
0x40, // Length of data.
_TRANSFER_SINGLE_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
from, // `from`.
0 // `to`.
)
}
_afterTokenTransfer(operator, from, address(0), id, amount, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
*/
function _burnBatch(address from, uint256[] calldata ids, uint256[] calldata amounts) internal virtual {
if(from == address(0)) { _revert(BurnFromZeroAddress.selector); }
if(ids.length != amounts.length) { _revert(ArrayLengthMismatch.selector); }
address operator = _msgSenderERC1155P();
_beforeBatchTokenTransfer(operator, from, address(0), ids, amounts, "");
for (uint256 i = 0; i < ids.length;) {
uint256 id = ids[i];
uint256 amount = amounts[i];
if(id > MAX_TOKEN_ID) { _revert(ExceedsMaximumTokenId.selector); }
uint256 fromBalance = getBalance(from, id);
if(amount > fromBalance) { _revert(BurnExceedsBalance.selector); }
unchecked {
fromBalance -= amount;
}
setBalance(from, id, fromBalance);
unchecked {
++i;
}
}
/// @solidity memory-safe-assembly
assembly {
let memOffset := mload(0x40)
mstore(memOffset, 0x40)
mstore(add(memOffset,0x20), add(0x60, mul(0x20,ids.length)))
mstore(add(memOffset,0x40), ids.length)
calldatacopy(add(memOffset,0x60), ids.offset, mul(0x20,ids.length))
mstore(add(add(memOffset,0x60),mul(0x20,ids.length)), amounts.length)
calldatacopy(add(add(memOffset,0x80),mul(0x20,ids.length)), amounts.offset, mul(0x20,amounts.length))
log4(
memOffset,
add(0x80,mul(0x40,amounts.length)),
_TRANSFER_BATCH_EVENT_SIGNATURE, // Signature.
operator, // `operator`.
from, // `from`.
0 // `to`.
)
}
_afterBatchTokenTransfer(operator, from, address(0), ids, amounts, "");
}
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, caller())
mstore(0x20, or(APPROVAL_STORAGE_OFFSET, shr(96, shl(96, operator))))
mstore(0x00, keccak256(0x00, 0x40))
mstore(0x20, approved)
sstore(mload(0x00), mload(0x20))
log3(
0x20,
0x20,
_APPROVAL_FOR_ALL_EVENT_SIGNATURE,
caller(),
shr(96, shl(96, operator))
)
}
}
/**
* @dev Hook that is called before any single token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {}
/**
* @dev Hook that is called before any batch token transfer. This includes minting
* and burning.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeBatchTokenTransfer(
address operator,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory data
) internal virtual {}
/**
* @dev Hook that is called after any single token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {}
/**
* @dev Hook that is called after any batch token transfer. This includes minting
* and burning.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterBatchTokenTransfer(
address operator,
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory data
) internal virtual {}
/**
* @dev Private function to invoke {IERC1155Receiver-onERC155Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `id` - Token ID to be transferred.
* `amount` - Balance of token to be transferred
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC1155Received(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory _data
) private returns (bool) {
try ERC1155P__IERC1155Receiver(to).onERC1155Received(_msgSenderERC1155P(), from, id, amount, _data) returns (
bytes4 retval
) {
return retval == ERC1155P__IERC1155Receiver(to).onERC1155Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
/**
* @dev Private function to invoke {IERC1155Receiver-onERC155Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `id` - Token ID to be transferred.
* `amount` - Balance of token to be transferred
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC1155BatchReceived(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes memory _data
) private returns (bool) {
try ERC1155P__IERC1155Receiver(to).onERC1155BatchReceived(_msgSenderERC1155P(), from, ids, amounts, _data) returns (
bytes4 retval
) {
return retval == ERC1155P__IERC1155Receiver(to).onERC1155BatchReceived.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/
function _msgSenderERC1155P() 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) {
/// @solidity memory-safe-assembly
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)
}
}
/**
* @dev For more efficient reverts.
*/
function _revert(bytes4 errorSelector) internal pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}
IERC1155P.sol 193 lines
// SPDX-License-Identifier: MIT
// ERC721P Contracts v1.1
pragma solidity ^0.8.20;
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155P {
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Arrays cannot be different lengths.
*/
error ArrayLengthMismatch();
/**
* Cannot burn from the zero address.
*/
error BurnFromZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The quantity of tokens being burned is greater than account balance.
*/
error BurnExceedsBalance();
/**
* The quantity of tokens being transferred is greater than account balance.
*/
error TransferExceedsBalance();
/**
* The resulting token balance exceeds the maximum storable by ERC1155P
*/
error ExceedsMaximumBalance();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC1155Receiver interface.
*/
error TransferToNonERC1155ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* Exceeds max token ID
*/
error ExceedsMaximumTokenId();
// =============================================================
// 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);
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
OwnableRoles.sol 530 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Ownable} from "./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `user`'s roles is updated to `roles`.
/// Each bit of `roles` represents whether the role is set.
event RolesUpdated(address indexed user, uint256 indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The role slot of `user` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
/// let roleSlot := keccak256(0x00, 0x20)
/// ```
/// This automatically ignores the upper bits of the `user` in case
/// they are not clean, as well as keep the `keccak256` under 32-bytes.
///
/// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Overwrite the roles directly without authorization guard.
function _setRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Store the new value.
sstore(keccak256(0x0c, 0x20), roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Updates the roles directly without authorization guard.
/// If `on` is true, each set bit of `roles` will be turned on,
/// otherwise, each set bit of `roles` will be turned off.
function _updateRoles(address user, uint256 roles, bool on) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value.
let current := sload(roleSlot)
// Compute the updated roles if `on` is true.
let updated := or(current, roles)
// Compute the updated roles if `on` is false.
// Use `and` to compute the intersection of `current` and `roles`,
// `xor` it with `current` to flip the bits in the intersection.
if iszero(on) { updated := xor(current, and(current, roles)) }
// Then, store the new value.
sstore(roleSlot, updated)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
/// @dev Grants the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn on.
function _grantRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, true);
}
/// @dev Removes the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn off.
function _removeRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.
function _checkRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,
/// and does not have any of the `roles`.
/// Checks for ownership first, then lazily checks for roles.
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,
/// and is not the owner.
/// Checks for roles first, then lazily checks for ownership.
function _checkRolesOrOwner(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity
// cleans dirty upper bits when storing variables into memory.
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
/// @solidity memory-safe-assembly
assembly {
// Grab the pointer to the free memory.
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
// The absence of lookup tables, De Bruijn, etc., here is intentional for
// smaller bytecode, as this function is not meant to be called on-chain.
for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.
// Push back into the ordinals array if the bit is set.
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
// Store the length of `ordinals`.
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.
mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be an no-op for the role.
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be an no-op for the role.
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be an no-op for the role.
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the roles of `user`.
function rolesOf(address user) public view virtual returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles != 0;
}
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by an account with `roles`.
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account
/// with `roles`. Checks for ownership first, then lazily checks for roles.
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`
/// or the owner. Checks for roles first, then lazily checks for ownership.
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ROLE CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// IYKYK
uint256 internal constant _ROLE_0 = 1 << 0;
uint256 internal constant _ROLE_1 = 1 << 1;
uint256 internal constant _ROLE_2 = 1 << 2;
uint256 internal constant _ROLE_3 = 1 << 3;
uint256 internal constant _ROLE_4 = 1 << 4;
uint256 internal constant _ROLE_5 = 1 << 5;
uint256 internal constant _ROLE_6 = 1 << 6;
uint256 internal constant _ROLE_7 = 1 << 7;
uint256 internal constant _ROLE_8 = 1 << 8;
uint256 internal constant _ROLE_9 = 1 << 9;
uint256 internal constant _ROLE_10 = 1 << 10;
uint256 internal constant _ROLE_11 = 1 << 11;
uint256 internal constant _ROLE_12 = 1 << 12;
uint256 internal constant _ROLE_13 = 1 << 13;
uint256 internal constant _ROLE_14 = 1 << 14;
uint256 internal constant _ROLE_15 = 1 << 15;
uint256 internal constant _ROLE_16 = 1 << 16;
uint256 internal constant _ROLE_17 = 1 << 17;
uint256 internal constant _ROLE_18 = 1 << 18;
uint256 internal constant _ROLE_19 = 1 << 19;
uint256 internal constant _ROLE_20 = 1 << 20;
uint256 internal constant _ROLE_21 = 1 << 21;
uint256 internal constant _ROLE_22 = 1 << 22;
uint256 internal constant _ROLE_23 = 1 << 23;
uint256 internal constant _ROLE_24 = 1 << 24;
uint256 internal constant _ROLE_25 = 1 << 25;
uint256 internal constant _ROLE_26 = 1 << 26;
uint256 internal constant _ROLE_27 = 1 << 27;
uint256 internal constant _ROLE_28 = 1 << 28;
uint256 internal constant _ROLE_29 = 1 << 29;
uint256 internal constant _ROLE_30 = 1 << 30;
uint256 internal constant _ROLE_31 = 1 << 31;
uint256 internal constant _ROLE_32 = 1 << 32;
uint256 internal constant _ROLE_33 = 1 << 33;
uint256 internal constant _ROLE_34 = 1 << 34;
uint256 internal constant _ROLE_35 = 1 << 35;
uint256 internal constant _ROLE_36 = 1 << 36;
uint256 internal constant _ROLE_37 = 1 << 37;
uint256 internal constant _ROLE_38 = 1 << 38;
uint256 internal constant _ROLE_39 = 1 << 39;
uint256 internal constant _ROLE_40 = 1 << 40;
uint256 internal constant _ROLE_41 = 1 << 41;
uint256 internal constant _ROLE_42 = 1 << 42;
uint256 internal constant _ROLE_43 = 1 << 43;
uint256 internal constant _ROLE_44 = 1 << 44;
uint256 internal constant _ROLE_45 = 1 << 45;
uint256 internal constant _ROLE_46 = 1 << 46;
uint256 internal constant _ROLE_47 = 1 << 47;
uint256 internal constant _ROLE_48 = 1 << 48;
uint256 internal constant _ROLE_49 = 1 << 49;
uint256 internal constant _ROLE_50 = 1 << 50;
uint256 internal constant _ROLE_51 = 1 << 51;
uint256 internal constant _ROLE_52 = 1 << 52;
uint256 internal constant _ROLE_53 = 1 << 53;
uint256 internal constant _ROLE_54 = 1 << 54;
uint256 internal constant _ROLE_55 = 1 << 55;
uint256 internal constant _ROLE_56 = 1 << 56;
uint256 internal constant _ROLE_57 = 1 << 57;
uint256 internal constant _ROLE_58 = 1 << 58;
uint256 internal constant _ROLE_59 = 1 << 59;
uint256 internal constant _ROLE_60 = 1 << 60;
uint256 internal constant _ROLE_61 = 1 << 61;
uint256 internal constant _ROLE_62 = 1 << 62;
uint256 internal constant _ROLE_63 = 1 << 63;
uint256 internal constant _ROLE_64 = 1 << 64;
uint256 internal constant _ROLE_65 = 1 << 65;
uint256 internal constant _ROLE_66 = 1 << 66;
uint256 internal constant _ROLE_67 = 1 << 67;
uint256 internal constant _ROLE_68 = 1 << 68;
uint256 internal constant _ROLE_69 = 1 << 69;
uint256 internal constant _ROLE_70 = 1 << 70;
uint256 internal constant _ROLE_71 = 1 << 71;
uint256 internal constant _ROLE_72 = 1 << 72;
uint256 internal constant _ROLE_73 = 1 << 73;
uint256 internal constant _ROLE_74 = 1 << 74;
uint256 internal constant _ROLE_75 = 1 << 75;
uint256 internal constant _ROLE_76 = 1 << 76;
uint256 internal constant _ROLE_77 = 1 << 77;
uint256 internal constant _ROLE_78 = 1 << 78;
uint256 internal constant _ROLE_79 = 1 << 79;
uint256 internal constant _ROLE_80 = 1 << 80;
uint256 internal constant _ROLE_81 = 1 << 81;
uint256 internal constant _ROLE_82 = 1 << 82;
uint256 internal constant _ROLE_83 = 1 << 83;
uint256 internal constant _ROLE_84 = 1 << 84;
uint256 internal constant _ROLE_85 = 1 << 85;
uint256 internal constant _ROLE_86 = 1 << 86;
uint256 internal constant _ROLE_87 = 1 << 87;
uint256 internal constant _ROLE_88 = 1 << 88;
uint256 internal constant _ROLE_89 = 1 << 89;
uint256 internal constant _ROLE_90 = 1 << 90;
uint256 internal constant _ROLE_91 = 1 << 91;
uint256 internal constant _ROLE_92 = 1 << 92;
uint256 internal constant _ROLE_93 = 1 << 93;
uint256 internal constant _ROLE_94 = 1 << 94;
uint256 internal constant _ROLE_95 = 1 << 95;
uint256 internal constant _ROLE_96 = 1 << 96;
uint256 internal constant _ROLE_97 = 1 << 97;
uint256 internal constant _ROLE_98 = 1 << 98;
uint256 internal constant _ROLE_99 = 1 << 99;
uint256 internal constant _ROLE_100 = 1 << 100;
uint256 internal constant _ROLE_101 = 1 << 101;
uint256 internal constant _ROLE_102 = 1 << 102;
uint256 internal constant _ROLE_103 = 1 << 103;
uint256 internal constant _ROLE_104 = 1 << 104;
uint256 internal constant _ROLE_105 = 1 << 105;
uint256 internal constant _ROLE_106 = 1 << 106;
uint256 internal constant _ROLE_107 = 1 << 107;
uint256 internal constant _ROLE_108 = 1 << 108;
uint256 internal constant _ROLE_109 = 1 << 109;
uint256 internal constant _ROLE_110 = 1 << 110;
uint256 internal constant _ROLE_111 = 1 << 111;
uint256 internal constant _ROLE_112 = 1 << 112;
uint256 internal constant _ROLE_113 = 1 << 113;
uint256 internal constant _ROLE_114 = 1 << 114;
uint256 internal constant _ROLE_115 = 1 << 115;
uint256 internal constant _ROLE_116 = 1 << 116;
uint256 internal constant _ROLE_117 = 1 << 117;
uint256 internal constant _ROLE_118 = 1 << 118;
uint256 internal constant _ROLE_119 = 1 << 119;
uint256 internal constant _ROLE_120 = 1 << 120;
uint256 internal constant _ROLE_121 = 1 << 121;
uint256 internal constant _ROLE_122 = 1 << 122;
uint256 internal constant _ROLE_123 = 1 << 123;
uint256 internal constant _ROLE_124 = 1 << 124;
uint256 internal constant _ROLE_125 = 1 << 125;
uint256 internal constant _ROLE_126 = 1 << 126;
uint256 internal constant _ROLE_127 = 1 << 127;
uint256 internal constant _ROLE_128 = 1 << 128;
uint256 internal constant _ROLE_129 = 1 << 129;
uint256 internal constant _ROLE_130 = 1 << 130;
uint256 internal constant _ROLE_131 = 1 << 131;
uint256 internal constant _ROLE_132 = 1 << 132;
uint256 internal constant _ROLE_133 = 1 << 133;
uint256 internal constant _ROLE_134 = 1 << 134;
uint256 internal constant _ROLE_135 = 1 << 135;
uint256 internal constant _ROLE_136 = 1 << 136;
uint256 internal constant _ROLE_137 = 1 << 137;
uint256 internal constant _ROLE_138 = 1 << 138;
uint256 internal constant _ROLE_139 = 1 << 139;
uint256 internal constant _ROLE_140 = 1 << 140;
uint256 internal constant _ROLE_141 = 1 << 141;
uint256 internal constant _ROLE_142 = 1 << 142;
uint256 internal constant _ROLE_143 = 1 << 143;
uint256 internal constant _ROLE_144 = 1 << 144;
uint256 internal constant _ROLE_145 = 1 << 145;
uint256 internal constant _ROLE_146 = 1 << 146;
uint256 internal constant _ROLE_147 = 1 << 147;
uint256 internal constant _ROLE_148 = 1 << 148;
uint256 internal constant _ROLE_149 = 1 << 149;
uint256 internal constant _ROLE_150 = 1 << 150;
uint256 internal constant _ROLE_151 = 1 << 151;
uint256 internal constant _ROLE_152 = 1 << 152;
uint256 internal constant _ROLE_153 = 1 << 153;
uint256 internal constant _ROLE_154 = 1 << 154;
uint256 internal constant _ROLE_155 = 1 << 155;
uint256 internal constant _ROLE_156 = 1 << 156;
uint256 internal constant _ROLE_157 = 1 << 157;
uint256 internal constant _ROLE_158 = 1 << 158;
uint256 internal constant _ROLE_159 = 1 << 159;
uint256 internal constant _ROLE_160 = 1 << 160;
uint256 internal constant _ROLE_161 = 1 << 161;
uint256 internal constant _ROLE_162 = 1 << 162;
uint256 internal constant _ROLE_163 = 1 << 163;
uint256 internal constant _ROLE_164 = 1 << 164;
uint256 internal constant _ROLE_165 = 1 << 165;
uint256 internal constant _ROLE_166 = 1 << 166;
uint256 internal constant _ROLE_167 = 1 << 167;
uint256 internal constant _ROLE_168 = 1 << 168;
uint256 internal constant _ROLE_169 = 1 << 169;
uint256 internal constant _ROLE_170 = 1 << 170;
uint256 internal constant _ROLE_171 = 1 << 171;
uint256 internal constant _ROLE_172 = 1 << 172;
uint256 internal constant _ROLE_173 = 1 << 173;
uint256 internal constant _ROLE_174 = 1 << 174;
uint256 internal constant _ROLE_175 = 1 << 175;
uint256 internal constant _ROLE_176 = 1 << 176;
uint256 internal constant _ROLE_177 = 1 << 177;
uint256 internal constant _ROLE_178 = 1 << 178;
uint256 internal constant _ROLE_179 = 1 << 179;
uint256 internal constant _ROLE_180 = 1 << 180;
uint256 internal constant _ROLE_181 = 1 << 181;
uint256 internal constant _ROLE_182 = 1 << 182;
uint256 internal constant _ROLE_183 = 1 << 183;
uint256 internal constant _ROLE_184 = 1 << 184;
uint256 internal constant _ROLE_185 = 1 << 185;
uint256 internal constant _ROLE_186 = 1 << 186;
uint256 internal constant _ROLE_187 = 1 << 187;
uint256 internal constant _ROLE_188 = 1 << 188;
uint256 internal constant _ROLE_189 = 1 << 189;
uint256 internal constant _ROLE_190 = 1 << 190;
uint256 internal constant _ROLE_191 = 1 << 191;
uint256 internal constant _ROLE_192 = 1 << 192;
uint256 internal constant _ROLE_193 = 1 << 193;
uint256 internal constant _ROLE_194 = 1 << 194;
uint256 internal constant _ROLE_195 = 1 << 195;
uint256 internal constant _ROLE_196 = 1 << 196;
uint256 internal constant _ROLE_197 = 1 << 197;
uint256 internal constant _ROLE_198 = 1 << 198;
uint256 internal constant _ROLE_199 = 1 << 199;
uint256 internal constant _ROLE_200 = 1 << 200;
uint256 internal constant _ROLE_201 = 1 << 201;
uint256 internal constant _ROLE_202 = 1 << 202;
uint256 internal constant _ROLE_203 = 1 << 203;
uint256 internal constant _ROLE_204 = 1 << 204;
uint256 internal constant _ROLE_205 = 1 << 205;
uint256 internal constant _ROLE_206 = 1 << 206;
uint256 internal constant _ROLE_207 = 1 << 207;
uint256 internal constant _ROLE_208 = 1 << 208;
uint256 internal constant _ROLE_209 = 1 << 209;
uint256 internal constant _ROLE_210 = 1 << 210;
uint256 internal constant _ROLE_211 = 1 << 211;
uint256 internal constant _ROLE_212 = 1 << 212;
uint256 internal constant _ROLE_213 = 1 << 213;
uint256 internal constant _ROLE_214 = 1 << 214;
uint256 internal constant _ROLE_215 = 1 << 215;
uint256 internal constant _ROLE_216 = 1 << 216;
uint256 internal constant _ROLE_217 = 1 << 217;
uint256 internal constant _ROLE_218 = 1 << 218;
uint256 internal constant _ROLE_219 = 1 << 219;
uint256 internal constant _ROLE_220 = 1 << 220;
uint256 internal constant _ROLE_221 = 1 << 221;
uint256 internal constant _ROLE_222 = 1 << 222;
uint256 internal constant _ROLE_223 = 1 << 223;
uint256 internal constant _ROLE_224 = 1 << 224;
uint256 internal constant _ROLE_225 = 1 << 225;
uint256 internal constant _ROLE_226 = 1 << 226;
uint256 internal constant _ROLE_227 = 1 << 227;
uint256 internal constant _ROLE_228 = 1 << 228;
uint256 internal constant _ROLE_229 = 1 << 229;
uint256 internal constant _ROLE_230 = 1 << 230;
uint256 internal constant _ROLE_231 = 1 << 231;
uint256 internal constant _ROLE_232 = 1 << 232;
uint256 internal constant _ROLE_233 = 1 << 233;
uint256 internal constant _ROLE_234 = 1 << 234;
uint256 internal constant _ROLE_235 = 1 << 235;
uint256 internal constant _ROLE_236 = 1 << 236;
uint256 internal constant _ROLE_237 = 1 << 237;
uint256 internal constant _ROLE_238 = 1 << 238;
uint256 internal constant _ROLE_239 = 1 << 239;
uint256 internal constant _ROLE_240 = 1 << 240;
uint256 internal constant _ROLE_241 = 1 << 241;
uint256 internal constant _ROLE_242 = 1 << 242;
uint256 internal constant _ROLE_243 = 1 << 243;
uint256 internal constant _ROLE_244 = 1 << 244;
uint256 internal constant _ROLE_245 = 1 << 245;
uint256 internal constant _ROLE_246 = 1 << 246;
uint256 internal constant _ROLE_247 = 1 << 247;
uint256 internal constant _ROLE_248 = 1 << 248;
uint256 internal constant _ROLE_249 = 1 << 249;
uint256 internal constant _ROLE_250 = 1 << 250;
uint256 internal constant _ROLE_251 = 1 << 251;
uint256 internal constant _ROLE_252 = 1 << 252;
uint256 internal constant _ROLE_253 = 1 << 253;
uint256 internal constant _ROLE_254 = 1 << 254;
uint256 internal constant _ROLE_255 = 1 << 255;
}
IOperatorRegistry.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IOperatorRegistry {
error OperatorNotAllowed();
function isAllowedOperator(address operator, address tokenHolder) external view returns (bool);
}
Read Contract
BURNER_ROLE 0x282c51f3 → uint256
MINTER_ROLE 0xd5391393 → uint256
balanceOf 0x00fdd58e → uint256
balanceOfBatch 0x4e1273f4 → uint256[]
hasAllRoles 0x1cd64df4 → bool
hasAnyRole 0x514e62fc → bool
isApprovedForAll 0xe985e9c5 → bool
name 0x06fdde03 → string
operatorRegistry 0x58c2225b → address
owner 0x8da5cb5b → address
ownershipHandoverExpiresAt 0xfee81cf4 → uint256
rolesOf 0x2de94807 → uint256
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
uri 0x0e89341c → string
Write Contract 18 functions
These functions modify contract state and require a wallet transaction to execute.
burn 0xf5298aca
address account
uint256 id
uint256 amount
burnBatch 0x6b20c454
address account
uint256[] ids
uint256[] amounts
cancelOwnershipHandover 0x54d1f13d
No parameters
completeOwnershipHandover 0xf04e283e
address pendingOwner
grantRoles 0x1c10893f
address user
uint256 roles
mint 0x731133e9
address account
uint256 id
uint256 amount
bytes data
mintBatch 0x1f7fdffa
address to
uint256[] ids
uint256[] amounts
bytes data
renounceOwnership 0x715018a6
No parameters
renounceRoles 0x183a4f6e
uint256 roles
requestOwnershipHandover 0x25692962
No parameters
revokeRoles 0x4a4ee7b1
address user
uint256 roles
safeBatchTransferFrom 0x2eb2c2d6
address from
address to
uint256[] ids
uint256[] amounts
bytes data
safeTransferFrom 0xf242432a
address from
address to
uint256 id
uint256 amount
bytes data
setApprovalForAll 0xa22cb465
address operator
bool approved
setBaseUri 0xa0bcfc7f
string baseUri
setOperatorRegistry 0x9d28fb86
address _operatorRegistry
setTokenUri 0x57f7789e
uint256 id
string tokenUri
transferOwnership 0xf2fde38b
address newOwner
Recent Transactions
No transactions found for this address