// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)pragmasolidity ^0.8.20;import {IAccessControl} from"./IAccessControl.sol";
import {Context} from"../utils/Context.sol";
import {ERC165} from"../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/abstractcontractAccessControlisContext, IAccessControl, ERC165{
structRoleData {
mapping(address account =>bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32publicconstant DEFAULT_ADMIN_ROLE =0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/modifieronlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IAccessControl).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) publicviewvirtualreturns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/function_checkRole(bytes32 role) internalviewvirtual{
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/function_checkRole(bytes32 role, address account) internalviewvirtual{
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) publicviewvirtualreturns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/functiongrantRole(bytes32 role, address account) publicvirtualonlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/functionrevokeRole(bytes32 role, address account) publicvirtualonlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/functionrenounceRole(bytes32 role, address callerConfirmation) publicvirtual{
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/function_setRoleAdmin(bytes32 role, bytes32 adminRole) internalvirtual{
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/function_grantRole(bytes32 role, address account) internalvirtualreturns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] =true;
emit RoleGranted(role, account, _msgSender());
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/function_revokeRole(bytes32 role, address account) internalvirtualreturns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] =false;
emit RoleRevoked(role, account, _msgSender());
returntrue;
} else {
returnfalse;
}
}
}
Contract Source Code
File 2 of 30: Address.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)pragmasolidity ^0.8.20;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/errorAddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/errorAddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/errorFailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
if (address(this).balance< amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
if (address(this).balance< value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata
) internalviewreturns (bytesmemory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty// otherwise we already know that it was a contractif (returndata.length==0&& target.code.length==0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/functionverifyCallResult(bool success, bytesmemory returndata) internalpurereturns (bytesmemory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/function_revert(bytesmemory returndata) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
Contract Source Code
File 3 of 30: Base64.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Library to encode strings in Base64./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <brecht@loopring.org>.libraryBase64{
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// See: https://datatracker.ietf.org/doc/html/rfc4648/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'./// @param noPadding Whether to strip away the padding.functionencode(bytesmemory data, bool fileSafe, bool noPadding)
internalpurereturns (stringmemory result)
{
/// @solidity memory-safe-assemblyassembly {
let dataLength :=mload(data)
if dataLength {
// Multiply by 4/3 rounded up.// The `shl(2, ...)` is equivalent to multiplying by 4.let encodedLength :=shl(2, div(add(dataLength, 2), 3))
// Set `result` to point to the start of the free memory.
result :=mload(0x40)
// Store the table into the scratch space.// Offsetted by -1 byte so that the `mload` will load the character.// We will rewrite the free memory pointer at `0x40` later with// the allocated size.// The magic constant 0x0670 will turn "-_" into "+/".mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
// Skip the first slot, which stores the length.let ptr :=add(result, 0x20)
let end :=add(ptr, encodedLength)
// Run over the input, 3 bytes at a time.for {} 1 {} {
data :=add(data, 3) // Advance 3 bytes.let input :=mload(data)
// Write 4 bytes. Optimized for fewer stack operations.mstore8(0, mload(and(shr(18, input), 0x3F)))
mstore8(1, mload(and(shr(12, input), 0x3F)))
mstore8(2, mload(and(shr(6, input), 0x3F)))
mstore8(3, mload(and(input, 0x3F)))
mstore(ptr, mload(0x00))
ptr :=add(ptr, 4) // Advance 4 bytes.ifiszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.// Equivalent to `o = [0, 2, 1][dataLength % 3]`.let o :=div(2, mod(dataLength, 3))
// Offset `ptr` and pad with '='. We can simply write over the end.mstore(sub(ptr, o), shl(240, 0x3d3d))
// Set `o` to zero if there is padding.
o :=mul(iszero(iszero(noPadding)), o)
mstore(sub(ptr, o), 0) // Zeroize the slot after the string.mstore(result, sub(encodedLength, o)) // Store the length.
}
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// Equivalent to `encode(data, false, false)`.functionencode(bytesmemory data) internalpurereturns (stringmemory result) {
result = encode(data, false, false);
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648./// Equivalent to `encode(data, fileSafe, false)`.functionencode(bytesmemory data, bool fileSafe)
internalpurereturns (stringmemory result)
{
result = encode(data, fileSafe, false);
}
/// @dev Decodes base64 encoded `data`.////// Supports:/// - RFC 4648 (both standard and file-safe mode)./// - RFC 3501 (63: ',').////// Does not support:/// - Line breaks.////// Note: For performance reasons,/// this function will NOT revert on invalid `data` inputs./// Outputs for invalid inputs will simply be undefined behaviour./// It is the user's responsibility to ensure that the `data`/// is a valid base64 encoded string.functiondecode(stringmemory data) internalpurereturns (bytesmemory result) {
/// @solidity memory-safe-assemblyassembly {
let dataLength :=mload(data)
if dataLength {
let decodedLength :=mul(shr(2, dataLength), 3)
for {} 1 {} {
// If padded.ifiszero(and(dataLength, 3)) {
let t :=xor(mload(add(data, dataLength)), 0x3d3d)
// forgefmt: disable-next-item
decodedLength :=sub(
decodedLength,
add(iszero(byte(30, t)), iszero(byte(31, t)))
)
break
}
// If non-padded.
decodedLength :=add(decodedLength, sub(and(dataLength, 3), 1))
break
}
result :=mload(0x40)
// Write the length of the bytes.mstore(result, decodedLength)
// Skip the first slot, which stores the length.let ptr :=add(result, 0x20)
let end :=add(ptr, decodedLength)
// Load the table into the scratch space.// Constants are optimized for smaller bytecode with zero gas overhead.// `m` also doubles as the mask of the upper 6 bits.let m :=0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8ccmstore(0x5b, m)
mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
for {} 1 {} {
// Read 4 bytes.
data :=add(data, 4)
let input :=mload(data)
// Write 3 bytes.// forgefmt: disable-next-itemmstore(ptr, or(
and(m, mload(byte(28, input))),
shr(6, or(
and(m, mload(byte(29, input))),
shr(6, or(
and(m, mload(byte(30, input))),
shr(6, mload(byte(31, input)))
))
))
))
ptr :=add(ptr, 3)
ifiszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.mstore(end, 0) // Zeroize the slot after the bytes.mstore(0x60, 0) // Restore the zero slot.
}
}
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^0.8.20;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)pragmasolidity ^0.8.20;import {IERC165} from"./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualreturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 8 of 30: ERC721Psi.sol
// SPDX-License-Identifier: MIT/**
______ _____ _____ ______ ___ __ _ _ _
| ____| __ \ / ____|____ |__ \/_ | || || |
| |__ | |__) | | / / ) || | \| |/ |
| __| | _ /| | / / / / | |\_ _/
| |____| | \ \| |____ / / / /_ | | | |
|______|_| \_\\_____|/_/ |____||_| |_|
- github: https://github.com/estarriolvetch/ERC721Psi
- npm: https://www.npmjs.com/package/erc721psi
*/pragmasolidity ^0.8.0;import"@openzeppelin/contracts/utils/Strings.sol";
import"@openzeppelin/contracts/utils/Address.sol";
import"solady/src/utils/LibBitmap.sol";
import"./interface/IERC721Psi.sol";
/**
* @dev Interface of ERC721 token receiver.
*/interfaceERC721Psi__IERC721Receiver{
functiononERC721Received(address operator,
addressfrom,
uint256 tokenId,
bytescalldata data
) externalreturns (bytes4);
}
contractERC721PsiisIERC721Psi{
usingAddressforaddress;
usingStringsforuint256;
usingLibBitmapforLibBitmap.Bitmap;
LibBitmap.Bitmap private _batchHead;
stringprivate _name;
stringprivate _symbol;
// Mapping from token ID to owner addressmapping(uint256=>address) internal _owners;
uint256internal _currentIndex;
mapping(uint256=>address) private _tokenApprovals;
mapping(address=>mapping(address=>bool)) private _operatorApprovals;
// The mask of the lower 160 bits for addresses.uint256privateconstant _BITMASK_ADDRESS = (1<<160) -1;
// The `Transfer` event signature is given by:// `keccak256(bytes("Transfer(address,address,uint256)"))`.bytes32privateconstant _TRANSFER_EVENT_SIGNATURE =0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
}
/**
* @dev Returns the starting token ID.
* To change the starting token ID, please override this function.
*/function_startTokenId() internalviewvirtualreturns (uint256) {
return0;
}
/**
* @dev Returns the next token ID to be minted.
*/function_nextTokenId() internalviewvirtualreturns (uint256) {
return _currentIndex;
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/function_totalMinted() internalviewvirtualreturns (uint256) {
return _currentIndex - _startTokenId();
}
/**
* @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 ==0x80ac58cd||// ERC165 interface ID for ERC721.
interfaceId ==0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
}
/**
* @dev See {IERC721-balanceOf}.
*/functionbalanceOf(address owner)
publicviewvirtualoverridereturns (uint)
{
if(owner ==address(0)) revert BalanceQueryForZeroAddress();
uint count;
for( uint i = _startTokenId(); i < _nextTokenId(); ++i ){
if(_exists(i)){
if( owner == ownerOf(i)){
++count;
}
}
}
return count;
}
/**
* @dev See {IERC721-ownerOf}.
*/functionownerOf(uint256 tokenId)
publicviewvirtualoverridereturns (address)
{
(address owner, ) = _ownerAndBatchHeadOf(tokenId);
return owner;
}
function_ownerAndBatchHeadOf(uint256 tokenId) internalviewreturns (address owner, uint256 tokenIdBatchHead){
if (!_exists(tokenId)) revert OwnerQueryForNonexistentToken();
tokenIdBatchHead = _getBatchHead(tokenId);
owner = _owners[tokenIdBatchHead];
}
/**
* @dev See {IERC721Metadata-name}.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/functiontokenURI(uint256 tokenId) publicviewvirtualoverridereturns (stringmemory) {
if( !_exists(tokenId)) revert URIQueryForNonexistentToken();
stringmemory baseURI = _baseURI();
returnbytes(baseURI).length>0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
*/function_baseURI() internalviewvirtualreturns (stringmemory) {
return"";
}
/**
* @dev See {IERC721-approve}.
*/functionapprove(address to, uint256 tokenId) publicpayablevirtualoverride{
address owner = ownerOf(tokenId);
if (_msgSenderERC721Psi() != owner) {
if (!isApprovedForAll(owner, _msgSenderERC721Psi())) {
revert ApprovalCallerNotOwnerNorApproved();
}
}
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/functiongetApproved(uint256 tokenId)
publicviewvirtualoverridereturns (address)
{
if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/functionsetApprovalForAll(address operator, bool approved)
publicvirtualoverride{
_operatorApprovals[_msgSenderERC721Psi()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721Psi(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/functionisApprovedForAll(address owner, address operator)
publicviewvirtualoverridereturns (bool)
{
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) publicpayablevirtualoverride{
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) publicpayablevirtualoverride{
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) publicpayablevirtualoverride{
_safeTransfer(from, to, tokenId, _data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `_data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/function_safeTransfer(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) internalvirtual{
_transfer(from, to, tokenId);
if (!_checkOnERC721Received(from, to, tokenId, 1, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`).
*/function_exists(uint256 tokenId) internalviewvirtualreturns (bool) {
return tokenId < _nextTokenId() && _startTokenId() <= tokenId;
}
errorOperatorQueryForNonexistentToken();
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/function_isApprovedOrOwner(address spender, uint256 tokenId)
internalviewvirtualreturns (bool)
{
if (!_exists(tokenId)) revert OperatorQueryForNonexistentToken();
address owner = ownerOf(tokenId);
return (spender == owner ||
getApproved(tokenId) == spender ||
isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event.
*/function_safeMint(address to, uint256 quantity) internalvirtual{
_safeMint(to, quantity, "");
}
function_safeMint(address to,
uint256 quantity,
bytesmemory _data
) internalvirtual{
_mint(to, quantity);
uint256 end = _currentIndex;
if (!_checkOnERC721Received(address(0), to, end - quantity, quantity, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
// Reentrancy protection.if (_currentIndex != end) revert();
}
function_mint(address to,
uint256 quantity
) internalvirtual{
uint256 nextTokenId = _nextTokenId();
if (quantity ==0) revert MintZeroQuantity();
if (to ==address(0)) revert MintToZeroAddress();
_beforeTokenTransfers(address(0), to, nextTokenId, quantity);
_currentIndex += quantity;
_owners[nextTokenId] = to;
_batchHead.set(nextTokenId);
uint256 toMasked;
uint256 end = nextTokenId + quantity;
// Use assembly to loop and emit the `Transfer` event for gas savings.// The duplicated `log4` removes an extra check and reduces stack juggling.// The assembly, together with the surrounding Solidity code, have been// delicately arranged to nudge the compiler into producing optimized opcodes.assembly {
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
toMasked :=and(to, _BITMASK_ADDRESS)
// Emit the `Transfer` event.log4(
0, // Start of data (0, since no data).0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.0, // `address(0)`.
toMasked, // `to`.
nextTokenId // `tokenId`.
)
// The `iszero(eq(,))` check ensures that large values of `quantity`// that overflows uint256 will make the loop run out of gas.// The compiler will optimize the `iszero` away for performance.for {
let tokenId :=add(nextTokenId, 1)
} iszero(eq(tokenId, end)) {
tokenId :=add(tokenId, 1)
} {
// Emit the `Transfer` event. Similar to above.log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
}
}
_afterTokenTransfers(address(0), to, nextTokenId, quantity);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/function_transfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{
(address owner, uint256 tokenIdBatchHead) = _ownerAndBatchHeadOf(tokenId);
if (owner !=from) revert TransferFromIncorrectOwner();
if (!_isApprovedOrOwner(_msgSenderERC721Psi(), tokenId)) {
revert TransferCallerNotOwnerNorApproved();
}
if (to ==address(0)) revert TransferToZeroAddress();
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
uint256 subsequentTokenId = tokenId +1;
if(!_batchHead.get(subsequentTokenId) &&
subsequentTokenId < _nextTokenId()
) {
_owners[subsequentTokenId] =from;
_batchHead.set(subsequentTokenId);
}
_owners[tokenId] = to;
if(tokenId != tokenIdBatchHead) {
_batchHead.set(tokenId);
}
emit Transfer(from, to, tokenId);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits a {Approval} event.
*/function_approve(address to, uint256 tokenId) internalvirtual{
_tokenApprovals[tokenId] = to;
emit Approval(ownerOf(tokenId), to, tokenId);
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param startTokenId uint256 the first ID of the tokens to be transferred
* @param quantity uint256 amount of the tokens to be transfered.
* @param _data bytes optional data to send along with the call
* @return r bool whether the call correctly returned the expected magic value
*/function_checkOnERC721Received(addressfrom,
address to,
uint256 startTokenId,
uint256 quantity,
bytesmemory _data
) privatereturns (bool r) {
/// @dev removed isContract() in v5.0 but their ERC721 uses this check:if (to.code.length>0) {
r =true;
for(uint256 tokenId = startTokenId; tokenId < startTokenId + quantity; tokenId++){
try ERC721Psi__IERC721Receiver(to).onERC721Received( _msgSenderERC721Psi(), from, tokenId, _data) returns (bytes4 retval) {
r = r && retval == ERC721Psi__IERC721Receiver.onERC721Received.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
revert TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
return r;
} else {
returntrue;
}
}
function_getBatchHead(uint256 tokenId) internalviewreturns (uint256 tokenIdBatchHead) {
tokenIdBatchHead = _batchHead.findLastSet(tokenId);
}
functiontotalSupply() publicvirtualoverrideviewreturns (uint256) {
return _totalMinted();
}
/**
* @dev Returns an array of token IDs owned by `owner`.
*
* This function scans the ownership mapping and is O(`totalSupply`) in complexity.
* It is meant to be called off-chain.
*
* This function is compatiable with ERC721AQueryable.
*/functiontokensOfOwner(address owner) externalviewvirtualreturns (uint256[] memory) {
unchecked {
uint256 tokenIdsIdx;
uint256 tokenIdsLength = balanceOf(owner);
uint256[] memory tokenIds =newuint256[](tokenIdsLength);
for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
if (_exists(i)) {
if (ownerOf(i) == owner) {
tokenIds[tokenIdsIdx++] = i;
}
}
}
return tokenIds;
}
}
/**
* @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
*/function_beforeTokenTransfers(addressfrom,
address to,
uint256 startTokenId,
uint256 quantity
) internalvirtual{}
/**
* @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
* minting.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero.
* - `from` and `to` are never both zero.
*/function_afterTokenTransfers(addressfrom,
address to,
uint256 startTokenId,
uint256 quantity
) internalvirtual{}
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/function_msgSenderERC721Psi() internalviewvirtualreturns (address) {
returnmsg.sender;
}
}
Contract Source Code
File 9 of 30: ERC721PsiBurnable.sol
// SPDX-License-Identifier: MIT/**
______ _____ _____ ______ ___ __ _ _ _
| ____| __ \ / ____|____ |__ \/_ | || || |
| |__ | |__) | | / / ) || | \| |/ |
| __| | _ /| | / / / / | |\_ _/
| |____| | \ \| |____ / / / /_ | | | |
|______|_| \_\\_____|/_/ |____||_| |_|
*/pragmasolidity ^0.8.0;import"solady/src/utils/LibBitmap.sol";
import"../ERC721Psi.sol";
abstractcontractERC721PsiBurnableisERC721Psi{
usingLibBitmapforLibBitmap.Bitmap;
LibBitmap.Bitmap private _burnedToken;
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/function_burn(uint256 tokenId) internalvirtual{
addressfrom= ownerOf(tokenId);
_beforeTokenTransfers(from, address(0), tokenId, 1);
_burnedToken.set(tokenId);
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/function_exists(uint256 tokenId) internalviewoverridevirtualreturns (bool){
if(_burnedToken.get(tokenId)) {
returnfalse;
}
returnsuper._exists(tokenId);
}
/**
* @dev See {IERC721-ownerOf}.
*/functionownerOf(uint256 tokenId)
publicviewvirtualoverridereturns (address)
{
if (_burnedToken.get(tokenId)) {
returnaddress(0);
}
else {
returnsuper.ownerOf(tokenId);
}
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/functiontotalSupply() publicviewvirtualoverridereturns (uint256) {
return _currentIndex - _burned() - _startTokenId();
}
/**
* @dev Returns number of token burned.
*/function_burned() internalviewreturns (uint256 burned){
return _burnedToken.popCount( _startTokenId(), _totalMinted());
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)pragmasolidity ^0.8.20;/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/interfaceIAccessControl{
/**
* @dev The `account` is missing a role.
*/errorAccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/errorAccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/eventRoleAdminChanged(bytes32indexed role, bytes32indexed previousAdminRole, bytes32indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/eventRoleGranted(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/eventRoleRevoked(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) externalviewreturns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) externalviewreturns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functiongrantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionrevokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/functionrenounceRole(bytes32 role, address callerConfirmation) external;
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 16 of 30: IERC721Psi.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Interface of ERC721Psi.
*/interfaceIERC721Psi{
/**
* The caller must own the token or be an approved operator.
*/errorApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/errorApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/errorBalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/errorMintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/errorMintZeroQuantity();
/**
* The token does not exist.
*/errorOwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/errorTransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/errorTransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/errorTransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/errorTransferToZeroAddress();
/**
* The token does not exist.
*/errorURIQueryForNonexistentToken();
// =============================================================// TOKEN COUNTERS// =============================================================/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/functiontotalSupply() externalviewreturns (uint256);
// =============================================================// IERC165// =============================================================/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
// =============================================================// IERC721// =============================================================/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/eventApproval(addressindexed owner, addressindexed approved, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables
* (`approved`) `operator` to manage all of its assets.
*/eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/**
* @dev Returns the number of tokens in `owner`'s account.
*/functionbalanceOf(address owner) externalviewreturns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functionownerOf(uint256 tokenId) externalviewreturns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`,
* checking first that contract recipients are aware of the ERC721 protocol
* to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move
* this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytescalldata data
) externalpayable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) externalpayable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) externalpayable;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/functionapprove(address to, uint256 tokenId) externalpayable;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/functionsetApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functiongetApproved(uint256 tokenId) externalviewreturns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/functionisApprovedForAll(address owner, address operator) externalviewreturns (bool);
// =============================================================// IERC721Metadata// =============================================================/**
* @dev Returns the token collection name.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the token collection symbol.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/functiontokenURI(uint256 tokenId) externalviewreturns (stringmemory);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Library for parsing JSONs./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)libraryJSONParserLib{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The input is invalid.errorParsingFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// There are 6 types of variables in JSON (excluding undefined)./// @dev For denoting that an item has not been initialized./// A item returned from `parse` will never be of an undefined type./// Parsing a invalid JSON string will simply revert.uint8internalconstant TYPE_UNDEFINED =0;
/// @dev Type representing an array (e.g. `[1,2,3]`).uint8internalconstant TYPE_ARRAY =1;
/// @dev Type representing an object (e.g. `{"a":"A","b":"B"}`).uint8internalconstant TYPE_OBJECT =2;
/// @dev Type representing a number (e.g. `-1.23e+21`).uint8internalconstant TYPE_NUMBER =3;
/// @dev Type representing a string (e.g. `"hello"`).uint8internalconstant TYPE_STRING =4;
/// @dev Type representing a boolean (i.e. `true` or `false`).uint8internalconstant TYPE_BOOLEAN =5;
/// @dev Type representing null (i.e. `null`).uint8internalconstant TYPE_NULL =6;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STRUCTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev A pointer to a parsed JSON node.structItem {
// Do NOT modify the `_data` directly.uint256 _data;
}
// Private constants for packing `_data`.uint256privateconstant _BITPOS_STRING =32*7-8;
uint256privateconstant _BITPOS_KEY_LENGTH =32*6-8;
uint256privateconstant _BITPOS_KEY =32*5-8;
uint256privateconstant _BITPOS_VALUE_LENGTH =32*4-8;
uint256privateconstant _BITPOS_VALUE =32*3-8;
uint256privateconstant _BITPOS_CHILD =32*2-8;
uint256privateconstant _BITPOS_SIBLING_OR_PARENT =32*1-8;
uint256privateconstant _BITMASK_POINTER =0xffffffff;
uint256privateconstant _BITMASK_TYPE =7;
uint256privateconstant _KEY_INITED =1<<3;
uint256privateconstant _VALUE_INITED =1<<4;
uint256privateconstant _CHILDREN_INITED =1<<5;
uint256privateconstant _PARENT_IS_ARRAY =1<<6;
uint256privateconstant _PARENT_IS_OBJECT =1<<7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* JSON PARSING OPERATION *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Parses the JSON string `s`, and returns the root./// Reverts if `s` is not a valid JSON as specified in RFC 8259./// Object items WILL simply contain all their children, inclusive of repeated keys,/// in the same order which they appear in the JSON string.////// Note: For efficiency, this function WILL NOT make a copy of `s`./// The parsed tree WILL contain offsets to `s`./// Do NOT pass in a string that WILL be modified later on.functionparse(stringmemory s) internalpurereturns (Item memory result) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x40, result) // We will use our own allocation instead.
}
bytes32 r = _query(_toInput(s), 255);
/// @solidity memory-safe-assemblyassembly {
result := r
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* JSON ITEM OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// Note:// - An item is a node in the JSON tree.// - The value of a string item WILL be double-quoted, JSON encoded.// - We make a distinction between `index` and `key`.// - Items in arrays are located by `index` (uint256).// - Items in objects are located by `key` (string).// - Keys are always strings, double-quoted, JSON encoded.//// These design choices are made to balance between efficiency and ease-of-use./// @dev Returns the string value of the item./// This is its exact string representation in the original JSON string./// The returned string WILL have leading and trailing whitespace trimmed./// All inner whitespace WILL be preserved, exactly as it is in the original JSON string./// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.////// Note: This function lazily instantiates and caches the returned string./// Do NOT modify the returned string.functionvalue(Item memory item) internalpurereturns (stringmemory result) {
bytes32 r = _query(_toInput(item), 0);
/// @solidity memory-safe-assemblyassembly {
result := r
}
}
/// @dev Returns the index of the item in the array./// It the item's parent is not an array, returns 0.functionindex(Item memory item) internalpurereturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly {
ifand(mload(item), _PARENT_IS_ARRAY) {
result :=and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))
}
}
}
/// @dev Returns the key of the item in the object./// It the item's parent is not an object, returns an empty string./// The returned string WILL be double-quoted, JSON encoded.////// Note: This function lazily instantiates and caches the returned string./// Do NOT modify the returned string.functionkey(Item memory item) internalpurereturns (stringmemory result) {
if (item._data & _PARENT_IS_OBJECT !=0) {
bytes32 r = _query(_toInput(item), 1);
/// @solidity memory-safe-assemblyassembly {
result := r
}
}
}
/// @dev Returns the key of the item in the object./// It the item is neither an array nor object, returns an empty array.////// Note: This function lazily instantiates and caches the returned array./// Do NOT modify the returned array.functionchildren(Item memory item) internalpurereturns (Item[] memory result) {
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assemblyassembly {
result := r
}
}
/// @dev Returns the number of children./// It the item is neither an array nor object, returns zero.functionsize(Item memory item) internalpurereturns (uint256 result) {
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assemblyassembly {
result :=mload(r)
}
}
/// @dev Returns the item at index `i` for (array)./// If `item` is not an array, the result's type WILL be undefined./// If there is no item with the index, the result's type WILL be undefined.functionat(Item memory item, uint256 i) internalpurereturns (Item memory result) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x40, result) // Free the default allocation. We'll allocate manually.
}
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assemblyassembly {
result :=mload(add(add(r, 0x20), shl(5, i)))
ifiszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {
result :=0x60// Reset to the zero pointer.
}
}
}
/// @dev Returns the item at key `k` for (object)./// If `item` is not an object, the result's type WILL be undefined./// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons./// - Correct : `item.at('"k"')`./// - Wrong : `item.at("k")`./// For duplicated keys, the last item with the key WILL be returned./// If there is no item with the key, the result's type WILL be undefined.functionat(Item memory item, stringmemory k) internalpurereturns (Item memory result) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x40, result) // Free the default allocation. We'll allocate manually.
result :=0x60// Initialize to the zero pointer.
}
if (isObject(item)) {
bytes32 kHash =keccak256(bytes(k));
Item[] memory r = children(item);
// We'll just do a linear search. The alternatives are very bloated.for (uint256 i = r.length<<5; i !=0;) {
/// @solidity memory-safe-assemblyassembly {
item :=mload(add(r, i))
i :=sub(i, 0x20)
}
if (keccak256(bytes(key(item))) != kHash) continue;
result = item;
break;
}
}
}
/// @dev Returns the item's type.functiongetType(Item memory item) internalpurereturns (uint8 result) {
result =uint8(item._data & _BITMASK_TYPE);
}
/// Note: All types are mutually exclusive./// @dev Returns whether the item is of type undefined.functionisUndefined(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;
}
/// @dev Returns whether the item is of type array.functionisArray(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_ARRAY;
}
/// @dev Returns whether the item is of type object.functionisObject(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_OBJECT;
}
/// @dev Returns whether the item is of type number.functionisNumber(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_NUMBER;
}
/// @dev Returns whether the item is of type string.functionisString(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_STRING;
}
/// @dev Returns whether the item is of type boolean.functionisBoolean(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;
}
/// @dev Returns whether the item is of type null.functionisNull(Item memory item) internalpurereturns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_NULL;
}
/// @dev Returns the item's parent./// If the item does not have a parent, the result's type will be undefined.functionparent(Item memory item) internalpurereturns (Item memory result) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x40, result) // Free the default allocation. We've already allocated.
result :=and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)
ifiszero(result) { result :=0x60 } // Reset to the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* UTILITY FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10)./// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,/// or if the parsed number is too big for a uint256.functionparseUint(stringmemory s) internalpurereturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly {
let n :=mload(s)
let preMulOverflowThres :=div(not(0), 10)
for { let i :=0 } 1 {} {
i :=add(i, 1)
let digit :=sub(and(mload(add(s, i)), 0xff), 48)
let mulOverflowed :=gt(result, preMulOverflowThres)
let product :=mul(10, result)
result :=add(product, digit)
n :=mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))
ifiszero(lt(i, n)) { break }
}
ifiszero(n) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Parses a signed integer from a string (in decimal, i.e. base 10)./// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,/// or if the parsed number is too big for a int256.functionparseInt(stringmemory s) internalpurereturns (int256 result) {
uint256 n =bytes(s).length;
uint256 sign;
uint256 isNegative;
/// @solidity memory-safe-assemblyassembly {
if n {
let c :=and(mload(add(s, 1)), 0xff)
isNegative :=eq(c, 45)
ifor(eq(c, 43), isNegative) {
sign := c
s :=add(s, 1)
mstore(s, sub(n, 1))
}
ifiszero(or(sign, lt(sub(c, 48), 10))) { s :=0x60 }
}
}
uint256 x = parseUint(s);
/// @solidity memory-safe-assemblyassembly {
ifshr(255, x) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.revert(0x1c, 0x04)
}
if sign {
mstore(s, sign)
s :=sub(s, 1)
mstore(s, n)
}
result :=xor(x, mul(xor(x, add(not(x), 1)), isNegative))
}
}
/// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16)./// Reverts if `s` is not a valid uint256 hex string matching the RegEx/// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.functionparseUintFromHex(stringmemory s) internalpurereturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly {
let n :=mload(s)
// Skip two if starts with '0x' or '0X'.let i :=shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))
for {} 1 {} {
i :=add(i, 1)
let c :=byte(
and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),
0x3010a071000000b0104040208000c05090d060e0f
)
n :=mul(n, iszero(or(iszero(c), shr(252, result))))
result :=add(shl(4, result), sub(c, 1))
ifiszero(lt(i, n)) { break }
}
ifiszero(n) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Decodes a JSON encoded string./// The string MUST be double-quoted, JSON encoded./// Reverts if the string is invalid./// As you can see, it's pretty complex for a deceptively simple looking task.functiondecodeString(stringmemory s) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
functionfail() {
mstore(0x00, 0x10182796) // `ParsingFailed()`.revert(0x1c, 0x04)
}
functiondecodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut{
_pOut := add(pIn_, 4)
let b_ := iszero(gt(_pOut, end_))
let t_ := mload(pIn_) // Load the whole word.for { let i_ :=0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {
let c_ := sub(byte(i_, t_), 48)
if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.
c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))
_unicode := add(shl(4, _unicode), c_)
}
}
functiondecodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut{
_unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)
if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {
let t_ := mload(_pOut) // Load the whole word.
end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\u'.
t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)
_unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))
}
}
functionappendCodePointAsUTF8(pIn_, c_) -> _pOut{
if iszero(gt(c_, 0x7f)) {
mstore8(pIn_, c_)
_pOut := add(pIn_, 1)
leave
}
mstore8(0x1f, c_)
mstore8(0x1e, shr(6, c_))
if iszero(gt(c_, 0x7ff)) {
mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))
_pOut := add(pIn_, 2)
leave
}
mstore8(0x1d, shr(12, c_))
if iszero(gt(c_, 0xffff)) {
mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))
_pOut := add(pIn_, 3)
leave
}
mstore8(0x1c, shr(18, c_))
mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))
_pOut := add(pIn_, shl(2, lt(c_, 0x110000)))
}
functionchr(p_) -> _c{
_c :=byte(0, mload(p_))
}
let n := mload(s)
let end := add(add(s, n), 0x1f)
if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {
fail() // Fail if not double-quoted.
}
let out := add(mload(0x40), 0x20)
for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {
let c := chr(curr)
curr := add(curr, 1)
// Not '\\'.if iszero(eq(c, 92)) {
// Not '"'.if iszero(eq(c, 34)) {
mstore8(out, c)
out := add(out, 1)
continue
}
curr := end
}
if iszero(eq(curr, end)) {
let escape := chr(curr)
curr := add(curr, 1)
// '"', '/', '\\'.if and(shr(escape, 0x100000000000800400000000), 1) {
mstore8(out, escape)
out := add(out, 1)
continue
}
// 'u'.if eq(escape, 117) {
escape, curr := decodeUnicodeCodePoint(curr, end)
out := appendCodePointAsUTF8(out, escape)
continue
}
// `{'b':'\b', 'f':'\f', 'n':'\n', 'r':'\r', 't':'\t'}`.
escape :=byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)
if escape {
mstore8(out, escape)
out := add(out, 1)
continue
}
}
fail()
break
}
mstore(out, 0) // Zeroize the last slot.
result := mload(0x40)
mstore(result, sub(out, add(result, 0x20))) // Store the length.
mstore(0x40, add(out, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PRIVATE HELPERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Performs a query on the input with the given mode.function_query(bytes32 input, uint256 mode) privatepurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
functionfail() {
mstore(0x00, 0x10182796) // `ParsingFailed()`.revert(0x1c, 0x04)
}
functionchr(p_) -> _c{
_c :=byte(0, mload(p_))
}
functionskipWhitespace(pIn_, end_) -> _pOut{
for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {
if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \n\r\t'.
}
}
functionsetP(packed_, bitpos_, p_) -> _packed{
// Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.
returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))
_packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))
}
functiongetP(packed_, bitpos_) -> _p{
_p := and(_BITMASK_POINTER, shr(bitpos_, packed_))
}
functionmallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item{
_item := mload(0x40)
// forgefmt: disable-next-item
packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),
_BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))
mstore(_item, or(packed_, type_))
mstore(0x40, add(_item, 0x20)) // Allocate memory.
}
functionparseValue(s_, sibling_, pIn_, end_) -> _item, _pOut{
let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)
_pOut := skipWhitespace(pIn_, end_)
if iszero(lt(_pOut, end_)) { leave }
for { let c_ := chr(_pOut) } 1 {} {
// If starts with '"'.if eq(c_, 34) {
let pStart_ := _pOut
_pOut := parseStringSub(s_, packed_, _pOut, end_)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)
break
}
// If starts with '['.if eq(c_, 91) {
_item, _pOut := parseArray(s_, packed_, _pOut, end_)
break
}
// If starts with '{'.if eq(c_, 123) {
_item, _pOut := parseObject(s_, packed_, _pOut, end_)
break
}
// If starts with any in '0123456789-'.if and(shr(c_, shl(45, 0x1ff9)), 1) {
_item, _pOut := parseNumber(s_, packed_, _pOut, end_)
break
}
if iszero(gt(add(_pOut, 4), end_)) {
let pStart_ := _pOut
let w_ := shr(224, mload(_pOut))
// 'true' in hex format.if eq(w_, 0x74727565) {
_pOut := add(_pOut, 4)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)
break
}
// 'null' in hex format.if eq(w_, 0x6e756c6c) {
_pOut := add(_pOut, 4)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)
break
}
}
if iszero(gt(add(_pOut, 5), end_)) {
let pStart_ := _pOut
let w_ := shr(216, mload(_pOut))
// 'false' in hex format.if eq(w_, 0x66616c7365) {
_pOut := add(_pOut, 5)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)
break
}
}
fail()
break
}
_pOut := skipWhitespace(_pOut, end_)
}
functionparseArray(s_, packed_, pIn_, end_) -> _item, _pOut{
let j_ :=0for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(_pOut, end_)) { fail() }
if iszero(_item) {
_pOut := skipWhitespace(_pOut, end_)
if eq(chr(_pOut), 93) { break } // ']'.
}
_item, _pOut := parseValue(s_, _item, _pOut, end_)
if _item {
// forgefmt: disable-next-item
mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),
_BITPOS_KEY, j_))
j_ := add(j_, 1)
let c_ := chr(_pOut)
if eq(c_, 93) { break } // ']'.if eq(c_, 44) { continue } // ','.
}
_pOut := end_
}
_pOut := add(_pOut, 1)
packed_ := setP(packed_, _BITPOS_CHILD, _item)
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)
}
functionparseObject(s_, packed_, pIn_, end_) -> _item, _pOut{
for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(_pOut, end_)) { fail() }
if iszero(_item) {
_pOut := skipWhitespace(_pOut, end_)
if eq(chr(_pOut), 125) { break } // '}'.
}
_pOut := skipWhitespace(_pOut, end_)
let pKeyStart_ := _pOut
let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)
_pOut := skipWhitespace(pKeyEnd_, end_)
// If ':'.if eq(chr(_pOut), 58) {
_item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)
if _item {
// forgefmt: disable-next-item
mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),
_BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),
_BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))
let c_ := chr(_pOut)
if eq(c_, 125) { break } // '}'.if eq(c_, 44) { continue } // ','.
}
}
_pOut := end_
}
_pOut := add(_pOut, 1)
packed_ := setP(packed_, _BITPOS_CHILD, _item)
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)
}
functioncheckStringU(p_, o_) {
// If not in '0123456789abcdefABCDEF', revert.if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }
if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }
}
functionparseStringSub(s_, packed_, pIn_, end_) -> _pOut{
if iszero(lt(pIn_, end_)) { fail() }
for { _pOut := add(pIn_, 1) } 1 {} {
let c_ := chr(_pOut)
if eq(c_, 34) { break } // '"'.// Not '\'.if iszero(eq(c_, 92)) {
_pOut := add(_pOut, 1)
continue
}
c_ := chr(add(_pOut, 1))
// '"', '\', '//', 'b', 'f', 'n', 'r', 't'.if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {
_pOut := add(_pOut, 2)
continue
}
// 'u'.if eq(c_, 117) {
checkStringU(_pOut, 2)
_pOut := add(_pOut, 6)
continue
}
_pOut := end_
break
}
if iszero(lt(_pOut, end_)) { fail() }
_pOut := add(_pOut, 1)
}
functionskip0To9s(pIn_, end_, atLeastOne_) -> _pOut{
for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.
}
if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }
}
functionparseNumber(s_, packed_, pIn_, end_) -> _item, _pOut{
_pOut := pIn_
if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.
let c_ := chr(_pOut)
_pOut := add(_pOut, 1)
if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.
let t_ := mload(_pOut)
// 'E', 'e'.if eq(or(0x20, byte(0, t_)), 101) {
// forgefmt: disable-next-item
_pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.
add(_pOut, 1)), end_, 1)
}
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)
}
functioncopyStr(s_, offset_, len_) -> _sCopy{
_sCopy := mload(0x40)
s_ := add(s_, offset_)
let w_ := not(0x1f)
for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {
mstore(add(_sCopy, i_), mload(add(s_, i_)))
i_ := add(i_, w_) // `sub(i_, 0x20)`.if iszero(i_) { break }
}
mstore(_sCopy, len_) // Copy the length.
mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.
mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.
}
functionvalue(item_) -> _value{
let packed_ := mload(item_)
_value := getP(packed_, _BITPOS_VALUE) // The offset in the string.if iszero(and(_VALUE_INITED, packed_)) {
let s_ := getP(packed_, _BITPOS_STRING)
_value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))
packed_ := setP(packed_, _BITPOS_VALUE, _value)
mstore(s_, or(_VALUE_INITED, packed_))
}
}
functionchildren(item_) -> _arr{
_arr :=0x60// Initialize to the zero pointer.
let packed_ := mload(item_)
for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {
if or(iszero(packed_), iszero(item_)) { break }
if and(packed_, _CHILDREN_INITED) {
_arr := getP(packed_, _BITPOS_CHILD)
break
}
_arr := mload(0x40)
let o_ := add(_arr, 0x20)
for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {
mstore(o_, h_)
let q_ := mload(h_)
let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)
mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))
h_ := y_
o_ := add(o_, 0x20)
}
let w_ := not(0x1f)
let n_ := add(w_, sub(o_, _arr))
mstore(_arr, shr(5, n_))
mstore(0x40, o_) // Allocate memory.
packed_ := setP(packed_, _BITPOS_CHILD, _arr)
mstore(item_, or(_CHILDREN_INITED, packed_))
// Reverse the array.if iszero(lt(n_, 0x40)) {
let lo_ := add(_arr, 0x20)
let hi_ := add(_arr, n_)
for {} 1 {} {
let temp_ := mload(lo_)
mstore(lo_, mload(hi_))
mstore(hi_, temp_)
hi_ := add(hi_, w_)
lo_ := add(lo_, 0x20)
if iszero(lt(lo_, hi_)) { break }
}
}
break
}
}
functiongetStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result{
_result :=0x60// Initialize to the zero pointer.
let packed_ := mload(item_)
if or(iszero(item_), iszero(packed_)) { leave }
_result := getP(packed_, bitpos_)
if iszero(and(bitmaskInited_, packed_)) {
let s_ := getP(packed_, _BITPOS_STRING)
_result := copyStr(s_, _result, getP(packed_, bitposLength_))
mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))
}
}
switch mode
// Get value.
case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }
// Get key.
case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }
// Get children.
case 3 { result := children(input) }
// Parse.
default {
let p := add(input, 0x20)
let e := add(p, mload(input))
if iszero(eq(p, e)) {
let c := chr(e)
mstore8(e, 34) // Place a '"' at the end to speed up parsing.// The `34 << 248` makes `mallocItem` preserve '"' at the end.
mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))
result, p := parseValue(input, 0, p, e)
mstore8(e, c) // Restore the original char at the end.
}
if or(lt(p, e), iszero(result)) { fail() }
}
}
}
/// @dev Casts the input to a bytes32.function_toInput(stringmemory input) privatepurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
result := input
}
}
/// @dev Casts the input to a bytes32.function_toInput(Item memory input) privatepurereturns (bytes32 result) {
/// @solidity memory-safe-assemblyassembly {
result := input
}
}
}
Contract Source Code
File 20 of 30: LibBit.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Library for bit twiddling and boolean operations./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)libraryLibBit{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* BIT TWIDDLING OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Find last set./// Returns the index of the most significant bit of `x`,/// counting from the least significant bit position./// If `x` is zero, returns 256.functionfls(uint256 x) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
r :=or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r :=or(r, shl(6, lt(0xffffffffffffffff, 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
r :=or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros./// Returns the number of zeros preceding the most significant one bit./// If `x` is zero, returns 256.functionclz(uint256 x) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
r :=shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r :=or(r, shl(6, lt(0xffffffffffffffff, 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
r :=add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set./// Returns the index of the least significant bit of `x`,/// counting from the least significant bit position./// If `x` is zero, returns 256./// Equivalent to `ctz` (count trailing zeros), which gives/// the number of zeros following the least significant one bit.functionffs(uint256 x) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
// Isolate the least significant bit.let b :=and(x, add(not(x), 1))
r :=or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))
r :=or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r :=or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.// forgefmt: disable-next-item
r :=or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.functionpopCount(uint256 x) internalpurereturns (uint256 c) {
/// @solidity memory-safe-assemblyassembly {
let max :=not(0)
let isMax :=eq(x, max)
x :=sub(x, and(shr(1, x), div(max, 3)))
x :=add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x :=and(add(x, shr(4, x)), div(max, 17))
c :=or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns whether `x` is a power of 2.functionisPo2(uint256 x) internalpurereturns (bool result) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to `x && !(x & (x - 1))`.
result :=iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.functionreverseBits(uint256 x) internalpurereturns (uint256 r) {
uint256 m0 =0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 <<2);
uint256 m2 = m1 ^ (m1 <<1);
r = reverseBytes(x);
r = (m2 & (r >>1)) | ((m2 & r) <<1);
r = (m1 & (r >>2)) | ((m1 & r) <<2);
r = (m0 & (r >>4)) | ((m0 & r) <<4);
}
/// @dev Returns `x` reversed at the byte level.functionreverseBytes(uint256 x) internalpurereturns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.uint256 m0 =0x100000000000000000000000000000001* (~toUint(x ==0) >>192);
uint256 m1 = m0 ^ (m0 <<32);
uint256 m2 = m1 ^ (m1 <<16);
uint256 m3 = m2 ^ (m2 <<8);
r = (m3 & (x >>8)) | ((m3 & x) <<8);
r = (m2 & (r >>16)) | ((m2 & r) <<16);
r = (m1 & (r >>32)) | ((m1 & r) <<32);
r = (m0 & (r >>64)) | ((m0 & r) <<64);
r = (r >>128) | (r <<128);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* BOOLEAN OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// A Solidity bool on the stack or memory is represented as a 256-bit word.// Non-zero values are true, zero is false.// A clean bool is either 0 (false) or 1 (true) under the hood.// Usually, if not always, the bool result of a regular Solidity expression,// or the argument of a public/external function will be a clean bool.// You can usually use the raw variants for more performance.// If uncertain, test (best with exact compiler settings).// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s)./// @dev Returns `x & y`. Inputs must be clean.functionrawAnd(bool x, bool y) internalpurereturns (bool z) {
/// @solidity memory-safe-assemblyassembly {
z :=and(x, y)
}
}
/// @dev Returns `x & y`.functionand(bool x, bool y) internalpurereturns (bool z) {
/// @solidity memory-safe-assemblyassembly {
z :=and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `x | y`. Inputs must be clean.functionrawOr(bool x, bool y) internalpurereturns (bool z) {
/// @solidity memory-safe-assemblyassembly {
z :=or(x, y)
}
}
/// @dev Returns `x | y`.functionor(bool x, bool y) internalpurereturns (bool z) {
/// @solidity memory-safe-assemblyassembly {
z :=or(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.functionrawToUint(bool b) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.functiontoUint(bool b) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
z :=iszero(iszero(b))
}
}
}
Contract Source Code
File 21 of 30: LibBitmap.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;import {LibBit} from"./LibBit.sol";
/// @notice Library for storage of packed unsigned booleans./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)libraryLibBitmap{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The constant returned when a bitmap scan does not find a result.uint256internalconstant NOT_FOUND =type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STRUCTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev A bitmap in storage.structBitmap {
mapping(uint256=>uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the boolean value of the bit at `index` in `bitmap`.functionget(Bitmap storage bitmap, uint256 index) internalviewreturns (bool isSet) {
// It is better to set `isSet` to either 0 or 1, than zero vs non-zero.// Both cost the same amount of gas, but the former allows the returned value// to be reused without cleaning the upper bits.uint256 b = (bitmap.map[index >>8] >> (index &0xff)) &1;
/// @solidity memory-safe-assemblyassembly {
isSet := b
}
}
/// @dev Updates the bit at `index` in `bitmap` to true.functionset(Bitmap storage bitmap, uint256 index) internal{
bitmap.map[index >>8] |= (1<< (index &0xff));
}
/// @dev Updates the bit at `index` in `bitmap` to false.functionunset(Bitmap storage bitmap, uint256 index) internal{
bitmap.map[index >>8] &=~(1<< (index &0xff));
}
/// @dev Flips the bit at `index` in `bitmap`./// Returns the boolean result of the flipped bit.functiontoggle(Bitmap storage bitmap, uint256 index) internalreturns (bool newIsSet) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot :=keccak256(0x00, 0x40)
let shift :=and(index, 0xff)
let storageValue :=xor(sload(storageSlot), shl(shift, 1))
// It makes sense to return the `newIsSet`,// as it allow us to skip an additional warm `sload`,// and it costs minimal gas (about 15),// which may be optimized away if the returned value is unused.
newIsSet :=and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
/// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.functionsetTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot :=keccak256(0x00, 0x40)
let storageValue :=sload(storageSlot)
let shift :=and(index, 0xff)
sstore(
storageSlot,
// Unsets the bit at `shift` via `and`, then sets its new value via `or`.or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
/// @dev Consecutively sets `amount` of bits starting from the bit at `start`.functionsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
let max :=not(0)
let shift :=and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
ifiszero(lt(add(shift, amount), 257)) {
let storageSlot :=keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket :=add(mload(0x00), 1)
let bucketEnd :=add(mload(0x00), shr(8, add(amount, shift)))
amount :=and(add(amount, shift), 0xff)
shift :=0for {} iszero(eq(bucket, bucketEnd)) { bucket :=add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot :=keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
/// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.functionunsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
let shift :=and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
ifiszero(lt(add(shift, amount), 257)) {
let storageSlot :=keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket :=add(mload(0x00), 1)
let bucketEnd :=add(mload(0x00), shr(8, add(amount, shift)))
amount :=and(add(amount, shift), 0xff)
shift :=0for {} iszero(eq(bucket, bucketEnd)) { bucket :=add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot :=keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
/// @dev Returns number of set bits within a range by/// scanning `amount` of bits starting from the bit at `start`.functionpopCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internalviewreturns (uint256 count)
{
unchecked {
uint256 bucket = start >>8;
uint256 shift = start &0xff;
if (!(amount + shift <257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >>8);
amount = (amount + shift) &0xff;
shift =0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256- amount));
}
}
/// @dev Returns the index of the most significant set bit before the bit at `before`./// If no set bit is found, returns `NOT_FOUND`.functionfindLastSet(Bitmap storage bitmap, uint256 before)
internalviewreturns (uint256 setBitIndex)
{
uint256 bucket;
uint256 bucketBits;
/// @solidity memory-safe-assemblyassembly {
setBitIndex :=not(0)
bucket :=shr(8, before)
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset :=and(0xff, not(before)) // `256 - (255 & before) - 1`.
bucketBits :=shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
ifiszero(or(bucketBits, iszero(bucket))) {
for {} 1 {} {
bucket :=add(bucket, setBitIndex) // `sub(bucket, 1)`.mstore(0x00, bucket)
bucketBits :=sload(keccak256(0x00, 0x40))
ifor(bucketBits, iszero(bucket)) { break }
}
}
}
if (bucketBits !=0) {
setBitIndex = (bucket <<8) | LibBit.fls(bucketBits);
/// @solidity memory-safe-assemblyassembly {
setBitIndex :=or(setBitIndex, sub(0, gt(setBitIndex, before)))
}
}
}
}
Contract Source Code
File 22 of 30: LibPRNG.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Library for generating pseudorandom numbers./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol)libraryLibPRNG{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STRUCTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev A pseudorandom number state in memory.structPRNG {
uint256 state;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Seeds the `prng` with `state`.functionseed(PRNG memory prng, uint256 state) internalpure{
/// @solidity memory-safe-assemblyassembly {
mstore(prng, state)
}
}
/// @dev Returns the next pseudorandom uint256./// All bits of the returned uint256 pass the NIST Statistical Test Suite.functionnext(PRNG memory prng) internalpurereturns (uint256 result) {
// We simply use `keccak256` for a great balance between// runtime gas costs, bytecode size, and statistical properties.//// A high-quality LCG with a 32-byte state// is only about 30% more gas efficient during runtime,// but requires a 32-byte multiplier, which can cause bytecode bloat// when this function is inlined.//// Using this method is about 2x more efficient than// `nextRandomness = uint256(keccak256(abi.encode(randomness)))`./// @solidity memory-safe-assemblyassembly {
result :=keccak256(prng, 0x20)
mstore(prng, result)
}
}
/// @dev Returns a pseudorandom uint256, uniformly distributed/// between 0 (inclusive) and `upper` (exclusive)./// If your modulus is big, this method is recommended/// for uniform sampling to avoid modulo bias./// For uniform sampling across all uint256 values,/// or for small enough moduli such that the bias is neligible,/// use {next} instead.functionuniform(PRNG memory prng, uint256 upper) internalpurereturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly {
for {} 1 {} {
result :=keccak256(prng, 0x20)
mstore(prng, result)
ifiszero(lt(result, mod(sub(0, upper), upper))) { break }
}
result :=mod(result, upper)
}
}
/// @dev Shuffles the array in-place with Fisher-Yates shuffle.functionshuffle(PRNG memory prng, uint256[] memory a) internalpure{
/// @solidity memory-safe-assemblyassembly {
let n :=mload(a)
let w :=not(0)
let mask :=shr(128, w)
if n {
for { a :=add(a, 0x20) } 1 {} {
// We can just directly use `keccak256`, cuz// the other approaches don't save much.let r :=keccak256(prng, 0x20)
mstore(prng, r)
// Note that there will be a very tiny modulo bias// if the length of the array is not a power of 2.// For all practical purposes, it is negligible// and will not be a fairness or security concern.
{
let j :=add(a, shl(5, mod(shr(128, r), n)))
n :=add(n, w) // `sub(n, 1)`.ifiszero(n) { break }
let i :=add(a, shl(5, n))
let t :=mload(i)
mstore(i, mload(j))
mstore(j, t)
}
{
let j :=add(a, shl(5, mod(and(r, mask), n)))
n :=add(n, w) // `sub(n, 1)`.ifiszero(n) { break }
let i :=add(a, shl(5, n))
let t :=mload(i)
mstore(i, mload(j))
mstore(j, t)
}
}
}
}
}
/// @dev Shuffles the bytes in-place with Fisher-Yates shuffle.functionshuffle(PRNG memory prng, bytesmemory a) internalpure{
/// @solidity memory-safe-assemblyassembly {
let n :=mload(a)
let w :=not(0)
let mask :=shr(128, w)
if n {
let b :=add(a, 0x01)
for { a :=add(a, 0x20) } 1 {} {
// We can just directly use `keccak256`, cuz// the other approaches don't save much.let r :=keccak256(prng, 0x20)
mstore(prng, r)
// Note that there will be a very tiny modulo bias// if the length of the array is not a power of 2.// For all practical purposes, it is negligible// and will not be a fairness or security concern.
{
let o :=mod(shr(128, r), n)
n :=add(n, w) // `sub(n, 1)`.ifiszero(n) { break }
let t :=mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
{
let o :=mod(and(r, mask), n)
n :=add(n, w) // `sub(n, 1)`.ifiszero(n) { break }
let t :=mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
}
}
}
}
/// @dev Returns a sample from the standard normal distribution denominated in `WAD`.functionstandardNormalWad(PRNG memory prng) internalpurereturns (int256 result) {
/// @solidity memory-safe-assemblyassembly {
// Technically, this is the Irwin-Hall distribution with 20 samples.// The chance of drawing a sample outside 10 σ from the standard normal distribution// is ≈ 0.000000000000000000000015, which is insignificant for most practical purposes.// This function uses about 324 gas.let n :=0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001let a :=0x2d3e5279f07911d5062dc0dd5c8998d7let m :=0x0fffffffffffffff0fffffffffffffff0fffffffffffffff0ffffffffffffffflet s :=0x1000000000000000100000000000000010000000000000001let r :=keccak256(prng, 0x20)
mstore(prng, r)
let r1 :=mulmod(r, a, n)
let r2 :=mulmod(r1, a, n)
let r3 :=mulmod(r2, a, n)
// forgefmt: disable-next-item
result :=sar(96, sub(mul(53229877791723203740515581680,
add(add(shr(192, mul(s, add(and(m, r), and(m, r1)))),
shr(192, mul(s, add(and(m, r2), and(m, r3))))),
shr(192, mul(and(m, mulmod(r3, a, n)), s)))),
613698707936721051257405563935529819467266145679))
}
}
}
Contract Source Code
File 23 of 30: LibString.sol
// 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));
}
/// @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)
}
}
}
Contract Source Code
File 24 of 30: LibZip.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Library for compressing and decompressing bytes./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol)/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor)/// @author FastLZ by ariya (https://github.com/ariya/FastLZ)////// @dev Note:/// The accompanying solady.js library includes implementations of/// FastLZ and calldata operations for convenience.libraryLibZip{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* FAST LZ OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// LZ77 implementation based on FastLZ.// Equivalent to level 1 compression and decompression at the following commit:// https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42// Decompression is backwards compatible./// @dev Returns the compressed `data`.functionflzCompress(bytesmemory data) internalpurereturns (bytesmemory result) {
/// @solidity memory-safe-assemblyassembly {
functionms8(d_, v_) ->_d{
mstore8(d_, v_)
_d :=add(d_, 1)
}
functionu24(p_) -> _u{
let w := mload(p_)
_u := or(shl(16, byte(2, w)), or(shl(8, byte(1, w)), byte(0, w)))
}
functioncmp(p_, q_, e_) -> _l{
for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } {
e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_)
}
}
functionliterals(runs_, src_, dest_) -> _o{
for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } {
mstore(ms8(_o, 31), mload(src_))
_o := add(_o, 0x21)
src_ := add(src_, 0x20)
}
if iszero(runs_) { leave }
mstore(ms8(_o, sub(runs_, 1)), mload(src_))
_o := add(1, add(_o, runs_))
}
functionmatch(l_, d_, o_) -> _o{
for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } {
o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_))
}
if iszero(lt(l_, 7)) {
_o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_))
leave
}
_o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_))
}
functionsetHash(i_, v_) {
let p := add(mload(0x40), shl(2, i_))
mstore(p, xor(mload(p), shl(224, xor(shr(224, mload(p)), v_))))
}
functiongetHash(i_) -> _h{
_h := shr(224, mload(add(mload(0x40), shl(2, i_))))
}
functionhash(v_) -> _r{
_r := and(shr(19, mul(2654435769, v_)), 0x1fff)
}
functionsetNextHash(ip_, ipStart_) -> _ip{
setHash(hash(u24(ip_)), sub(ip_, ipStart_))
_ip := add(ip_, 1)
}
codecopy(mload(0x40), codesize(), 0x8000) // Zeroize the hashmap.
let op := add(mload(0x40), 0x8000)
let a := add(data, 0x20)
let ipStart := a
let ipLimit := sub(add(ipStart, mload(data)), 13)
for { let ip := add(2, a) } lt(ip, ipLimit) {} {
let r :=0
let d :=0for {} 1 {} {
let s := u24(ip)
let h := hash(s)
r := add(ipStart, getHash(h))
setHash(h, sub(ip, ipStart))
d := sub(ip, r)
if iszero(lt(ip, ipLimit)) { break }
ip := add(ip, 1)
if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } }
}
if iszero(lt(ip, ipLimit)) { break }
ip := sub(ip, 1)
if gt(ip, a) { op := literals(sub(ip, a), a, op) }
let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9))
op := match(l, d, op)
ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart)
a := ip
}
op := literals(sub(add(ipStart, mload(data)), a), a, op)
result := mload(0x40)
let t := add(result, 0x8000)
let n := sub(op, t)
mstore(result, n) // Store the length.// Copy the result to compact the memory, overwriting the hashmap.
let o := add(result, 0x20)
for { let i } lt(i, n) { i := add(i, 0x20) } { mstore(add(o, i), mload(add(t, i))) }
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(add(o, n), 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.functionflzDecompress(bytesmemory data) internalpurereturns (bytesmemory result) {
/// @solidity memory-safe-assemblyassembly {
let end :=add(add(data, 0x20), mload(data))
result :=mload(0x40)
let op :=add(result, 0x20)
for { data :=add(data, 0x20) } lt(data, end) {} {
let w :=mload(data)
let c :=byte(0, w)
let t :=shr(5, c)
ifiszero(t) {
mstore(op, mload(add(data, 1)))
data :=add(data, add(2, c))
op :=add(op, add(1, c))
continue
}
let g :=eq(t, 7)
let l :=add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) // Mfor {
let s :=add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) // Rlet r :=sub(op, s)
let f :=xor(s, mul(gt(s, 0x20), xor(s, 0x20)))
let j :=0
} 1 {} {
mstore(add(op, j), mload(add(r, j)))
j :=add(j, f)
ifiszero(lt(j, l)) { break }
}
data :=add(data, add(2, g))
op :=add(op, l)
}
mstore(result, sub(op, add(result, 0x20))) // Store the length.mstore(op, 0) // Zeroize the slot after the string.mstore(0x40, add(op, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CALLDATA OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// Calldata compression and decompression using selective run length encoding:// - Sequences of 0x00 (up to 128 consecutive).// - Sequences of 0xff (up to 32 consecutive).//// A run length encoded block consists of two bytes:// (0) 0x00// (1) A control byte with the following bit layout:// - [7] `0: 0x00, 1: 0xff`.// - [0..6] `runLength - 1`.//// The first 4 bytes are bitwise negated so that the compressed calldata// can be dispatched into the `fallback` and `receive` functions./// @dev Returns the compressed `data`.functioncdCompress(bytesmemory data) internalpurereturns (bytesmemory result) {
/// @solidity memory-safe-assemblyassembly {
functionrle(v_, o_, d_) ->_o, _d{
mstore(o_, shl(240, or(and(0xff, add(d_, 0xff)), and(0x80, v_))))
_o :=add(o_, 2)
}
result := mload(0x40)
let o := add(result, 0x20)
let z :=0// Number of consecutive 0x00.
let y :=0// Number of consecutive 0xff.for { let end := add(data, mload(data)) } iszero(eq(data, end)) {} {
data := add(data, 1)
let c :=byte(31, mload(data))
if iszero(c) {
if y { o, y := rle(0xff, o, y) }
z := add(z, 1)
if eq(z, 0x80) { o, z := rle(0x00, o, 0x80) }
continue
}
if eq(c, 0xff) {
if z { o, z := rle(0x00, o, z) }
y := add(y, 1)
if eq(y, 0x20) { o, y := rle(0xff, o, 0x20) }
continue
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
mstore8(o, c)
o := add(o, 1)
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
// Bitwise negate the first 4 bytes.
mstore(add(result, 4), not(mload(add(result, 4))))
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.functioncdDecompress(bytesmemory data) internalpurereturns (bytesmemory result) {
/// @solidity memory-safe-assemblyassembly {
ifmload(data) {
result :=mload(0x40)
let o :=add(result, 0x20)
let s :=add(data, 4)
let v :=mload(s)
let end :=add(data, mload(data))
mstore(s, not(v)) // Bitwise negate the first 4 bytes.for {} lt(data, end) {} {
data :=add(data, 1)
let c :=byte(31, mload(data))
ifiszero(c) {
data :=add(data, 1)
let d :=byte(31, mload(data))
// Fill with either 0xff or 0x00.mstore(o, not(0))
ifiszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o :=add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o :=add(o, 1)
}
mstore(s, v) // Restore the first 4 bytes.mstore(result, sub(o, add(result, 0x20))) // Store the length.mstore(o, 0) // Zeroize the slot after the string.mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
}
/// @dev To be called in the `fallback` function./// ```/// fallback() external payable { LibZip.cdFallback(); }/// receive() external payable {} // Silence compiler warning to add a `receive` function./// ```/// For efficiency, this function will directly return the results, terminating the context./// If called internally, it must be called at the end of the function.functioncdFallback() internal{
assembly {
ifiszero(calldatasize()) { return(calldatasize(), calldatasize()) }
let o :=0let f :=not(3) // For negating the first 4 bytes.for { let i :=0 } lt(i, calldatasize()) {} {
let c :=byte(0, xor(add(i, f), calldataload(i)))
i :=add(i, 1)
ifiszero(c) {
let d :=byte(0, xor(add(i, f), calldataload(i)))
i :=add(i, 1)
// Fill with either 0xff or 0x00.mstore(o, not(0))
ifiszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o :=add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o :=add(o, 1)
}
let success :=delegatecall(gas(), address(), 0x00, o, codesize(), 0x00)
returndatacopy(0x00, 0x00, returndatasize())
ifiszero(success) { revert(0x00, returndatasize()) }
return(0x00, returndatasize())
}
}
}
Contract Source Code
File 25 of 30: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)pragmasolidity ^0.8.20;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
/**
* @dev Muldiv operation overflow.
*/errorMathOverflowedMulDiv();
enumRounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
if (b ==0) {
// Guarantee the same behavior as in a regular Solidity division.return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0 = x * y; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.// The surrounding unchecked block does not change this fact.// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.uint256 twos = denominator & (0- denominator);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also// works in modular arithmetic, doubling the correct bits in each step.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (unsignedRoundsUp(rounding) &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) &&10** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) &&1<< (result <<3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/functionunsignedRoundsUp(Rounding rounding) internalpurereturns (bool) {
returnuint8(rounding) %2==1;
}
}
Contract Source Code
File 26 of 30: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/constructor(address initialOwner) {
if (initialOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
if (newOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 27 of 30: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)pragmasolidity ^0.8.20;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant NOT_ENTERED =1;
uint256privateconstant ENTERED =2;
uint256private _status;
/**
* @dev Unauthorized reentrant call.
*/errorReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function_nonReentrantBefore() private{
// On the first call to nonReentrant, _status will be NOT_ENTEREDif (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function_nonReentrantAfter() private{
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/function_reentrancyGuardEntered() internalviewreturns (bool) {
return _status == ENTERED;
}
}
Contract Source Code
File 28 of 30: SafeTransferLib.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)////// @dev Note:/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection./// - For ERC20s, this implementation won't check that a token has code,/// responsibility is delegated to the caller.librarySafeTransferLib{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The ETH transfer has failed.errorETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.errorTransferFromFailed();
/// @dev The ERC20 `transfer` has failed.errorTransferFailed();
/// @dev The ERC20 `approve` has failed.errorApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.uint256internalconstant GAS_STIPEND_NO_STORAGE_WRITES =2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few/// storage reads and writes, but low enough to prevent griefing.uint256internalconstant GAS_STIPEND_NO_GRIEF =100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ETH OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.//// The regular variants:// - Forwards all remaining gas to the target.// - Reverts if the target reverts.// - Reverts if the current contract has insufficient balance.//// The force variants:// - Forwards with an optional gas stipend// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).// - If the target reverts, or if the gas stipend is exhausted,// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.// - Reverts if the current contract has insufficient balance.//// The try variants:// - Forwards with a mandatory gas stipend.// - Instead of reverting, returns whether the transfer succeeded./// @dev Sends `amount` (in wei) ETH to `to`.functionsafeTransferETH(address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
ifiszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.functionsafeTransferAllETH(address to) internal{
/// @solidity memory-safe-assemblyassembly {
// Transfer all the ETH and check if it succeeded or not.ifiszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.functionforceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal{
/// @solidity memory-safe-assemblyassembly {
iflt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
ifiszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.functionforceSafeTransferAllETH(address to, uint256 gasStipend) internal{
/// @solidity memory-safe-assemblyassembly {
ifiszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.functionforceSafeTransferETH(address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
iflt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
ifiszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.functionforceSafeTransferAllETH(address to) internal{
/// @solidity memory-safe-assemblyassembly {
// forgefmt: disable-next-itemifiszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.functiontrySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internalreturns (bool success)
{
/// @solidity memory-safe-assemblyassembly {
success :=call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.functiontrySafeTransferAllETH(address to, uint256 gasStipend)
internalreturns (bool success)
{
/// @solidity memory-safe-assemblyassembly {
success :=call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ERC20 OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Sends `amount` of ERC20 `token` from `from` to `to`./// Reverts upon failure.////// The `from` account must have at least `amount` approved for/// the current contract to manage.functionsafeTransferFrom(address token, addressfrom, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
let m :=mload(0x40) // Cache the free memory pointer.mstore(0x60, amount) // Store the `amount` argument.mstore(0x40, to) // Store the `to` argument.mstore(0x2c, shl(96, from)) // Store the `from` argument.mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`./// Reverts upon failure.////// The `from` account must have their entire balance approved for/// the current contract to manage.functionsafeTransferAllFrom(address token, addressfrom, address to)
internalreturns (uint256 amount)
{
/// @solidity memory-safe-assemblyassembly {
let m :=mload(0x40) // Cache the free memory pointer.mstore(0x40, to) // Store the `to` argument.mstore(0x2c, shl(96, from)) // Store the `from` argument.mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.// Read the balance, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount :=mload(0x60) // The `amount` is already at 0x60. We'll need to return it.// Perform the transfer, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`./// Reverts upon failure.functionsafeTransfer(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`./// Reverts upon failure.functionsafeTransferAll(address token, address to) internalreturns (uint256 amount) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.mstore(0x20, address()) // Store the address of the current contract.// Read the balance, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount :=mload(0x34) // The `amount` is already at 0x34. We'll need to return it.mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract./// Reverts upon failure.functionsafeApprove(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.// Perform the approval, reverting upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract./// If the initial attempt to approve fails, attempts to reset the approved amount to zero,/// then retries the approval again (some tokens, e.g. USDT, requires this)./// Reverts upon failure.functionsafeApproveWithRetry(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.// Perform the approval, retrying upon failure.ifiszero(
and( // The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.mstore(0x34, amount) // Store back the original `amount`.// Retry the approval, reverting upon failure.ifiszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`./// Returns zero if the `token` does not exist.functionbalanceOf(address token, address account) internalviewreturns (uint256 amount) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, account) // Store the `account` argument.mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
Contract Source Code
File 29 of 30: SignedMath.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)pragmasolidity ^0.8.20;/**
* @dev Standard signed math utilities missing in the Solidity language.
*/librarySignedMath{
/**
* @dev Returns the largest of two signed numbers.
*/functionmax(int256 a, int256 b) internalpurereturns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/functionmin(int256 a, int256 b) internalpurereturns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/functionaverage(int256 a, int256 b) internalpurereturns (int256) {
// Formula from the book "Hacker's Delight"int256 x = (a & b) + ((a ^ b) >>1);
return x + (int256(uint256(x) >>255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/functionabs(int256 n) internalpurereturns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`returnuint256(n >=0 ? n : -n);
}
}
}
Contract Source Code
File 30 of 30: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)pragmasolidity ^0.8.20;import {Math} from"./math/Math.sol";
import {SignedMath} from"./math/SignedMath.sol";
/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant HEX_DIGITS ="0123456789abcdef";
uint8privateconstant ADDRESS_LENGTH =20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/errorStringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
uint256 length = Math.log10(value) +1;
stringmemory buffer =newstring(length);
uint256 ptr;
/// @solidity memory-safe-assemblyassembly {
ptr :=add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assemblyassembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /=10;
if (value ==0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/functiontoStringSigned(int256 value) internalpurereturns (stringmemory) {
returnstring.concat(value <0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
return toHexString(value, Math.log256(value) +1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
uint256 localValue = value;
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = HEX_DIGITS[localValue &0xf];
localValue >>=4;
}
if (localValue !=0) {
revert StringsInsufficientHexLength(value, length);
}
returnstring(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/functiontoHexString(address addr) internalpurereturns (stringmemory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/functionequal(stringmemory a, stringmemory b) internalpurereturns (bool) {
returnbytes(a).length==bytes(b).length&&keccak256(bytes(a)) ==keccak256(bytes(b));
}
}