// SPDX-License-Identifier: MIT// ERC1155P Contracts v1.1// Creator: 0xjustadev/0xth0maspragmasolidity ^0.8.20;import"./IERC1155P.sol";
/**
* @dev Interface of ERC1155 token receiver.
*/interfaceERC1155P__IERC1155Receiver{
functiononERC1155Received(address operator,
addressfrom,
uint256 id,
uint256 value,
bytescalldata data
) externalreturns (bytes4);
functiononERC1155BatchReceived(address operator,
addressfrom,
uint256[] calldata ids,
uint256[] calldata values,
bytescalldata data
) externalreturns (bytes4);
}
/**
* @dev Interface for IERC1155MetadataURI.
*/interfaceERC1155P__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.
*/functionuri(uint256 id) externalviewreturns (stringmemory);
}
/**
* @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
*/abstractcontractERC1155PisIERC1155P, ERC1155P__IERC1155MetadataURI{
/**
* @dev MAX_ACCOUNT_TOKEN_BALANCE is 2^16-1 because token balances are
* are being packed into 16 bits within each bucket.
*/uint256privateconstant MAX_ACCOUNT_TOKEN_BALANCE =0xFFFF;
uint256privateconstant BALANCE_STORAGE_OFFSET =0xE000000000000000000000000000000000000000000000000000000000000000;
uint256privateconstant 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
*/uint256privateconstant MAX_TOKEN_ID =0x07FFFFFFFFFFFFFFFFFFFFFFF;
// The `TransferSingle` event signature is given by:// `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.bytes32privateconstant _TRANSFER_SINGLE_EVENT_SIGNATURE =0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
// The `TransferBatch` event signature is given by:// `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.bytes32privateconstant _TRANSFER_BATCH_EVENT_SIGNATURE =0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
// The `ApprovalForAll` event signature is given by:// `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.bytes32privateconstant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/// @dev Returns the name of the token.functionname() publicviewvirtualreturns(stringmemory);
/// @dev Returns the symbol of the token.functionsymbol() publicviewvirtualreturns(stringmemory);
/**
* @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.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (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#metadatafunctionuri(uint256 id) publicviewvirtualreturns (stringmemory);
/**
* @dev See {IERC1155-balanceOf}.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/functionbalanceOf(address account, uint256 id) publicviewvirtualoverridereturns (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) internalviewreturns (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
*
*/functiongetBalance(address account, uint256 id) privateviewreturns (uint256 _balance) {
/// @solidity memory-safe-assemblyassembly {
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
*
*/functionsetBalance(address account, uint256 id, uint256 amount) private{
/// @solidity memory-safe-assemblyassembly {
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
*
*/functiongetMinted(address account, uint256 id) privateviewreturns (uint256 _minted) {
/// @solidity memory-safe-assemblyassembly {
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
*
*/functionsetMinted(address account, uint256 id, uint256 amount) private{
/// @solidity memory-safe-assemblyassembly {
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.
*/functionbalanceOfBatch(address[] calldata accounts,
uint256[] calldata ids
) publicviewvirtualoverridereturns (uint256[] memory) {
if(accounts.length!= ids.length) { _revert(ArrayLengthMismatch.selector); }
uint256[] memory batchBalances =newuint256[](accounts.length);
for(uint256 i =0; i < accounts.length;) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
unchecked {
++i;
}
}
return batchBalances;
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/functionisApprovedForAll(address account, address operator) publicviewvirtualoverridereturns (bool _approved) {
/// @solidity memory-safe-assemblyassembly {
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}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
uint256 amount,
bytesmemory data
) publicvirtualoverride{
_safeTransferFrom(from, to, id, amount, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytesmemory data
) publicvirtualoverride{
_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(addressfrom,
address to,
uint256 id,
uint256 amount,
bytesmemory data
) internalvirtual{
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-assemblyassembly {
// 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(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytesmemory data
) internalvirtual{
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-assemblyassembly {
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, bytesmemory data) internalvirtual{
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-assemblyassembly {
// 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,
bytesmemory data
) internalvirtual{
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-assemblyassembly {
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(addressfrom, uint256 id, uint256 amount) internalvirtual{
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-assemblyassembly {
// 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(addressfrom, uint256[] calldata ids, uint256[] calldata amounts) internalvirtual{
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-assemblyassembly {
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.
*/functionsetApprovalForAll(address operator, bool approved) publicvirtualoverride{
/// @solidity memory-safe-assemblyassembly {
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,
addressfrom,
address to,
uint256 id,
uint256 amount,
bytesmemory data
) internalvirtual{}
/**
* @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,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytesmemory data
) internalvirtual{}
/**
* @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,
addressfrom,
address to,
uint256 id,
uint256 amount,
bytesmemory data
) internalvirtual{}
/**
* @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,
addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytesmemory data
) internalvirtual{}
/**
* @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(addressfrom,
address to,
uint256 id,
uint256 amount,
bytesmemory _data
) privatereturns (bool) {
try ERC1155P__IERC1155Receiver(to).onERC1155Received(_msgSenderERC1155P(), from, id, amount, _data) returns (
bytes4 retval
) {
return retval == ERC1155P__IERC1155Receiver(to).onERC1155Received.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
/// @solidity memory-safe-assemblyassembly {
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(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytesmemory _data
) privatereturns (bool) {
try ERC1155P__IERC1155Receiver(to).onERC1155BatchReceived(_msgSenderERC1155P(), from, ids, amounts, _data) returns (
bytes4 retval
) {
return retval == ERC1155P__IERC1155Receiver(to).onERC1155BatchReceived.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
_revert(TransferToNonERC1155ReceiverImplementer.selector);
}
/// @solidity memory-safe-assemblyassembly {
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() internalviewvirtualreturns (address) {
returnmsg.sender;
}
/**
* @dev Converts a uint256 to its ASCII string decimal representation.
*/function_toString(uint256 value) internalpurevirtualreturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
// 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-ignorefor { 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-ignoreifiszero(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) internalpure{
/// @solidity memory-safe-assemblyassembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}
Contract Source Code
File 2 of 7: IERC1155P.sol
// SPDX-License-Identifier: MIT// ERC721P Contracts v1.1pragmasolidity ^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._
*/interfaceIERC1155P{
/**
* Cannot query the balance for the zero address.
*/errorBalanceQueryForZeroAddress();
/**
* Arrays cannot be different lengths.
*/errorArrayLengthMismatch();
/**
* Cannot burn from the zero address.
*/errorBurnFromZeroAddress();
/**
* Cannot mint to the zero address.
*/errorMintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/errorMintZeroQuantity();
/**
* The quantity of tokens being burned is greater than account balance.
*/errorBurnExceedsBalance();
/**
* The quantity of tokens being transferred is greater than account balance.
*/errorTransferExceedsBalance();
/**
* The resulting token balance exceeds the maximum storable by ERC1155P
*/errorExceedsMaximumBalance();
/**
* The caller must own the token or be an approved operator.
*/errorTransferCallerNotOwnerNorApproved();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC1155Receiver interface.
*/errorTransferToNonERC1155ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/errorTransferToZeroAddress();
/**
* Exceeds max token ID
*/errorExceedsMaximumTokenId();
// =============================================================// 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.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/eventTransferSingle(addressindexed operator, addressindexedfrom, addressindexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/eventTransferBatch(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/eventApprovalForAll(addressindexed account, addressindexed 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}.
*/eventURI(string value, uint256indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/functionbalanceOf(address account, uint256 id) externalviewreturns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/functionbalanceOfBatch(address[] calldata accounts,
uint256[] calldata ids
) externalviewreturns (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.
*/functionsetApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/functionisApprovedForAll(address account, address operator) externalviewreturns (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.
*/functionsafeTransferFrom(addressfrom, address to, uint256 id, uint256 amount, bytescalldata 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.
*/functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytescalldata data
) external;
}
// SPDX-License-Identifier: MITpragmasolidity ^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.libraryLibString{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The length of the output is too small to contain all the hex digits.errorHexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.errorTooBigForSmallString();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The constant returned when the `search` is not found in the string.uint256internalconstant NOT_FOUND =type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* DECIMAL OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the base 10 decimal representation of `value`.functiontoString(uint256 value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
// 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)
ifiszero(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`.functiontoString(int256 value) internalpurereturns (stringmemory str) {
if (value >=0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) +1);
}
/// @solidity memory-safe-assemblyassembly {
// 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.functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assemblyassembly {
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.functiontoHexStringNoPrefix(uint256 value, uint256 length)
internalpurereturns (stringmemory str)
{
/// @solidity memory-safe-assemblyassembly {
// 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)
ifiszero(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.functiontoHexString(uint256 value) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assemblyassembly {
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"`.functiontoMinimalHexString(uint256 value) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assemblyassembly {
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"`.functiontoMinimalHexStringNoPrefix(uint256 value) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assemblyassembly {
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.functiontoHexStringNoPrefix(uint256 value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
// 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)
ifiszero(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-55functiontoHexStringChecksummed(address value) internalpurereturns (stringmemory str) {
str = toHexString(value);
/// @solidity memory-safe-assemblyassembly {
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)
ifeq(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.functiontoHexString(address value) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assemblyassembly {
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.functiontoHexStringNoPrefix(address value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
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)
ifeq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes./// The output is encoded using 2 hexadecimal digits per byte.functiontoHexString(bytesmemory raw) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assemblyassembly {
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.functiontoHexStringNoPrefix(bytesmemory raw) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
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.functionruneCount(stringmemory s) internalpurereturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly {
ifmload(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)))))
ifiszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string./// (i.e. all characters codes are in [0..127])functionis7BitASCII(stringmemory s) internalpurereturns (bool result) {
/// @solidity memory-safe-assemblyassembly {
let mask :=shl(7, div(not(0), 255))
result :=1let n :=mload(s)
if n {
let o :=add(s, 0x20)
let end :=add(o, n)
let last :=mload(end)
mstore(end, 0)
for {} 1 {} {
ifand(mask, mload(o)) {
result :=0break
}
o :=add(o, 0x20)
ifiszero(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`.functionreplace(stringmemory subject, stringmemory search, stringmemory replacement)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
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)
ifiszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd :=add(sub(subjectEnd, searchLength), 1)
let h :=0ifiszero(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.ifiszero(shr(m, xor(t, s))) {
if h {
ifiszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result :=add(result, 1)
subject :=add(subject, 1)
ifiszero(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)
ifiszero(lt(o, replacementLength)) { break }
}
result :=add(result, replacementLength)
subject :=add(subject, searchLength)
if searchLength {
ifiszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result :=add(result, 1)
subject :=add(subject, 1)
ifiszero(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.functionindexOf(stringmemory subject, stringmemory search, uint256from)
internalpurereturns (uint256 result)
{
/// @solidity memory-safe-assemblyassembly {
for { let subjectLength :=mload(subject) } 1 {} {
ifiszero(mload(search)) {
ifiszero(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))
ifiszero(and(lt(subject, end), lt(from, subjectLength))) { break }
ifiszero(lt(searchLength, 0x20)) {
for { let h :=keccak256(add(search, 0x20), searchLength) } 1 {} {
ifiszero(shr(m, xor(mload(subject), s))) {
ifeq(keccak256(subject, searchLength), h) {
result :=sub(subject, subjectStart)
break
}
}
subject :=add(subject, 1)
ifiszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
ifiszero(shr(m, xor(mload(subject), s))) {
result :=sub(subject, subjectStart)
break
}
subject :=add(subject, 1)
ifiszero(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.functionindexOf(stringmemory subject, stringmemory search)
internalpurereturns (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.functionlastIndexOf(stringmemory subject, stringmemory search, uint256from)
internalpurereturns (uint256 result)
{
/// @solidity memory-safe-assemblyassembly {
for {} 1 {} {
result :=not(0) // Initialize to `NOT_FOUND`.let searchLength :=mload(search)
ifgt(searchLength, mload(subject)) { break }
let w := result
let fromMax :=sub(mload(subject), searchLength)
ifiszero(gt(fromMax, from)) { from := fromMax }
let end :=add(add(subject, 0x20), w)
subject :=add(add(subject, 0x20), from)
ifiszero(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 {} {
ifeq(keccak256(subject, searchLength), h) {
result :=sub(subject, add(end, 1))
break
}
subject :=add(subject, w) // `sub(subject, 1)`.ifiszero(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.functionlastIndexOf(stringmemory subject, stringmemory search)
internalpurereturns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.functioncontains(stringmemory subject, stringmemory search) internalpurereturns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.functionstartsWith(stringmemory subject, stringmemory search)
internalpurereturns (bool result)
{
/// @solidity memory-safe-assemblyassembly {
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`.functionendsWith(stringmemory subject, stringmemory search)
internalpurereturns (bool result)
{
/// @solidity memory-safe-assemblyassembly {
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`.functionrepeat(stringmemory subject, uint256 times)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
let subjectLength :=mload(subject)
ifiszero(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)
ifiszero(lt(o, subjectLength)) { break }
}
output :=add(output, subjectLength)
times :=sub(times, 1)
ifiszero(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.functionslice(stringmemory subject, uint256 start, uint256 end)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
let subjectLength :=mload(subject)
ifiszero(gt(subjectLength, end)) { end := subjectLength }
ifiszero(gt(subjectLength, start)) { start := subjectLength }
iflt(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)`.ifiszero(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.functionslice(stringmemory subject, uint256 start)
internalpurereturns (stringmemory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`./// The indices are byte offsets.functionindicesOf(stringmemory subject, stringmemory search)
internalpurereturns (uint256[] memory result)
{
/// @solidity memory-safe-assemblyassembly {
let subjectLength :=mload(subject)
let searchLength :=mload(search)
ifiszero(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 :=0ifiszero(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.ifiszero(shr(m, xor(t, s))) {
if h {
ifiszero(eq(keccak256(subject, searchLength), h)) {
subject :=add(subject, 1)
ifiszero(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 {
ifiszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject :=add(subject, 1)
ifiszero(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.functionsplit(stringmemory subject, stringmemory delimiter)
internalpurereturns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assemblyassembly {
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 :=0for {} 1 {} {
let index :=mload(indexPtr)
mstore(indexPtr, 0x60)
ifiszero(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)`.ifiszero(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)
ifiszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
ifiszero(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.functionconcat(stringmemory a, stringmemory b)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
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)`.ifiszero(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)`.ifiszero(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.functiontoCase(stringmemory subject, bool toUpper)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
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)))
ifiszero(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.functionfromSmallString(bytes32 s) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
result :=mload(0x40)
let n :=0for {} 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.functionnormalizeSmallString(bytes32 s) internalpurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
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.functiontoSmallString(stringmemory s) internalpurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
result :=mload(s)
ifiszero(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.functionlower(stringmemory subject) internalpurereturns (stringmemory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string./// WARNING! This function is only compatible with 7-bit ASCII strings.functionupper(stringmemory subject) internalpurereturns (stringmemory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.functionescapeHTML(stringmemory s) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
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 `["\"","'","&","<",">"]`.ifiszero(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.functionescapeJSON(stringmemory s, bool addDoubleQuotes)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
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)
ifiszero(lt(c, 0x20)) {
ifiszero(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
}
ifiszero(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.functionescapeJSON(stringmemory s) internalpurereturns (stringmemory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.functioneq(stringmemory a, stringmemory b) internalpurereturns (bool result) {
/// @solidity memory-safe-assemblyassembly {
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.functioneqs(stringmemory a, bytes32 b) internalpurereturns (bool result) {
/// @solidity memory-safe-assemblyassembly {
// 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.functionpackOne(stringmemory a) internalpurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
// 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.functionunpackOne(bytes32 packed) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
// 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.functionpackTwo(stringmemory a, stringmemory b) internalpurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
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 lengths are valid and within the block gas limit.lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}./// Returns the empty strings if `packed` is `bytes32(0)`./// If `packed` is not an output of {packTwo}, the output behavior is undefined.functionunpackTwo(bytes32 packed)
internalpurereturns (stringmemory resultA, stringmemory resultB)
{
/// @solidity memory-safe-assemblyassembly {
// Grab the free memory pointer.
resultA :=mload(0x40)
resultB :=add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.functiondirectReturn(stringmemory a) internalpure{
assembly {
// Assumes that the string does not start from the scratch space.let retStart :=sub(a, 0x20)
let retSize :=add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced// by a method that doesn't zero right pad.mstore(add(retStart, retSize), 0)
// Store the return offset.mstore(retStart, 0x20)
// End the transaction, returning the string.return(retStart, retSize)
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^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.abstractcontractOwnable{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The caller is not authorized to call the function.errorUnauthorized();
/// @dev The `newOwner` cannot be the zero address.errorNewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.errorNoHandoverRequest();
/// @dev Cannot double-initialize.errorAlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* 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.eventOwnershipTransferred(addressindexed oldOwner, addressindexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.eventOwnershipHandoverRequested(addressindexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.eventOwnershipHandoverCanceled(addressindexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.uint256privateconstant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.uint256privateconstant _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.bytes32internalconstant _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.uint256privateconstant _HANDOVER_SLOT_SEED =0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Override to return true to make `_initializeOwner` prevent double-initialization.function_guardInitializeOwner() internalpurevirtualreturns (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) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
ifsload(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-assemblyassembly {
// 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) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
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-assemblyassembly {
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() internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// If the caller is not the stored owner, revert.ifiszero(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() internalviewvirtualreturns (uint64) {
return48*3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC UPDATE FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Allows the owner to transfer the ownership to `newOwner`.functiontransferOwnership(address newOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
ifiszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.functionrenounceOwnership() publicpayablevirtualonlyOwner{
_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.functionrequestOwnershipHandover() publicpayablevirtual{
unchecked {
uint256 expires =block.timestamp+ _ownershipHandoverValidFor();
/// @solidity memory-safe-assemblyassembly {
// 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.functioncancelOwnershipHandover() publicpayablevirtual{
/// @solidity memory-safe-assemblyassembly {
// 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`.functioncompleteOwnershipHandover(address pendingOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
// 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.ifgt(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.functionowner() publicviewvirtualreturns (address result) {
/// @solidity memory-safe-assemblyassembly {
result :=sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.functionownershipHandoverExpiresAt(address pendingOwner)
publicviewvirtualreturns (uint256 result)
{
/// @solidity memory-safe-assemblyassembly {
// 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.modifieronlyOwner() virtual{
_checkOwner();
_;
}
}
Contract Source Code
File 7 of 7: OwnableRoles.sol
// SPDX-License-Identifier: MITpragmasolidity ^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.abstractcontractOwnableRolesisOwnable{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EVENTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The `user`'s roles is updated to `roles`./// Each bit of `roles` represents whether the role is set.eventRolesUpdated(addressindexed user, uint256indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.uint256privateconstant _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")))`.uint256privateconstant _ROLE_SLOT_SEED =0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Overwrite the roles directly without authorization guard.function_setRoles(address user, uint256 roles) internalvirtual{
/// @solidity memory-safe-assemblyassembly {
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) internalvirtual{
/// @solidity memory-safe-assemblyassembly {
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.ifiszero(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) internalvirtual{
_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) internalvirtual{
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.function_checkRoles(uint256 roles) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// 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.ifiszero(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) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// If the caller is not the stored owner.// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.ifiszero(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.ifiszero(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) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// 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.ifiszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.ifiszero(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) internalpurereturns (uint256 roles) {
/// @solidity memory-safe-assemblyassembly {
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) internalpurereturns (uint8[] memory ordinals) {
/// @solidity memory-safe-assemblyassembly {
// 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)
ifiszero(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.functiongrantRoles(address user, uint256 roles) publicpayablevirtualonlyOwner{
_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.functionrevokeRoles(address user, uint256 roles) publicpayablevirtualonlyOwner{
_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.functionrenounceRoles(uint256 roles) publicpayablevirtual{
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC READ FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the roles of `user`.functionrolesOf(address user) publicviewvirtualreturns (uint256 roles) {
/// @solidity memory-safe-assemblyassembly {
// 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`.functionhasAnyRole(address user, uint256 roles) publicviewvirtualreturns (bool) {
return rolesOf(user) & roles !=0;
}
/// @dev Returns whether `user` has all of `roles`.functionhasAllRoles(address user, uint256 roles) publicviewvirtualreturns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MODIFIERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Marks a function as only callable by an account with `roles`.modifieronlyRoles(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.modifieronlyOwnerOrRoles(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.modifieronlyRolesOrOwner(uint256 roles) virtual{
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ROLE CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// IYKYKuint256internalconstant _ROLE_0 =1<<0;
uint256internalconstant _ROLE_1 =1<<1;
uint256internalconstant _ROLE_2 =1<<2;
uint256internalconstant _ROLE_3 =1<<3;
uint256internalconstant _ROLE_4 =1<<4;
uint256internalconstant _ROLE_5 =1<<5;
uint256internalconstant _ROLE_6 =1<<6;
uint256internalconstant _ROLE_7 =1<<7;
uint256internalconstant _ROLE_8 =1<<8;
uint256internalconstant _ROLE_9 =1<<9;
uint256internalconstant _ROLE_10 =1<<10;
uint256internalconstant _ROLE_11 =1<<11;
uint256internalconstant _ROLE_12 =1<<12;
uint256internalconstant _ROLE_13 =1<<13;
uint256internalconstant _ROLE_14 =1<<14;
uint256internalconstant _ROLE_15 =1<<15;
uint256internalconstant _ROLE_16 =1<<16;
uint256internalconstant _ROLE_17 =1<<17;
uint256internalconstant _ROLE_18 =1<<18;
uint256internalconstant _ROLE_19 =1<<19;
uint256internalconstant _ROLE_20 =1<<20;
uint256internalconstant _ROLE_21 =1<<21;
uint256internalconstant _ROLE_22 =1<<22;
uint256internalconstant _ROLE_23 =1<<23;
uint256internalconstant _ROLE_24 =1<<24;
uint256internalconstant _ROLE_25 =1<<25;
uint256internalconstant _ROLE_26 =1<<26;
uint256internalconstant _ROLE_27 =1<<27;
uint256internalconstant _ROLE_28 =1<<28;
uint256internalconstant _ROLE_29 =1<<29;
uint256internalconstant _ROLE_30 =1<<30;
uint256internalconstant _ROLE_31 =1<<31;
uint256internalconstant _ROLE_32 =1<<32;
uint256internalconstant _ROLE_33 =1<<33;
uint256internalconstant _ROLE_34 =1<<34;
uint256internalconstant _ROLE_35 =1<<35;
uint256internalconstant _ROLE_36 =1<<36;
uint256internalconstant _ROLE_37 =1<<37;
uint256internalconstant _ROLE_38 =1<<38;
uint256internalconstant _ROLE_39 =1<<39;
uint256internalconstant _ROLE_40 =1<<40;
uint256internalconstant _ROLE_41 =1<<41;
uint256internalconstant _ROLE_42 =1<<42;
uint256internalconstant _ROLE_43 =1<<43;
uint256internalconstant _ROLE_44 =1<<44;
uint256internalconstant _ROLE_45 =1<<45;
uint256internalconstant _ROLE_46 =1<<46;
uint256internalconstant _ROLE_47 =1<<47;
uint256internalconstant _ROLE_48 =1<<48;
uint256internalconstant _ROLE_49 =1<<49;
uint256internalconstant _ROLE_50 =1<<50;
uint256internalconstant _ROLE_51 =1<<51;
uint256internalconstant _ROLE_52 =1<<52;
uint256internalconstant _ROLE_53 =1<<53;
uint256internalconstant _ROLE_54 =1<<54;
uint256internalconstant _ROLE_55 =1<<55;
uint256internalconstant _ROLE_56 =1<<56;
uint256internalconstant _ROLE_57 =1<<57;
uint256internalconstant _ROLE_58 =1<<58;
uint256internalconstant _ROLE_59 =1<<59;
uint256internalconstant _ROLE_60 =1<<60;
uint256internalconstant _ROLE_61 =1<<61;
uint256internalconstant _ROLE_62 =1<<62;
uint256internalconstant _ROLE_63 =1<<63;
uint256internalconstant _ROLE_64 =1<<64;
uint256internalconstant _ROLE_65 =1<<65;
uint256internalconstant _ROLE_66 =1<<66;
uint256internalconstant _ROLE_67 =1<<67;
uint256internalconstant _ROLE_68 =1<<68;
uint256internalconstant _ROLE_69 =1<<69;
uint256internalconstant _ROLE_70 =1<<70;
uint256internalconstant _ROLE_71 =1<<71;
uint256internalconstant _ROLE_72 =1<<72;
uint256internalconstant _ROLE_73 =1<<73;
uint256internalconstant _ROLE_74 =1<<74;
uint256internalconstant _ROLE_75 =1<<75;
uint256internalconstant _ROLE_76 =1<<76;
uint256internalconstant _ROLE_77 =1<<77;
uint256internalconstant _ROLE_78 =1<<78;
uint256internalconstant _ROLE_79 =1<<79;
uint256internalconstant _ROLE_80 =1<<80;
uint256internalconstant _ROLE_81 =1<<81;
uint256internalconstant _ROLE_82 =1<<82;
uint256internalconstant _ROLE_83 =1<<83;
uint256internalconstant _ROLE_84 =1<<84;
uint256internalconstant _ROLE_85 =1<<85;
uint256internalconstant _ROLE_86 =1<<86;
uint256internalconstant _ROLE_87 =1<<87;
uint256internalconstant _ROLE_88 =1<<88;
uint256internalconstant _ROLE_89 =1<<89;
uint256internalconstant _ROLE_90 =1<<90;
uint256internalconstant _ROLE_91 =1<<91;
uint256internalconstant _ROLE_92 =1<<92;
uint256internalconstant _ROLE_93 =1<<93;
uint256internalconstant _ROLE_94 =1<<94;
uint256internalconstant _ROLE_95 =1<<95;
uint256internalconstant _ROLE_96 =1<<96;
uint256internalconstant _ROLE_97 =1<<97;
uint256internalconstant _ROLE_98 =1<<98;
uint256internalconstant _ROLE_99 =1<<99;
uint256internalconstant _ROLE_100 =1<<100;
uint256internalconstant _ROLE_101 =1<<101;
uint256internalconstant _ROLE_102 =1<<102;
uint256internalconstant _ROLE_103 =1<<103;
uint256internalconstant _ROLE_104 =1<<104;
uint256internalconstant _ROLE_105 =1<<105;
uint256internalconstant _ROLE_106 =1<<106;
uint256internalconstant _ROLE_107 =1<<107;
uint256internalconstant _ROLE_108 =1<<108;
uint256internalconstant _ROLE_109 =1<<109;
uint256internalconstant _ROLE_110 =1<<110;
uint256internalconstant _ROLE_111 =1<<111;
uint256internalconstant _ROLE_112 =1<<112;
uint256internalconstant _ROLE_113 =1<<113;
uint256internalconstant _ROLE_114 =1<<114;
uint256internalconstant _ROLE_115 =1<<115;
uint256internalconstant _ROLE_116 =1<<116;
uint256internalconstant _ROLE_117 =1<<117;
uint256internalconstant _ROLE_118 =1<<118;
uint256internalconstant _ROLE_119 =1<<119;
uint256internalconstant _ROLE_120 =1<<120;
uint256internalconstant _ROLE_121 =1<<121;
uint256internalconstant _ROLE_122 =1<<122;
uint256internalconstant _ROLE_123 =1<<123;
uint256internalconstant _ROLE_124 =1<<124;
uint256internalconstant _ROLE_125 =1<<125;
uint256internalconstant _ROLE_126 =1<<126;
uint256internalconstant _ROLE_127 =1<<127;
uint256internalconstant _ROLE_128 =1<<128;
uint256internalconstant _ROLE_129 =1<<129;
uint256internalconstant _ROLE_130 =1<<130;
uint256internalconstant _ROLE_131 =1<<131;
uint256internalconstant _ROLE_132 =1<<132;
uint256internalconstant _ROLE_133 =1<<133;
uint256internalconstant _ROLE_134 =1<<134;
uint256internalconstant _ROLE_135 =1<<135;
uint256internalconstant _ROLE_136 =1<<136;
uint256internalconstant _ROLE_137 =1<<137;
uint256internalconstant _ROLE_138 =1<<138;
uint256internalconstant _ROLE_139 =1<<139;
uint256internalconstant _ROLE_140 =1<<140;
uint256internalconstant _ROLE_141 =1<<141;
uint256internalconstant _ROLE_142 =1<<142;
uint256internalconstant _ROLE_143 =1<<143;
uint256internalconstant _ROLE_144 =1<<144;
uint256internalconstant _ROLE_145 =1<<145;
uint256internalconstant _ROLE_146 =1<<146;
uint256internalconstant _ROLE_147 =1<<147;
uint256internalconstant _ROLE_148 =1<<148;
uint256internalconstant _ROLE_149 =1<<149;
uint256internalconstant _ROLE_150 =1<<150;
uint256internalconstant _ROLE_151 =1<<151;
uint256internalconstant _ROLE_152 =1<<152;
uint256internalconstant _ROLE_153 =1<<153;
uint256internalconstant _ROLE_154 =1<<154;
uint256internalconstant _ROLE_155 =1<<155;
uint256internalconstant _ROLE_156 =1<<156;
uint256internalconstant _ROLE_157 =1<<157;
uint256internalconstant _ROLE_158 =1<<158;
uint256internalconstant _ROLE_159 =1<<159;
uint256internalconstant _ROLE_160 =1<<160;
uint256internalconstant _ROLE_161 =1<<161;
uint256internalconstant _ROLE_162 =1<<162;
uint256internalconstant _ROLE_163 =1<<163;
uint256internalconstant _ROLE_164 =1<<164;
uint256internalconstant _ROLE_165 =1<<165;
uint256internalconstant _ROLE_166 =1<<166;
uint256internalconstant _ROLE_167 =1<<167;
uint256internalconstant _ROLE_168 =1<<168;
uint256internalconstant _ROLE_169 =1<<169;
uint256internalconstant _ROLE_170 =1<<170;
uint256internalconstant _ROLE_171 =1<<171;
uint256internalconstant _ROLE_172 =1<<172;
uint256internalconstant _ROLE_173 =1<<173;
uint256internalconstant _ROLE_174 =1<<174;
uint256internalconstant _ROLE_175 =1<<175;
uint256internalconstant _ROLE_176 =1<<176;
uint256internalconstant _ROLE_177 =1<<177;
uint256internalconstant _ROLE_178 =1<<178;
uint256internalconstant _ROLE_179 =1<<179;
uint256internalconstant _ROLE_180 =1<<180;
uint256internalconstant _ROLE_181 =1<<181;
uint256internalconstant _ROLE_182 =1<<182;
uint256internalconstant _ROLE_183 =1<<183;
uint256internalconstant _ROLE_184 =1<<184;
uint256internalconstant _ROLE_185 =1<<185;
uint256internalconstant _ROLE_186 =1<<186;
uint256internalconstant _ROLE_187 =1<<187;
uint256internalconstant _ROLE_188 =1<<188;
uint256internalconstant _ROLE_189 =1<<189;
uint256internalconstant _ROLE_190 =1<<190;
uint256internalconstant _ROLE_191 =1<<191;
uint256internalconstant _ROLE_192 =1<<192;
uint256internalconstant _ROLE_193 =1<<193;
uint256internalconstant _ROLE_194 =1<<194;
uint256internalconstant _ROLE_195 =1<<195;
uint256internalconstant _ROLE_196 =1<<196;
uint256internalconstant _ROLE_197 =1<<197;
uint256internalconstant _ROLE_198 =1<<198;
uint256internalconstant _ROLE_199 =1<<199;
uint256internalconstant _ROLE_200 =1<<200;
uint256internalconstant _ROLE_201 =1<<201;
uint256internalconstant _ROLE_202 =1<<202;
uint256internalconstant _ROLE_203 =1<<203;
uint256internalconstant _ROLE_204 =1<<204;
uint256internalconstant _ROLE_205 =1<<205;
uint256internalconstant _ROLE_206 =1<<206;
uint256internalconstant _ROLE_207 =1<<207;
uint256internalconstant _ROLE_208 =1<<208;
uint256internalconstant _ROLE_209 =1<<209;
uint256internalconstant _ROLE_210 =1<<210;
uint256internalconstant _ROLE_211 =1<<211;
uint256internalconstant _ROLE_212 =1<<212;
uint256internalconstant _ROLE_213 =1<<213;
uint256internalconstant _ROLE_214 =1<<214;
uint256internalconstant _ROLE_215 =1<<215;
uint256internalconstant _ROLE_216 =1<<216;
uint256internalconstant _ROLE_217 =1<<217;
uint256internalconstant _ROLE_218 =1<<218;
uint256internalconstant _ROLE_219 =1<<219;
uint256internalconstant _ROLE_220 =1<<220;
uint256internalconstant _ROLE_221 =1<<221;
uint256internalconstant _ROLE_222 =1<<222;
uint256internalconstant _ROLE_223 =1<<223;
uint256internalconstant _ROLE_224 =1<<224;
uint256internalconstant _ROLE_225 =1<<225;
uint256internalconstant _ROLE_226 =1<<226;
uint256internalconstant _ROLE_227 =1<<227;
uint256internalconstant _ROLE_228 =1<<228;
uint256internalconstant _ROLE_229 =1<<229;
uint256internalconstant _ROLE_230 =1<<230;
uint256internalconstant _ROLE_231 =1<<231;
uint256internalconstant _ROLE_232 =1<<232;
uint256internalconstant _ROLE_233 =1<<233;
uint256internalconstant _ROLE_234 =1<<234;
uint256internalconstant _ROLE_235 =1<<235;
uint256internalconstant _ROLE_236 =1<<236;
uint256internalconstant _ROLE_237 =1<<237;
uint256internalconstant _ROLE_238 =1<<238;
uint256internalconstant _ROLE_239 =1<<239;
uint256internalconstant _ROLE_240 =1<<240;
uint256internalconstant _ROLE_241 =1<<241;
uint256internalconstant _ROLE_242 =1<<242;
uint256internalconstant _ROLE_243 =1<<243;
uint256internalconstant _ROLE_244 =1<<244;
uint256internalconstant _ROLE_245 =1<<245;
uint256internalconstant _ROLE_246 =1<<246;
uint256internalconstant _ROLE_247 =1<<247;
uint256internalconstant _ROLE_248 =1<<248;
uint256internalconstant _ROLE_249 =1<<249;
uint256internalconstant _ROLE_250 =1<<250;
uint256internalconstant _ROLE_251 =1<<251;
uint256internalconstant _ROLE_252 =1<<252;
uint256internalconstant _ROLE_253 =1<<253;
uint256internalconstant _ROLE_254 =1<<254;
uint256internalconstant _ROLE_255 =1<<255;
}