Last active
July 3, 2024 07:39
-
-
Save Amxx/49baf1c7c485f8110712bb6f2e888236 to your computer and use it in GitHub Desktop.
EthCC2024-DatastructuresForVerkle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Mapping 0x304BC89A59E41902B58934E0642772830CcA744d | |
721 0x9ed6c5564541b130cbc7436bb6654f9144bbe9c0cf2b44c8464388b879395586 42180 | |
155 0xf262d37ae1c0ea0878f60cc3950cb170e55e21ee96b2e219d243c1bed44b9f87 56089 | |
4337 0x8c3d7a8727a5cad487721f898aa42e8b0e82522b134a6d825819b6db91a94c58 46801 | |
191 0x201376e974ba73b059efb473bea9ecda54a2be4a4f83c45f094bf28f8173e6ab 60510 | |
20 0xb9f16779eb69d6675606fe7304b74d24f90d9e754412863d7566766b5b119da6 68810 | |
7702 0xb07efe99850a1e62f24b2faf01ce65f70ee4444edd9fad86f15b07285545de7d 46801 | |
1559 0xceec4797d1fd0abd248120dd31c7a20943e7e667739defcf8a24595f196d73ae 60522 | |
712 0x025e447415c3cc3053201332e288abc28cf5d351eb347cf0cf38c52c738d2913 60522 | |
1363 0x292d6b88cb79fe9355b66db13247df32e727275f4a52a66c350a3ccdf055f3e0 46801 | |
1155 0x36234686a2191b2a0ce37fb8deea2555fc26884206cdc15b08716826909f3de3 46801 | |
pop 0xe3ec04ae9a868b67362cdc64c3ca35f84f7e23bd83dd9288009c6ab1f2f794bd 83815 | |
Array 0xee62665b7560bE5c1099eC3A583f03Ef0Fc2B00e | |
721 0xa45bae0183283d4e49f17c53dbc03e273150b6c62446d5e02e7c177c4c9fa170 35925 | |
155 0x76eae70c5974f2e29048e51e9355caa25e0f31b7433d34d57891ca6ac8f17581 39658 | |
4337 0x43546f6d3715314d0a96d90a1b8367073a5c50439ed9363ceb1863f08ddb2d08 37175 | |
191 0x254a97faee26a40f025cbfe6074e83bd1b68be60cda736a5d5acb830a571ef67 40708 | |
20 0x7e66e1a8c9dc1e2d6e7c9bd85ff25f3ec1f6ff99bffd6314dd609ed09aae11e2 42503 | |
7702 0xba29d9224f12bb1f8b4c3afc1a166815074f1933ebf3aec9cf3b48fed9ba0201 37175 | |
1559 0x7bc118634c266e02df2328df6650f1cf1396cb628bc59d519492b8aaf17511f0 40320 | |
712 0xfcbdab37f29bc0b21870dd87f34e7cdb12a31c41ae9124046f74c91a9b433b71 40820 | |
1363 0x6f0c52209c3cf724f780f0a3899ad7f8fb90a3811fc3e4d7f50b70d1386fcdce 37175 | |
1155 0x811454f61e0a112fd6dd01e9b491d1d7eba05f252b640af8bd91cccb1a8a6ed8 37175 | |
pop 0x1a1a20dc5a41786134dd56fa5dd9c9453404e2f5f92b2ee031771736a36e6d61 50739 | |
Array2 0xC321bcE6da566FC575F1C112a33d9c47b2A5c008 | |
721 0xa6d1e07760fe75d81b63a2f02831193f70a6c70df545402ac7c14c49d327ad26 33098 | |
155 0x0e9fc98cc5bee2f234acf8e4bee8d5a7977bc05e0a143caff71937020c2131fb 37060 | |
4337 0xef8eef6ba84c2718269fb5b14b331fef52ae54f364b5fc01385408d6fef06f49 34171 | |
191 0xf5a87418a473dbf25b42cdcf55b6bef680807d5daed59575990be715e82079ac 38133 | |
20 0x130cd2b596a22fa04ae40fc576888190b398725873dfe5003c3dd81e44412283 39934 | |
7702 0xca3848f9eb26f77e4513010ae9bdd21c02d9aa794e76f2612b79ead292c1cf83 34171 | |
1559 0x34b41c76d928de4a747c01dfbb63f3fe0408acda1abb407547bbf9063374b221 37745 | |
712 0x7867310536f1a45a1b1863323d3b42de83ba62a494a0eac90edb7f68ba3a0ca8 38245 | |
1363 0x651cfe1bb45611fb7317236144bfdecd78616aa3871868429fa9d262486cdf1c 34171 | |
1155 0x0ceac2b2bc77de96c749e25b74810e7bdd0c99c955ed9f0b5ec866f9e7fb6969 34171 | |
pop 0x4e50851512c6b80476bc02a2884e146fb777dc5ffb6281c097f167bd2a435245 46859 | |
ERC721 0x697bCd5513F62773030135cA227848C4F499F7e4 | |
Mint 0xfe52af8243fcf2d18708b45cc4a13030fb746a1a0bbccc36b90e153f8473678f 40772 | |
Transfer 0x8392b88383b22ea3a7ae822dfa7594651b6d39464fa8903496340df5754c5631 55180 | |
ERC721Consolidated 0x63FA0723a30405f10B0Aa8c21442D3C3a71595Dd | |
Mint 0xc84ab8f9615c2104b12d70cc78c5ab0c0c187719f62a5e8e8e6ec1c35981b025 40772 | |
Transfer 0x6462301b788718119ce47c91ec3ae8198afca9448785e8ac7006b4bb7a1c51fe 49886 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol) | |
pragma solidity ^0.8.20; | |
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | |
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; | |
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; | |
import {Context} from "@openzeppelin/contracts/utils/Context.sol"; | |
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | |
import {IERC165, ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; | |
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; | |
contract ERC721Mock is ERC721 { | |
constructor() ERC721("ERC721Mock", "ERC721Mock") {} | |
function mint(address account, uint256 tokenId) external { | |
_mint(account, tokenId); | |
} | |
function burn(uint256 tokenId) external { | |
_burn(tokenId); | |
} | |
} | |
contract ERC721ConsolidatedMock is ERC721Consolidated { | |
constructor() ERC721Consolidated("ERC721Mock", "ERC721Mock") {} | |
function mint(address account, uint256 tokenId) external { | |
_mint(account, tokenId); | |
} | |
function burn(uint256 tokenId) external { | |
_burn(tokenId); | |
} | |
} | |
/** | |
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including | |
* the Metadata extension, but not including the Enumerable extension, which is available separately as | |
* {ERC721Enumerable}. | |
*/ | |
abstract contract ERC721Consolidated is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors { | |
using Strings for uint256; | |
// Token name | |
string private _name; | |
// Token symbol | |
string private _symbol; | |
struct TokenDetails { | |
address owner; | |
address approval; | |
} | |
struct AccountDetails { | |
uint256 balance; | |
mapping(address => bool) operators; | |
} | |
mapping(uint256 tokenId => TokenDetails) private _tokens; | |
mapping(address owner => AccountDetails) private _accounts; | |
/** | |
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. | |
*/ | |
constructor(string memory name_, string memory symbol_) { | |
_name = name_; | |
_symbol = symbol_; | |
} | |
/** | |
* @dev See {IERC165-supportsInterface}. | |
*/ | |
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { | |
return | |
interfaceId == type(IERC721).interfaceId || | |
interfaceId == type(IERC721Metadata).interfaceId || | |
super.supportsInterface(interfaceId); | |
} | |
/** | |
* @dev See {IERC721-balanceOf}. | |
*/ | |
function balanceOf(address owner) public view virtual returns (uint256) { | |
if (owner == address(0)) { | |
revert ERC721InvalidOwner(address(0)); | |
} | |
return _accounts[owner].balance; | |
} | |
/** | |
* @dev See {IERC721-ownerOf}. | |
*/ | |
function ownerOf(uint256 tokenId) public view virtual returns (address) { | |
return _requireOwned(tokenId); | |
} | |
/** | |
* @dev See {IERC721Metadata-name}. | |
*/ | |
function name() public view virtual returns (string memory) { | |
return _name; | |
} | |
/** | |
* @dev See {IERC721Metadata-symbol}. | |
*/ | |
function symbol() public view virtual returns (string memory) { | |
return _symbol; | |
} | |
/** | |
* @dev See {IERC721Metadata-tokenURI}. | |
*/ | |
function tokenURI(uint256 tokenId) public view virtual returns (string memory) { | |
_requireOwned(tokenId); | |
string memory baseURI = _baseURI(); | |
return bytes(baseURI).length > 0 ? string.concat(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 overridden in child contracts. | |
*/ | |
function _baseURI() internal view virtual returns (string memory) { | |
return ""; | |
} | |
/** | |
* @dev See {IERC721-approve}. | |
*/ | |
function approve(address to, uint256 tokenId) public virtual { | |
_approve(to, tokenId, _msgSender()); | |
} | |
/** | |
* @dev See {IERC721-getApproved}. | |
*/ | |
function getApproved(uint256 tokenId) public view virtual returns (address) { | |
_requireOwned(tokenId); | |
return _getApproved(tokenId); | |
} | |
/** | |
* @dev See {IERC721-setApprovalForAll}. | |
*/ | |
function setApprovalForAll(address operator, bool approved) public virtual { | |
_setApprovalForAll(_msgSender(), operator, approved); | |
} | |
/** | |
* @dev See {IERC721-isApprovedForAll}. | |
*/ | |
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { | |
return _accounts[owner].operators[operator]; | |
} | |
/** | |
* @dev See {IERC721-transferFrom}. | |
*/ | |
function transferFrom(address from, address to, uint256 tokenId) public virtual { | |
if (to == address(0)) { | |
revert ERC721InvalidReceiver(address(0)); | |
} | |
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists | |
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. | |
address previousOwner = _update(to, tokenId, _msgSender()); | |
if (previousOwner != from) { | |
revert ERC721IncorrectOwner(from, tokenId, previousOwner); | |
} | |
} | |
/** | |
* @dev See {IERC721-safeTransferFrom}. | |
*/ | |
function safeTransferFrom(address from, address to, uint256 tokenId) public { | |
safeTransferFrom(from, to, tokenId, ""); | |
} | |
/** | |
* @dev See {IERC721-safeTransferFrom}. | |
*/ | |
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { | |
transferFrom(from, to, tokenId); | |
_checkOnERC721Received(from, to, tokenId, data); | |
} | |
/** | |
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist | |
* | |
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the | |
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances | |
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by | |
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. | |
*/ | |
function _ownerOf(uint256 tokenId) internal view virtual returns (address) { | |
return _tokens[tokenId].owner; | |
} | |
/** | |
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. | |
*/ | |
function _getApproved(uint256 tokenId) internal view virtual returns (address) { | |
return _tokens[tokenId].approval; | |
} | |
/** | |
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in | |
* particular (ignoring whether it is owned by `owner`). | |
* | |
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this | |
* assumption. | |
*/ | |
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { | |
return | |
spender != address(0) && | |
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); | |
} | |
/** | |
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. | |
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets | |
* the `spender` for the specific `tokenId`. | |
* | |
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this | |
* assumption. | |
*/ | |
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { | |
if (!_isAuthorized(owner, spender, tokenId)) { | |
if (owner == address(0)) { | |
revert ERC721NonexistentToken(tokenId); | |
} else { | |
revert ERC721InsufficientApproval(spender, tokenId); | |
} | |
} | |
} | |
/** | |
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. | |
* | |
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that | |
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values. | |
* | |
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the | |
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership | |
* remain consistent with one another. | |
*/ | |
function _increaseBalance(address account, uint128 value) internal virtual { | |
unchecked { | |
_accounts[account].balance += value; | |
} | |
} | |
/** | |
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner | |
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. | |
* | |
* The `auth` argument is optional. If the value passed is non 0, then this function will check that | |
* `auth` is either the owner of the token, or approved to operate on the token (by the owner). | |
* | |
* Emits a {Transfer} event. | |
* | |
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. | |
*/ | |
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { | |
address from = _ownerOf(tokenId); | |
// Perform (optional) operator check | |
if (auth != address(0)) { | |
_checkAuthorized(from, auth, tokenId); | |
} | |
// Execute the update | |
if (from != address(0)) { | |
// Clear approval. No need to re-authorize or emit the Approval event | |
_approve(address(0), tokenId, address(0), false); | |
unchecked { | |
_accounts[from].balance -= 1; | |
} | |
} | |
if (to != address(0)) { | |
unchecked { | |
_accounts[to].balance += 1; | |
} | |
} | |
_tokens[tokenId].owner = to; | |
emit Transfer(from, to, tokenId); | |
return from; | |
} | |
/** | |
* @dev Mints `tokenId` and transfers it to `to`. | |
* | |
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible | |
* | |
* Requirements: | |
* | |
* - `tokenId` must not exist. | |
* - `to` cannot be the zero address. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function _mint(address to, uint256 tokenId) internal { | |
if (to == address(0)) { | |
revert ERC721InvalidReceiver(address(0)); | |
} | |
address previousOwner = _update(to, tokenId, address(0)); | |
if (previousOwner != address(0)) { | |
revert ERC721InvalidSender(address(0)); | |
} | |
} | |
/** | |
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. | |
* | |
* Requirements: | |
* | |
* - `tokenId` must not exist. | |
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function _safeMint(address to, uint256 tokenId) internal { | |
_safeMint(to, tokenId, ""); | |
} | |
/** | |
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is | |
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients. | |
*/ | |
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { | |
_mint(to, tokenId); | |
_checkOnERC721Received(address(0), to, tokenId, data); | |
} | |
/** | |
* @dev Destroys `tokenId`. | |
* The approval is cleared when the token is burned. | |
* This is an internal function that does not check if the sender is authorized to operate on the token. | |
* | |
* Requirements: | |
* | |
* - `tokenId` must exist. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function _burn(uint256 tokenId) internal { | |
address previousOwner = _update(address(0), tokenId, address(0)); | |
if (previousOwner == address(0)) { | |
revert ERC721NonexistentToken(tokenId); | |
} | |
} | |
/** | |
* @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(address from, address to, uint256 tokenId) internal { | |
if (to == address(0)) { | |
revert ERC721InvalidReceiver(address(0)); | |
} | |
address previousOwner = _update(to, tokenId, address(0)); | |
if (previousOwner == address(0)) { | |
revert ERC721NonexistentToken(tokenId); | |
} else if (previousOwner != from) { | |
revert ERC721IncorrectOwner(from, tokenId, previousOwner); | |
} | |
} | |
/** | |
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients | |
* are aware of the ERC721 standard 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 like {safeTransferFrom} in the sense that it invokes | |
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. | |
* implement alternative mechanisms to perform token transfer, such as signature-based. | |
* | |
* Requirements: | |
* | |
* - `tokenId` token must exist and be owned by `from`. | |
* - `to` cannot be the zero address. | |
* - `from` cannot be the zero address. | |
* - 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(address from, address to, uint256 tokenId) internal { | |
_safeTransfer(from, to, tokenId, ""); | |
} | |
/** | |
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is | |
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients. | |
*/ | |
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { | |
_transfer(from, to, tokenId); | |
_checkOnERC721Received(from, to, tokenId, data); | |
} | |
/** | |
* @dev Approve `to` to operate on `tokenId` | |
* | |
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is | |
* either the owner of the token, or approved to operate on all tokens held by this owner. | |
* | |
* Emits an {Approval} event. | |
* | |
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. | |
*/ | |
function _approve(address to, uint256 tokenId, address auth) internal { | |
_approve(to, tokenId, auth, true); | |
} | |
/** | |
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not | |
* emitted in the context of transfers. | |
*/ | |
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { | |
// Avoid reading the owner unless necessary | |
if (emitEvent || auth != address(0)) { | |
address owner = _requireOwned(tokenId); | |
// We do not use _isAuthorized because single-token approvals should not be able to call approve | |
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { | |
revert ERC721InvalidApprover(auth); | |
} | |
if (emitEvent) { | |
emit Approval(owner, to, tokenId); | |
} | |
} | |
_tokens[tokenId].approval = to; | |
} | |
/** | |
* @dev Approve `operator` to operate on all of `owner` tokens | |
* | |
* Requirements: | |
* - operator can't be the address zero. | |
* | |
* Emits an {ApprovalForAll} event. | |
*/ | |
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { | |
if (operator == address(0)) { | |
revert ERC721InvalidOperator(operator); | |
} | |
_accounts[owner].operators[operator] = approved; | |
emit ApprovalForAll(owner, operator, approved); | |
} | |
/** | |
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). | |
* Returns the owner. | |
* | |
* Overrides to ownership logic should be done to {_ownerOf}. | |
*/ | |
function _requireOwned(uint256 tokenId) internal view returns (address) { | |
address owner = _ownerOf(tokenId); | |
if (owner == address(0)) { | |
revert ERC721NonexistentToken(tokenId); | |
} | |
return owner; | |
} | |
/** | |
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the | |
* recipient doesn't accept the token transfer. 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 tokenId uint256 ID of the token to be transferred | |
* @param data bytes optional data to send along with the call | |
*/ | |
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { | |
if (to.code.length > 0) { | |
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { | |
if (retval != IERC721Receiver.onERC721Received.selector) { | |
revert ERC721InvalidReceiver(to); | |
} | |
} catch (bytes memory reason) { | |
if (reason.length == 0) { | |
revert ERC721InvalidReceiver(to); | |
} else { | |
/// @solidity memory-safe-assembly | |
assembly { | |
revert(add(32, reason), mload(reason)) | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// This file was procedurally generated from scripts/generate/templates/Heap.js. | |
pragma solidity ^0.8.20; | |
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | |
import {Panic} from "./Panic.sol"; | |
contract HeapArrayMock { | |
using HeapArray for HeapArray.Uint256Heap; | |
HeapArray.Uint256Heap data; | |
function peek() public view returns (uint256) { return data.peek(); } | |
function pop() public returns (uint256) { return data.pop(); } | |
function insert(uint256 value) public { data.insert(value); } | |
function length() public view returns (uint32) { return data.length(); } | |
function clear() public { data.clear(); } | |
} | |
library HeapArray { | |
using SafeCast for *; | |
struct Uint256Heap { | |
Uint256HeapNode[] data; | |
} | |
struct Uint256HeapNode { | |
uint256 value; | |
uint32 index; // position -> value | |
uint32 lookup; // value -> position | |
} | |
function peek(Uint256Heap storage self) internal view returns (uint256) { | |
return _unsafeNodeAccess(self, self.data[0].index).value; | |
} | |
function pop(Uint256Heap storage self) internal returns (uint256) { | |
unchecked { | |
uint32 size = length(self); | |
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP); | |
uint32 last = size - 1; | |
// get root location (in the data array) and value | |
Uint256HeapNode storage rootNode = _unsafeNodeAccess(self, 0); | |
uint32 rootIdx = rootNode.index; | |
Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx); | |
Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last); | |
uint256 rootDataValue = rootData.value; | |
// if root is not the last element of the data array (that will get pop-ed), reorder the data array. | |
if (rootIdx != last) { | |
// get details about the value stored in the last element of the array (that will get pop-ed) | |
uint32 lastDataIdx = lastNode.lookup; | |
uint256 lastDataValue = lastNode.value; | |
// copy these values to the location of the root (that is safe, and that we no longer use) | |
rootData.value = lastDataValue; | |
rootData.lookup = lastDataIdx; | |
// update the tree node that used to point to that last element (value now located where the root was) | |
_unsafeNodeAccess(self, lastDataIdx).index = rootIdx; | |
} | |
// get last leaf location (in the data array) and value | |
uint32 lastIdx = lastNode.index; | |
uint256 lastValue = _unsafeNodeAccess(self, lastIdx).value; | |
// move the last leaf to the root, pop last leaf ... | |
rootNode.index = lastIdx; | |
_unsafeNodeAccess(self, lastIdx).lookup = 0; | |
self.data.pop(); | |
// ... and heapify | |
_siftDown(self, last, 0, lastValue); | |
// return root value | |
return rootDataValue; | |
} | |
} | |
function insert(Uint256Heap storage self, uint256 value) internal { | |
uint32 size = length(self); | |
if (size == type(uint32).max) Panic.panic(Panic.RESOURCE_ERROR); | |
self.data.push(Uint256HeapNode({index: size, lookup: size, value: value})); | |
_siftUp(self, size, value); | |
} | |
function length(Uint256Heap storage self) internal view returns (uint32) { | |
return self.data.length.toUint32(); | |
} | |
function clear(Uint256Heap storage self) internal { | |
Uint256HeapNode[] storage data = self.data; | |
/// @solidity memory-safe-assembly | |
assembly { | |
sstore(data.slot, 0) | |
} | |
} | |
function _swap(Uint256Heap storage self, uint32 i, uint32 j) private { | |
Uint256HeapNode storage ni = _unsafeNodeAccess(self, i); | |
Uint256HeapNode storage nj = _unsafeNodeAccess(self, j); | |
uint32 ii = ni.index; | |
uint32 jj = nj.index; | |
// update pointers to the data (swap the value) | |
ni.index = jj; | |
nj.index = ii; | |
// update lookup pointers for consistency | |
_unsafeNodeAccess(self, ii).lookup = j; | |
_unsafeNodeAccess(self, jj).lookup = i; | |
} | |
function _siftDown( | |
Uint256Heap storage self, | |
uint32 size, | |
uint32 pos, | |
uint256 value | |
) private { | |
uint256 left = 2 * pos + 1; // this could overflow uint32 | |
uint256 right = 2 * pos + 2; // this could overflow uint32 | |
if (right < size) { | |
// the check guarantees that `left` and `right` are both valid uint32 | |
uint32 lIndex = uint32(left); | |
uint32 rIndex = uint32(right); | |
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; | |
uint256 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value; | |
if (lValue < value || rValue < value) { | |
if (lValue < rValue) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex, value); | |
} else { | |
_swap(self, pos, rIndex); | |
_siftDown(self, size, rIndex, value); | |
} | |
} | |
} else if (left < size) { | |
// the check guarantees that `left` is a valid uint32 | |
uint32 lIndex = uint32(left); | |
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; | |
if (lValue < value) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex, value); | |
} | |
} | |
} | |
function _siftUp( | |
Uint256Heap storage self, | |
uint32 pos, | |
uint256 value | |
) private { | |
unchecked { | |
while (pos > 0) { | |
uint32 parent = (pos - 1) / 2; | |
uint256 parentValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, parent).index).value; | |
if (parentValue < value) break; | |
_swap(self, pos, parent); | |
pos = parent; | |
} | |
} | |
} | |
function _unsafeNodeAccess( | |
Uint256Heap storage self, | |
uint32 pos | |
) private pure returns (Uint256HeapNode storage result) { | |
assembly ("memory-safe") { | |
mstore(0x00, self.slot) | |
result.slot := add(keccak256(0x00, 0x20), mul(pos, 2)) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// This file was procedurally generated from scripts/generate/templates/Heap.js. | |
pragma solidity ^0.8.20; | |
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | |
import {Panic} from "./Panic.sol"; | |
contract HeapArray2Mock { | |
using HeapArray2 for HeapArray2.Uint256Heap; | |
HeapArray2.Uint256Heap data; | |
function peek() public view returns (uint256) { return data.peek(); } | |
function pop() public returns (uint256) { return data.pop(); } | |
function insert(uint256 value) public { data.insert(value); } | |
function length() public view returns (uint32) { return data.length(); } | |
function clear() public { data.clear(); } | |
} | |
library HeapArray2 { | |
using SafeCast for *; | |
struct Uint256Heap { | |
bytes32 _placeholder_do_not_use; | |
} | |
struct Uint256HeapNode { | |
uint256 value; | |
uint32 index; // position -> value | |
uint32 lookup; // value -> position | |
} | |
struct Uint256HeapLength { | |
uint256 value; | |
} | |
function peek(Uint256Heap storage self) internal view returns (uint256) { | |
uint32 size = length(self); | |
if (size == 0) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS); | |
return _unsafeNodeAccess(self, _unsafeNodeAccess(self, 0).index).value; | |
} | |
function pop(Uint256Heap storage self) internal returns (uint256) { | |
unchecked { | |
uint32 size = length(self); | |
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP); | |
uint32 last = size - 1; | |
// get root location (in the data array) and value | |
Uint256HeapNode storage rootNode = _unsafeNodeAccess(self, 0); | |
uint32 rootIdx = rootNode.index; | |
Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx); | |
Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last); | |
uint256 rootDataValue = rootData.value; | |
// if root is not the last element of the data array (that will get pop-ed), reorder the data array. | |
if (rootIdx != last) { | |
// get details about the value stored in the last element of the array (that will get pop-ed) | |
uint32 lastDataIdx = lastNode.lookup; | |
uint256 lastDataValue = lastNode.value; | |
// copy these values to the location of the root (that is safe, and that we no longer use) | |
rootData.value = lastDataValue; | |
rootData.lookup = lastDataIdx; | |
// update the tree node that used to point to that last element (value now located where the root was) | |
_unsafeNodeAccess(self, lastDataIdx).index = rootIdx; | |
} | |
// get last leaf location (in the data array) and value | |
uint32 lastIdx = lastNode.index; | |
uint256 lastValue = _unsafeNodeAccess(self, lastIdx).value; | |
// move the last leaf to the root, pop last leaf ... | |
rootNode.index = lastIdx; | |
_unsafeNodeAccess(self, lastIdx).lookup = 0; | |
// self.data.pop(); | |
--_unsafeLengthAccess(self).value; | |
// ... and heapify | |
_siftDown(self, last, 0, lastValue); | |
// return root value | |
return rootDataValue; | |
} | |
} | |
function insert(Uint256Heap storage self, uint256 value) internal { | |
uint32 size = length(self); | |
if (size == type(uint32).max) Panic.panic(Panic.RESOURCE_ERROR); | |
// self.data.push(Uint256HeapNode({index: size, lookup: size, value: value})); | |
Uint256HeapNode storage node = _unsafeNodeAccess(self, size); | |
node.index = size; | |
node.lookup = size; | |
node.value = value; | |
++_unsafeLengthAccess(self).value; | |
_siftUp(self, size, value); | |
} | |
function length(Uint256Heap storage self) internal view returns (uint32) { | |
return _unsafeLengthAccess(self).value.toUint32(); | |
} | |
function clear(Uint256Heap storage self) internal { | |
_unsafeLengthAccess(self).value = 0; | |
// Uint256HeapNode[] storage data = self.data; | |
// /// @solidity memory-safe-assembly | |
// assembly { | |
// sstore(data.slot, 0) | |
// } | |
} | |
function _swap(Uint256Heap storage self, uint32 i, uint32 j) private { | |
Uint256HeapNode storage ni = _unsafeNodeAccess(self, i); | |
Uint256HeapNode storage nj = _unsafeNodeAccess(self, j); | |
uint32 ii = ni.index; | |
uint32 jj = nj.index; | |
// update pointers to the data (swap the value) | |
ni.index = jj; | |
nj.index = ii; | |
// update lookup pointers for consistency | |
_unsafeNodeAccess(self, ii).lookup = j; | |
_unsafeNodeAccess(self, jj).lookup = i; | |
} | |
function _siftDown( | |
Uint256Heap storage self, | |
uint32 size, | |
uint32 pos, | |
uint256 value | |
) private { | |
uint256 left = 2 * pos + 1; // this could overflow uint32 | |
uint256 right = 2 * pos + 2; // this could overflow uint32 | |
if (right < size) { | |
// the check guarantees that `left` and `right` are both valid uint32 | |
uint32 lIndex = uint32(left); | |
uint32 rIndex = uint32(right); | |
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; | |
uint256 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value; | |
if (lValue < value || rValue < value) { | |
if (lValue < rValue) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex, value); | |
} else { | |
_swap(self, pos, rIndex); | |
_siftDown(self, size, rIndex, value); | |
} | |
} | |
} else if (left < size) { | |
// the check guarantees that `left` is a valid uint32 | |
uint32 lIndex = uint32(left); | |
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; | |
if (lValue < value) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex, value); | |
} | |
} | |
} | |
function _siftUp( | |
Uint256Heap storage self, | |
uint32 pos, | |
uint256 value | |
) private { | |
unchecked { | |
while (pos > 0) { | |
uint32 parent = (pos - 1) / 2; | |
uint256 parentValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, parent).index).value; | |
if (parentValue < value) break; | |
_swap(self, pos, parent); | |
pos = parent; | |
} | |
} | |
} | |
function _unsafeNodeAccess( | |
Uint256Heap storage self, | |
uint32 pos | |
) private pure returns (Uint256HeapNode storage result) { | |
assembly ("memory-safe") { | |
mstore(0x00, self.slot) | |
result.slot := add(keccak256(0x00, 0x20), add(mul(pos, 2), 1)) | |
} | |
} | |
function _unsafeLengthAccess( | |
Uint256Heap storage self | |
) private pure returns (Uint256HeapLength storage result) { | |
assembly ("memory-safe") { | |
mstore(0x00, self.slot) | |
result.slot := keccak256(0x00, 0x20) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// This file was procedurally generated from scripts/generate/templates/Heap.js. | |
pragma solidity ^0.8.20; | |
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | |
import {Panic} from "./Panic.sol"; | |
contract HeapMappingMock { | |
using HeapMapping for HeapMapping.Uint256Heap; | |
HeapMapping.Uint256Heap data; | |
function peek() public view returns (uint256) { return data.peek(); } | |
function pop() public returns (uint256) { return data.pop(); } | |
function insert(uint256 value) public { data.insert(value); } | |
function length() public view returns (uint32) { return uint32(data.size); } | |
} | |
library HeapMapping { | |
using SafeCast for *; | |
struct Uint256Heap { | |
mapping(uint32 => uint32) tree; | |
mapping(uint32 => Node) items; | |
uint32 size; | |
uint32 nextItemIdx; | |
} | |
struct Node { | |
uint256 value; | |
uint32 heapIndex; // value -> position | |
} | |
function peek(Uint256Heap storage self) internal view returns (uint256) { | |
return self.items[self.tree[0]].value; | |
} | |
function pop(Uint256Heap storage self) internal returns (uint256) { | |
unchecked { | |
uint32 last = --self.size; | |
uint32 rootIdx = self.tree[0]; | |
uint256 rootValue = self.items[rootIdx].value; | |
delete self.items[rootIdx]; | |
self.tree[0] = self.tree[last]; | |
self.items[self.tree[0]].heapIndex = 0; | |
delete self.tree[last]; | |
_siftDown(self, last, 0); | |
return rootValue; | |
} | |
} | |
function insert(Uint256Heap storage self, uint256 value) internal { | |
uint32 pos = self.size++; | |
uint32 idx = self.nextItemIdx++; | |
self.tree[pos] = idx; | |
self.items[idx] = Node({ value: value, heapIndex: pos }); | |
_siftUp(self, pos, value); | |
} | |
function length(Uint256Heap storage self) internal view returns (uint32) { | |
return self.size; | |
} | |
function _swap(Uint256Heap storage self, uint32 i, uint32 j) private { | |
uint32 ii = self.tree[i]; | |
uint32 jj = self.tree[j]; | |
self.tree[i] = jj; | |
self.tree[j] = ii; | |
self.items[ii].heapIndex = j; | |
self.items[jj].heapIndex = i; | |
} | |
function _siftDown(Uint256Heap storage self, uint32 size, uint32 pos) private { | |
uint256 left = 2 * pos + 1; // this could overflow uint32 | |
uint256 right = 2 * pos + 2; // this could overflow uint32 | |
if (right < size) { | |
// the check guarantees that `left` and `right` are both valid uint32 | |
uint32 lIndex = uint32(left); | |
uint32 rIndex = uint32(right); | |
uint256 lValue = self.items[self.tree[lIndex]].value; | |
uint256 rValue = self.items[self.tree[rIndex]].value; | |
uint256 value = self.items[pos].value; | |
if (lValue < value || rValue < value) { | |
if (lValue < rValue) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex); | |
} else { | |
_swap(self, pos, rIndex); | |
_siftDown(self, size, rIndex); | |
} | |
} | |
} else if (left < size) { | |
// the check guarantees that `left` is a valid uint32 | |
uint32 lIndex = uint32(left); | |
uint256 lValue = self.items[self.tree[lIndex]].value; | |
uint256 value = self.items[pos].value; | |
if (lValue < value) { | |
_swap(self, pos, lIndex); | |
_siftDown(self, size, lIndex); | |
} | |
} | |
} | |
function _siftUp( | |
Uint256Heap storage self, | |
uint32 pos, | |
uint256 value | |
) private { | |
unchecked { | |
while (pos > 0) { | |
uint32 parent = (pos - 1) / 2; | |
uint256 parentValue = self.items[self.tree[parent]].value; | |
if (parentValue < value) break; | |
_swap(self, pos, parent); | |
pos = parent; | |
} | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.20; | |
/** | |
* @dev Helper library for emitting standardized panic codes. | |
* | |
* ```solidity | |
* contract Example { | |
* using Panic for uint256; | |
* | |
* // Use any of the declared internal constants | |
* function foo() { Panic.GENERIC.panic(); } | |
* | |
* // Alternatively | |
* function foo() { Panic.panic(Panic.GENERIC); } | |
* } | |
* ``` | |
* | |
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. | |
*/ | |
// slither-disable-next-line unused-state | |
library Panic { | |
/// @dev generic / unspecified error | |
uint256 internal constant GENERIC = 0x00; | |
/// @dev used by the assert() builtin | |
uint256 internal constant ASSERT = 0x01; | |
/// @dev arithmetic underflow or overflow | |
uint256 internal constant UNDER_OVERFLOW = 0x11; | |
/// @dev division or modulo by zero | |
uint256 internal constant DIVISION_BY_ZERO = 0x12; | |
/// @dev enum conversion error | |
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; | |
/// @dev invalid encoding in storage | |
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; | |
/// @dev empty array pop | |
uint256 internal constant EMPTY_ARRAY_POP = 0x31; | |
/// @dev array out of bounds access | |
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; | |
/// @dev resource error (too large allocation or too large array) | |
uint256 internal constant RESOURCE_ERROR = 0x41; | |
/// @dev calling invalid internal function | |
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; | |
/// @dev Reverts with a panic code. Recommended to use with | |
/// the internal constants with predefined codes. | |
function panic(uint256 code) internal pure { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x00, 0x4e487b71) | |
mstore(0x20, code) | |
revert(0x1c, 0x24) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment