diff --git a/README.md b/README.md index 8c3365f1..9365df3a 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,12 @@ The library consists of modules and utilities that are built with a help of [Ope - Implementation of [**Contracts Registry**](https://eips.ethereum.org/EIPS/eip-6224) pattern - Versatile access control smart contracts (Merkle whitelists, RBAC) - Enhanced and simplified [**Diamond**](https://eips.ethereum.org/EIPS/eip-2535) pattern -- Advanced data structures (**Vector**, **PriorityQueue**) +- Advanced data structures (**Vector**, **PriorityQueue**, **AVLTree**) - ZK-friendly [**Sparse Merkle Tree**](https://docs.iden3.io/publications/pdfs/Merkle-Tree.pdf) and [**Incremental Merkle Tree**](https://github.com/runtimeverification/deposit-contract-verification/blob/master/deposit-contract-verification.pdf) implementations - Flexible finance primitives (**Staking**, **Vesting**) - Robust UniswapV2 and UniswapV3 oracles -- Utilities to ease work with ERC20 decimals, arrays, sets and ZK proofs +- Lightweight SBT implementation +- Utilities to ease work with memory, ERC20 decimals, arrays, sets and ZK proofs ## Overview diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol new file mode 100644 index 00000000..1c0c04d8 --- /dev/null +++ b/contracts/libs/data-structures/AvlTree.sol @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + +/** + * @notice AVL Tree module + * + * This library provides implementation of three sets with dynamic `value` types: + * `UintAVL`, `Bytes32AVL` and `AddressAVL`. + * + * Each element in the tree has a bytes32 `key` field to allow storing values + * associated with different types of keys + * + * The implementation supports setting custom comparator function + * + * Gas usage for _insert and _remove functions (where count is the number of elements added to the tree): + * + * | Statistic | _insert | _remove | + * | --------- | ------------ | ---------------- | + * | count | 5000 | 5000 | + * | mean | 222,578 gas | 115,744 gas | + * | min | 110,520 gas | 34,461 gas | + * | max | 263,275 gas | 171,815 gas | + * + * ## Usage example: + * + * ``` + * using AvlTree for AvlTree.UintAVL; + * using Traversal for Traversal.Iterator; + * + * AvlTree.UintAVL internal uintTree; + * + * ................................................ + * + * uintTree.setComparator(comparatorFunction); + * + * uintTree.insert(bytes32(1), 1234); + * uintTree.insert(bytes32(3), 100); + * + * uintTree.tryGet(bytes32(1)); + * + * uintTree.remove(bytes32(1)); + * + * ................................................ + * + * Traversal.Iterator memory iterator_ = uintTree.first(); + * + * bytes32[] memory keys_ = new bytes32[](_uintTree.size()); + * bytes32[] memory values_ = new bytes32[](_uintTree.size()); + * + * while (iterator_.isValid()) { + * (keys_[i], values_[i]) = iterator_.value(); + * iterator_.next(); + * } + * ``` + */ +library AvlTree { + /** + ********************* + * UintAVL * + ********************* + */ + + struct UintAVL { + Tree _tree; + } + + /** + * @notice The function to set a custom comparator function, that will be used to build the uint256 tree. + * @param tree self. + * @param comparator_ The function that accepts keys of the nodes to compare. + */ + function setComparator( + UintAVL storage tree, + function(bytes32, bytes32) view returns (int256) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + /** + * @notice The function to insert a node into the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key to insert. + * @param value_ the value to insert. + */ + function insert(UintAVL storage tree, bytes32 key_, uint256 value_) internal { + _insert(tree._tree, key_, bytes32(value_)); + } + + /** + * @notice The function to remove a node from the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ + function remove(UintAVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); + } + + /** + * @notice The function to retrieve the value associated with a key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. + */ + function get(UintAVL storage tree, bytes32 key_) internal view returns (uint256) { + return uint256(_get(tree._tree, key_)); + } + + /** + * @notice The function to try to retrieve the value associated with a key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. + * @return The value associated with the key. + */ + function tryGet(UintAVL storage tree, bytes32 key_) internal view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _tryGet(tree._tree, key_); + + return (exists_, uint256(value_)); + } + + /** + * @notice The function to retrieve the size of the uint256 tree. + * @param tree self. + * @return The size of the tree. + */ + function size(UintAVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); + } + + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the uint256 tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ + function first(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); + } + + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the uint256 tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ + function last(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); + } + + /** + * @notice The function to check whether the custom comparator function is set for the uint256 tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ + function isCustomComparatorSet(UintAVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ********************** + * Bytes32AVL * + ********************** + */ + + struct Bytes32AVL { + Tree _tree; + } + + /** + * @notice The function to set a custom comparator function, that will be used to build the bytes32 tree. + * @param tree self. + * @param comparator_ The function that accepts keys and values of the nodes to compare. + */ + function setComparator( + Bytes32AVL storage tree, + function(bytes32, bytes32) view returns (int256) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + /** + * @notice The function to insert a node into the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key to insert. + * @param value_ the value to insert. + */ + function insert(Bytes32AVL storage tree, bytes32 key_, bytes32 value_) internal { + _insert(tree._tree, key_, value_); + } + + /** + * @notice The function to remove a node from the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ + function remove(Bytes32AVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); + } + + /** + * @notice The function to retrieve the value associated with a key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. + */ + function get(Bytes32AVL storage tree, bytes32 key_) internal view returns (bytes32) { + return _get(tree._tree, key_); + } + + /** + * @notice The function to try to retrieve the value associated with a key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. + * @return The value associated with the key. + */ + function tryGet(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool, bytes32) { + return _tryGet(tree._tree, key_); + } + + /** + * @notice The function to retrieve the size of the bytes32 tree. + * @param tree self. + * @return The size of the tree. + */ + function size(Bytes32AVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); + } + + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the bytes32 tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ + function first(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); + } + + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the bytes32 tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ + function last(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); + } + + /** + * @notice The function to check whether the custom comparator function is set for the bytes32 tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ + function isCustomComparatorSet(Bytes32AVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ********************** + * AddressAVL * + ********************** + */ + + struct AddressAVL { + Tree _tree; + } + + /** + * @notice The function to set a custom comparator function, that will be used to build the address tree. + * @param tree self. + * @param comparator_ The function that accepts keys and values of the nodes to compare. + */ + function setComparator( + AddressAVL storage tree, + function(bytes32, bytes32) view returns (int256) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + /** + * @notice The function to insert a node into the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ The key to insert. + * @param value_ The value to insert. + */ + function insert(AddressAVL storage tree, bytes32 key_, address value_) internal { + _insert(tree._tree, key_, bytes32(uint256(uint160(value_)))); + } + + /** + * @notice The function to remove a node from the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ + function remove(AddressAVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); + } + + /** + * @notice The function to retrieve the value associated with a key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. + */ + function get(AddressAVL storage tree, bytes32 key_) internal view returns (address) { + return address(uint160(uint256(_get(tree._tree, key_)))); + } + + /** + * @notice The function to try to retrieve the value associated with a key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. + * + * @param tree self. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. + * @return The value associated with the key. + */ + function tryGet(AddressAVL storage tree, bytes32 key_) internal view returns (bool, address) { + (bool exists_, bytes32 value_) = _tryGet(tree._tree, key_); + + return (exists_, address(uint160(uint256(value_)))); + } + + /** + * @notice The function to retrieve the size of the address tree. + * @param tree self. + * @return The size of the tree. + */ + function size(AddressAVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); + } + + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the address tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ + function first(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); + } + + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the address tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ + function last(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); + } + + /** + * @notice The function to check whether the custom comparator function is set for the address tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ + function isCustomComparatorSet(AddressAVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ************************* + * Internal Tree * + ************************* + */ + + struct Node { + bytes32 key; + bytes32 value; + uint64 height; + uint64 parent; + uint64 left; + uint64 right; + } + + struct Tree { + uint64 root; + uint64 totalCount; + uint64 removedCount; + bool isCustomComparatorSet; + mapping(uint64 => Node) tree; + function(bytes32, bytes32) view returns (int256) comparator; + } + + function _setComparator( + Tree storage tree, + function(bytes32, bytes32) view returns (int256) comparator_ + ) private { + require(_size(tree) == 0, "AvlTree: the tree must be empty"); + + tree.isCustomComparatorSet = true; + + tree.comparator = comparator_; + } + + function _insert(Tree storage tree, bytes32 key_, bytes32 value_) private { + require(key_ != 0, "AvlTree: key is not allowed to be 0"); + + tree.totalCount++; + + tree.root = _insertNode( + tree.tree, + tree.totalCount, + tree.root, + 0, + key_, + value_, + _getComparator(tree) + ); + } + + function _remove(Tree storage tree, bytes32 key_) private { + require(key_ != 0, "AvlTree: key is not allowed to be 0"); + + tree.root = _removeNode(tree.tree, tree.root, 0, bytes32(key_), _getComparator(tree)); + + tree.removedCount++; + } + + function _insertNode( + mapping(uint64 => Node) storage _tree, + uint64 index_, + uint64 node_, + uint64 parent_, + bytes32 key_, + bytes32 value_, + function(bytes32, bytes32) view returns (int256) comparator_ + ) private returns (uint64) { + int256 comparison_ = comparator_(key_, _tree[node_].key); + + if (_tree[node_].key == 0) { + _tree[index_] = Node({ + key: key_, + value: value_, + parent: parent_, + left: 0, + right: 0, + height: 1 + }); + + return index_; + } + + if (comparison_ < 0) { + _tree[node_].left = _insertNode( + _tree, + index_, + _tree[node_].left, + node_, + key_, + value_, + comparator_ + ); + } else if (comparison_ == 0) { + revert("AvlTree: the node already exists"); + } else { + _tree[node_].right = _insertNode( + _tree, + index_, + _tree[node_].right, + node_, + key_, + value_, + comparator_ + ); + } + + return _balance(_tree, node_); + } + + function _removeNode( + mapping(uint64 => Node) storage _tree, + uint64 node_, + uint64 parent_, + bytes32 key_, + function(bytes32, bytes32) view returns (int256) comparator_ + ) private returns (uint64) { + require(node_ != 0, "AvlTree: the node doesn't exist"); + + int256 comparison_ = comparator_(key_, _tree[node_].key); + + if (comparison_ == 0) { + uint64 left_ = _tree[node_].left; + uint64 right_ = _tree[node_].right; + + delete _tree[node_]; + + if (right_ == 0) { + if (left_ != 0) { + _tree[left_].parent = parent_; + } + + return left_; + } + + uint64 temp_; + + for (temp_ = right_; _tree[temp_].left != 0; temp_ = _tree[temp_].left) {} + + right_ = _removeMin(_tree, right_); + + _tree[temp_].left = left_; + _tree[temp_].right = right_; + + if (left_ != 0) { + _tree[left_].parent = temp_; + } + + if (right_ != 0) { + _tree[right_].parent = temp_; + } + + _tree[temp_].parent = parent_; + + return _balance(_tree, temp_); + } else if (comparison_ < 0) { + _tree[node_].left = _removeNode(_tree, _tree[node_].left, node_, key_, comparator_); + } else { + _tree[node_].right = _removeNode(_tree, _tree[node_].right, node_, key_, comparator_); + } + + return _balance(_tree, node_); + } + + function _removeMin( + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { + Node storage _node = _tree[node_]; + + if (_node.left == 0) { + if (_node.right != 0) { + _tree[_node.right].parent = _node.parent; + } + + return _node.right; + } + + _node.left = _removeMin(_tree, _node.left); + + return _balance(_tree, node_); + } + + function _rotateRight( + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { + Node storage _node = _tree[node_]; + + uint64 temp_ = _node.left; + + _tree[temp_].parent = _node.parent; + _node.parent = temp_; + + if (_tree[temp_].right != 0) { + _tree[_tree[temp_].right].parent = node_; + } + + _node.left = _tree[temp_].right; + _tree[temp_].right = node_; + + _updateHeight(_tree, node_); + _updateHeight(_tree, temp_); + + return temp_; + } + + function _rotateLeft( + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { + Node storage _node = _tree[node_]; + + uint64 temp_ = _node.right; + + _tree[temp_].parent = _node.parent; + _node.parent = temp_; + + if (_tree[temp_].left != 0) { + _tree[_tree[temp_].left].parent = node_; + } + + _node.right = _tree[temp_].left; + _tree[temp_].left = node_; + + _updateHeight(_tree, node_); + _updateHeight(_tree, temp_); + + return temp_; + } + + function _balance( + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { + _updateHeight(_tree, node_); + + Node storage _left = _tree[_tree[node_].left]; + Node storage _right = _tree[_tree[node_].right]; + + if (_left.height > _right.height + 1) { + if (_tree[_left.right].height > _tree[_left.left].height) { + _tree[node_].left = _rotateLeft(_tree, _tree[node_].left); + } + + return _rotateRight(_tree, node_); + } else if (_right.height > _left.height + 1) { + if (_tree[_right.left].height > _tree[_right.right].height) { + _tree[node_].right = _rotateRight(_tree, _tree[node_].right); + } + + return _rotateLeft(_tree, node_); + } + + return node_; + } + + function _updateHeight(mapping(uint64 => Node) storage _tree, uint64 node_) private { + Node storage _node = _tree[node_]; + + _node.height = uint64(1 + Math.max(_tree[_node.left].height, _tree[_node.right].height)); + } + + function _search( + mapping(uint64 => Node) storage _tree, + uint64 node_, + bytes32 key_, + function(bytes32, bytes32) view returns (int256) comparator_ + ) private view returns (uint64) { + if (node_ == 0) { + return 0; + } + + int256 comparison_ = comparator_(key_, _tree[node_].key); + + if (comparison_ == 0) { + return node_; + } else if (comparison_ < 0) { + return _search(_tree, _tree[node_].left, key_, comparator_); + } else { + return _search(_tree, _tree[node_].right, key_, comparator_); + } + } + + function _get(Tree storage tree, bytes32 key_) private view returns (bytes32) { + uint64 index_ = _search(tree.tree, tree.root, key_, _getComparator(tree)); + + require(index_ != 0, "AvlTree: the node doesn't exist"); + + return tree.tree[index_].value; + } + + function _tryGet(Tree storage tree, bytes32 key_) private view returns (bool, bytes32) { + uint64 index_ = _search(tree.tree, tree.root, key_, _getComparator(tree)); + + if (index_ == 0) { + return (false, 0); + } + + return (true, tree.tree[index_].value); + } + + function _size(Tree storage tree) private view returns (uint256) { + return tree.totalCount - tree.removedCount; + } + + function _first(Tree storage tree) private view returns (Traversal.Iterator memory) { + uint256 treeMappingSlot_; + assembly { + treeMappingSlot_ := add(tree.slot, 1) + } + + uint64 current_ = tree.root; + while (tree.tree[current_].left != 0) { + current_ = tree.tree[current_].left; + } + + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: current_}); + } + + function _last(Tree storage tree) private view returns (Traversal.Iterator memory) { + uint256 treeMappingSlot_; + assembly { + treeMappingSlot_ := add(tree.slot, 1) + } + + uint64 current_ = tree.root; + while (tree.tree[current_].right != 0) { + current_ = tree.tree[current_].right; + } + + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: current_}); + } + + function _getComparator( + Tree storage tree + ) private view returns (function(bytes32, bytes32) view returns (int256)) { + return tree.isCustomComparatorSet ? tree.comparator : _defaultComparator; + } + + function _defaultComparator(bytes32 key1_, bytes32 key2_) private pure returns (int256) { + if (key1_ < key2_) { + return -1; + } + + if (key1_ > key2_) { + return 1; + } + + return 0; + } +} + +/** + * @notice Traversal module + * + * This library provides functions to perform an in-order traversal of the AVL Tree + */ +library Traversal { + /** + * @notice Iterator struct to keep track of the current position in the tree. + * @param treeMappingSlot The storage slot of the tree mapping. + * @param currentNode The index of the current node in the traversal. + */ + struct Iterator { + uint256 treeMappingSlot; + uint64 currentNode; + } + + /** + * @notice The function to check if the iterator is currently valid (has not reached the end of the traversal). + * @param iterator_ self. + * @return True if the iterator is valid, false otherwise. + */ + function isValid(Iterator memory iterator_) internal pure returns (bool) { + return iterator_.currentNode != 0; + } + + /** + * @notice The function to check if there is a next node in the traversal. + * @param iterator_ self. + * @return True if there is a next node, false otherwise. + */ + function hasNext(Iterator memory iterator_) internal view returns (bool) { + return _has(iterator_, true); + } + + /** + * @notice The function to check if there is a previous node in the traversal. + * @param iterator_ self. + * @return True if there is a previous node, false otherwise. + */ + function hasPrev(Iterator memory iterator_) internal view returns (bool) { + return _has(iterator_, false); + } + + /** + * @notice The function to move the iterator to the next node and retrieve its key and value. + * @param iterator_ self. + * @return The key of the next node. + * @return The value of the next node. + */ + function next(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + return _moveToAdjacent(iterator_, true); + } + + /** + * @notice The function to move the iterator to the previous node and retrieve its key and value. + * @param iterator_ self. + * @return The key of the previous node. + * @return The value of the previous node. + */ + function prev(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + return _moveToAdjacent(iterator_, false); + } + + /** + * @notice The function to retrieve the key and value of the current node. + * @param iterator_ self. + * @return The key of the current node. + * @return The value of the current node. + */ + function value(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + return (node_.key, node_.value); + } + + function _has(Iterator memory iterator_, bool next_) private view returns (bool) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + if (_adjacent(node_, next_) != 0) { + return true; + } + + uint64 currentNodeIndex_ = iterator_.currentNode; + + while (currentNodeIndex_ != 0) { + AvlTree.Node memory parent_ = _getNode(iterator_.treeMappingSlot, node_.parent); + + if (currentNodeIndex_ == _adjacent(parent_, !next_)) { + return true; + } + + currentNodeIndex_ = node_.parent; + node_ = parent_; + } + + return false; + } + + function _moveToAdjacent( + Iterator memory iterator_, + bool next_ + ) internal view returns (bytes32, bytes32) { + uint64 currentNodeIndex_ = iterator_.currentNode; + + require(currentNodeIndex_ != 0, "Traversal: no more nodes"); + + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + + if (_adjacent(node_, next_) != 0) { + currentNodeIndex_ = _adjacent(node_, next_); + + AvlTree.Node memory childNode_ = _getNode( + iterator_.treeMappingSlot, + currentNodeIndex_ + ); + + while (_adjacent(childNode_, !next_) != 0) { + currentNodeIndex_ = _adjacent(childNode_, !next_); + childNode_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + } + } else { + uint64 parentIndex_ = node_.parent; + + AvlTree.Node memory parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + + while (parentIndex_ != 0 && currentNodeIndex_ == _adjacent(parentNode_, next_)) { + currentNodeIndex_ = parentIndex_; + + parentIndex_ = parentNode_.parent; + parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + } + + currentNodeIndex_ = parentIndex_; + } + + iterator_.currentNode = currentNodeIndex_; + + return value(iterator_); + } + + function _adjacent(AvlTree.Node memory node_, bool next_) private pure returns (uint64) { + return next_ ? node_.right : node_.left; + } + + function _getNode( + uint256 slot_, + uint64 index_ + ) private view returns (AvlTree.Node memory node_) { + bytes32 baseSlot_ = keccak256(abi.encode(index_, slot_)); + + assembly { + let valueSlot_ := add(baseSlot_, 1) + let packedSlot_ := add(baseSlot_, 2) + + let packedData_ := sload(packedSlot_) + + mstore(node_, sload(baseSlot_)) + mstore(add(node_, 0x20), sload(valueSlot_)) + mstore(add(node_, 0x40), and(packedData_, 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x60), and(shr(64, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x80), and(shr(128, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0xa0), and(shr(192, packedData_), 0xFFFFFFFFFFFFFFFF)) + } + + return node_; + } +} diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol new file mode 100644 index 00000000..5c39384d --- /dev/null +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {TypeCaster} from "../../../libs/utils/TypeCaster.sol"; + +import {Traversal, AvlTree} from "../../../libs/data-structures/AvlTree.sol"; + +contract AvlTreeMock { + using TypeCaster for *; + using AvlTree for *; + using Traversal for *; + + AvlTree.UintAVL internal _uintTree; + AvlTree.Bytes32AVL internal _bytes32Tree; + AvlTree.AddressAVL internal _addressTree; + + function setUintDescComparator() external { + _uintTree.setComparator(_descComparator); + } + + function setBytes32DescComparator() external { + _bytes32Tree.setComparator(_descComparator); + } + + function setAddressDescComparator() external { + _addressTree.setComparator(_descComparator); + } + + function insertUint(uint256 key_, uint256 value_) external { + _uintTree.insert(bytes32(key_), value_); + } + + function insertBytes32(uint256 key_, bytes32 value_) external { + _bytes32Tree.insert(bytes32(key_), value_); + } + + function insertAddress(uint256 key_, address value_) external { + _addressTree.insert(bytes32(key_), value_); + } + + function removeUint(uint256 key_) external { + _uintTree.remove(bytes32(key_)); + } + + function removeBytes32(uint256 key_) external { + _bytes32Tree.remove(bytes32(key_)); + } + + function removeAddress(uint256 key_) external { + _addressTree.remove(bytes32(key_)); + } + + function getUint(uint256 key_) external view returns (uint256) { + return _uintTree.get(bytes32(key_)); + } + + function getBytes32(uint256 key_) external view returns (bytes32) { + return _bytes32Tree.get(bytes32(key_)); + } + + function getAddressValue(uint256 key_) external view returns (address) { + return _addressTree.get(bytes32(key_)); + } + + function tryGetUint(uint256 key_) external view returns (bool, uint256) { + return _uintTree.tryGet(bytes32(key_)); + } + + function tryGetBytes32(uint256 key_) external view returns (bool, bytes32) { + return _bytes32Tree.tryGet(bytes32(key_)); + } + + function tryGetAddress(uint256 key_) external view returns (bool, address) { + return _addressTree.tryGet(bytes32(key_)); + } + + function sizeUint() external view returns (uint64) { + return _uintTree.size(); + } + + function sizeBytes32() external view returns (uint64) { + return _bytes32Tree.size(); + } + + function sizeAddress() external view returns (uint64) { + return _addressTree.size(); + } + + function rootUint() external view returns (uint256) { + return uint256(_uintTree._tree.tree[_uintTree._tree.root].key); + } + + function rootBytes32() external view returns (uint256) { + return uint256(_bytes32Tree._tree.tree[_bytes32Tree._tree.root].key); + } + + function rootAddress() external view returns (uint256) { + return uint256(_addressTree._tree.tree[_addressTree._tree.root].key); + } + + function traverseUint() external view returns (uint256[] memory, uint256[] memory) { + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _uintTree.first(), + _uintTree.size(), + true + ); + + return (keys_, values_.asUint256Array()); + } + + function backwardsTraversalUint() external view returns (uint256[] memory, uint256[] memory) { + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _uintTree.last(), + _uintTree.size(), + false + ); + + return (keys_, values_.asUint256Array()); + } + + function brokenTraversalUint() external view { + Traversal.Iterator memory iterator_ = _uintTree.first(); + + iterator_.currentNode = 0; + iterator_.next(); + } + + function backAndForthTraverseUint() + external + view + returns (uint256[] memory, uint256[] memory) + { + Traversal.Iterator memory iterator_ = _uintTree.first(); + + uint256[] memory keys_ = new uint256[](12); + uint256[] memory values_ = new uint256[](12); + + bool[12] memory directions_ = [ + true, + true, + false, + true, + false, + false, + true, + true, + true, + true, + true, + true + ]; + + bytes32 bytesKey_; + bytes32 bytesValue_; + + for (uint256 i = 0; i < 12; i++) { + (bytesKey_, bytesValue_) = iterator_.value(); + + keys_[i] = uint256(bytesKey_); + values_[i] = uint256(bytesValue_); + + directions_[i] ? iterator_.next() : iterator_.prev(); + } + + return (keys_, values_); + } + + function traverseBytes32() external view returns (uint256[] memory, bytes32[] memory) { + return _traverseAll(_bytes32Tree.first(), _bytes32Tree.size(), true); + } + + function backwardsTraversalBytes32() + external + view + returns (uint256[] memory, bytes32[] memory) + { + return _traverseAll(_bytes32Tree.last(), _bytes32Tree.size(), false); + } + + function traverseAddress() external view returns (uint256[] memory, address[] memory) { + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _addressTree.first(), + _addressTree.size(), + true + ); + + return (keys_, values_.asAddressArray()); + } + + function backwardsTraversalAddress() + external + view + returns (uint256[] memory, address[] memory) + { + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _addressTree.last(), + _addressTree.size(), + false + ); + + return (keys_, values_.asAddressArray()); + } + + function nextOnLast() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.last(); + + (bytes32 key_, bytes32 value_) = iterator_.next(); + + return (uint256(key_), uint256(value_)); + } + + function prevOnFirst() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.first(); + + (bytes32 key_, bytes32 value_) = iterator_.prev(); + + return (uint256(key_), uint256(value_)); + } + + function isCustomComparatorSetUint() external view returns (bool) { + return _uintTree.isCustomComparatorSet(); + } + + function isCustomComparatorSetBytes32() external view returns (bool) { + return _bytes32Tree.isCustomComparatorSet(); + } + + function isCustomComparatorSetAddress() external view returns (bool) { + return _addressTree.isCustomComparatorSet(); + } + + function _traverseAll( + Traversal.Iterator memory iterator_, + uint256 size_, + bool direction_ + ) private view returns (uint256[] memory, bytes32[] memory) { + uint256[] memory keys_ = new uint256[](size_); + bytes32[] memory values_ = new bytes32[](size_); + + uint256 index_; + + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = bytesValue_; + + direction_ ? iterator_.next() : iterator_.prev(); + + index_++; + } + + return (keys_, values_); + } + + function _descComparator(bytes32 key1_, bytes32 key2_) private pure returns (int256) { + if (key1_ > key2_) { + return -1; + } + + if (key1_ < key2_) { + return 1; + } + + return 0; + } +} diff --git a/package-lock.json b/package-lock.json index 88cf618c..46ec3008 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "license": "MIT", "dependencies": { "@openzeppelin/contracts": "4.9.5", @@ -72,21 +72,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -1056,17 +1056,17 @@ } }, "node_modules/@metamask/eth-sig-util": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.1.tgz", - "integrity": "sha512-59GSrMyFH2fPfu7nKeIQdZ150zxXNNhAQIUaFRUW+MGtVA4w/ONbiQobcRBLi+jQProfIyss51G8pfLPcQ0ylg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.2.tgz", + "integrity": "sha512-DhTDMNEtED0ihIc4Tysm6qUJTvArCdgSTeeJWdo526W/cAk5mrSAvEYYgv8idAiBumDtcPWGimMTaB7MvY64bg==", "dev": true, "dependencies": { "@ethereumjs/util": "^8.1.0", "@metamask/abi-utils": "^2.0.2", "@metamask/utils": "^8.1.0", + "@scure/base": "~1.1.3", "ethereum-cryptography": "^2.1.2", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" + "tweetnacl": "^1.0.3" }, "engines": { "node": "^16.20 || ^18.16 || >=20" @@ -1617,9 +1617,9 @@ } }, "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.5.tgz", - "integrity": "sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.6.tgz", + "integrity": "sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -2141,9 +2141,9 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.10.tgz", - "integrity": "sha512-PiaIWIoPvO6qm6t114ropMCagj6YAF24j9OkCA2mJDXFnlionEwhsBCJ8yek4aib575BI3OkART/90WsgHgLWw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "node_modules/@tsconfig/node12": { @@ -2204,9 +2204,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", - "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -2268,9 +2268,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.19.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", - "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2727,9 +2727,9 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -2961,12 +2961,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2978,6 +2978,12 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, + "node_modules/brotli-wasm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-2.0.1.tgz", + "integrity": "sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww==", + "dev": true + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3131,15 +3137,15 @@ } }, "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { @@ -3299,9 +3305,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", - "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -3437,9 +3443,9 @@ } }, "node_modules/core-js": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz", - "integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, "funding": { @@ -3879,9 +3885,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", - "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -3923,11 +3929,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -4083,12 +4089,12 @@ } }, "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz", + "integrity": "sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw==", "dev": true, "dependencies": { - "js-sha3": "^0.8.0" + "@noble/hashes": "^1.4.0" } }, "node_modules/ethereum-cryptography": { @@ -4218,9 +4224,9 @@ } }, "node_modules/ethers": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz", - "integrity": "sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", "dev": true, "funding": [ { @@ -4380,9 +4386,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4717,12 +4723,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4981,9 +4988,9 @@ } }, "node_modules/hardhat-gas-reporter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.0.2.tgz", - "integrity": "sha512-i/+g+dX+/+MZ7L4M5NE78TgjDgnE3dINhsNUJmjwbqR3cLSMPrsEyiee+/1c5w32JSD08SKyDf+8W9Q5iC+zLw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.2.0.tgz", + "integrity": "sha512-eAlLWnyDpQ+wJXgSCZsM0yt+rQm3ryJia1I1Hoi94LzlIfuSPcsMQM12VO6UHmAFLvXvoKxXPJ3ZYk0Kz+7CDQ==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.7.0", @@ -4991,6 +4998,7 @@ "@ethersproject/units": "^5.7.0", "@solidity-parser/parser": "^0.18.0", "axios": "^1.6.7", + "brotli-wasm": "^2.0.1", "chalk": "4.1.2", "cli-table3": "^0.6.3", "ethereum-cryptography": "^2.1.3", @@ -5064,22 +5072,22 @@ "dev": true }, "node_modules/hardhat-gas-reporter/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5095,9 +5103,9 @@ } }, "node_modules/hardhat-gas-reporter/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5541,9 +5549,9 @@ } }, "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "node_modules/import-fresh": { @@ -5581,6 +5589,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -5948,9 +5957,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -6292,9 +6301,9 @@ "dev": true }, "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -6371,12 +6380,12 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6459,9 +6468,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -6489,9 +6498,9 @@ } }, "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -6755,9 +6764,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -7040,16 +7049,16 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7090,9 +7099,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -7126,9 +7135,9 @@ } }, "node_modules/pony-cause": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.10.tgz", - "integrity": "sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", "dev": true, "engines": { "node": ">=12.0.0" @@ -7759,13 +7768,10 @@ "dev": true }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -7773,18 +7779,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -8046,9 +8040,9 @@ } }, "node_modules/solhint": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.2.tgz", - "integrity": "sha512-o7MNYS5QPgE6l+PTGOTAUtCzo0ZLnffQsv586hntSHBe2JbSDfkoxfhAOcjZjN4OesTgaX4UEEjCjH9y/4BP5w==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.4.tgz", + "integrity": "sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ==", "dev": true, "dependencies": { "@solidity-parser/parser": "^0.18.0", @@ -8242,9 +8236,9 @@ "dev": true }, "node_modules/solidity-coverage": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.11.tgz", - "integrity": "sha512-yy0Yk+olovBbXn0Me8BWULmmv7A69ZKkP5aTOJGOO8u61Tu2zS989erfjtFlUjDnfWtxRAVkd8BsQD704yLWHw==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.12.tgz", + "integrity": "sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.0.9", @@ -8257,7 +8251,7 @@ "global-modules": "^2.0.0", "globby": "^10.0.1", "jsonschema": "^1.2.4", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "mocha": "^10.2.0", "node-emoji": "^1.10.0", "pify": "^4.0.1", @@ -8548,9 +8542,9 @@ } }, "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -8597,15 +8591,15 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -9060,9 +9054,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -9110,9 +9104,9 @@ } }, "node_modules/undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" @@ -9572,12 +9566,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index f9d25cdb..b0b52671 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "license": "MIT", "author": "Distributed Lab", "readme": "README.md", diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts new file mode 100644 index 00000000..51847075 --- /dev/null +++ b/test/libs/data-structures/AvlTree.test.ts @@ -0,0 +1,607 @@ +import { ethers } from "hardhat"; +import { encodeBytes32String } from "ethers"; +import { expect } from "chai"; +import { Reverter } from "@/test/helpers/reverter"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; + +import { AvlTreeMock } from "@ethers-v6"; + +import { ZERO_ADDR } from "@/scripts/utils/constants"; + +describe("AvlTree", () => { + const reverter = new Reverter(); + + let USER1: SignerWithAddress; + let USER2: SignerWithAddress; + let USER3: SignerWithAddress; + let USER4: SignerWithAddress; + + let avlTree: AvlTreeMock; + + before(async () => { + [USER1, USER2, USER3, USER4] = await ethers.getSigners(); + const AvlTreeMock = await ethers.getContractFactory("AvlTreeMock"); + + avlTree = await AvlTreeMock.deploy(); + + await reverter.snapshot(); + }); + + afterEach(reverter.revert); + + function uintToBytes32Array(uintArray: Array) { + let bytes32Array = []; + + for (let i = 0; i < uintArray.length; i++) { + bytes32Array.push(encodeBytes32String(uintArray[i].toString())); + } + + return bytes32Array; + } + + describe("Uint Tree", () => { + describe("insert", () => { + it("should insert values to the uint tree correctly", async () => { + await avlTree.insertUint(4, 1); + await avlTree.insertUint(12, 2); + await avlTree.insertUint(1, 3); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.sizeUint()).to.equal(3); + + let traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 12]); + expect(traversal[1]).to.deep.equal([3, 1, 2]); + + await avlTree.insertUint(200, 4); + await avlTree.insertUint(10, 5); + await avlTree.insertUint(5, 6); + + expect(await avlTree.rootUint()).to.equal(10); + expect(await avlTree.sizeUint()).to.equal(6); + + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 200]); + expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 4]); + + await avlTree.insertUint(15, 7); + + expect(await avlTree.rootUint()).to.equal(10); + expect(await avlTree.sizeUint()).to.equal(7); + + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 15, 200]); + expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 7, 4]); + + await avlTree.insertUint(2, 8); + await avlTree.insertUint(3, 9); + + expect(await avlTree.rootUint()).to.equal(10); + expect(await avlTree.sizeUint()).to.equal(9); + + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 3, 4, 5, 10, 12, 15, 200]); + expect(traversal[1]).to.deep.equal([3, 8, 9, 1, 6, 5, 2, 7, 4]); + }); + + it("should handle complex operations on the uint tree", async () => { + await avlTree.insertUint(2, 10); + await avlTree.insertUint(3, 1); + await avlTree.insertUint(10, 18); + + await avlTree.removeUint(3); + + await avlTree.insertUint(4, 20); + await avlTree.insertUint(5, 10); + + await avlTree.removeUint(2); + await avlTree.removeUint(10); + await avlTree.removeUint(4); + + await avlTree.insertUint(4, 7); + await avlTree.insertUint(1, 20); + await avlTree.insertUint(2, 10); + await avlTree.insertUint(20, 20); + await avlTree.insertUint(111, 10); + + await avlTree.removeUint(20); + + await avlTree.insertUint(16, 20); + await avlTree.insertUint(17, 100); + await avlTree.insertUint(18, 20); + await avlTree.insertUint(19, 250); + await avlTree.insertUint(20, 4); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.sizeUint()).to.equal(10); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 16, 17, 18, 19, 20, 111]); + expect(traversal[1]).to.deep.equal([20, 10, 7, 10, 20, 100, 20, 250, 4, 10]); + }); + + it("should not allow to insert 0 as key to the uint tree", async () => { + await expect(avlTree.insertUint(0, 100)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + }); + + it("should not allow to insert node with duplicate key to the uint tree", async () => { + await avlTree.insertUint(2, 10); + await expect(avlTree.insertUint(2, 4)).to.be.revertedWith("AvlTree: the node already exists"); + }); + }); + + describe("remove", () => { + it("should remove a node in the uint tree correctly", async () => { + await avlTree.insertUint(2, 4); + await avlTree.insertUint(3, 8); + await avlTree.insertUint(6, 10); + await avlTree.insertUint(1, 2); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(7, 10); + await avlTree.insertUint(10, 10); + + expect(await avlTree.rootUint()).to.equal(3); + expect(await avlTree.sizeUint()).to.equal(7); + + await avlTree.removeUint(3); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.sizeUint()).to.equal(6); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 4, 6, 7, 10]); + expect(traversal[1]).to.deep.equal([2, 4, 10, 10, 10, 10]); + }); + + it("should handle removing root node in the uint tree correctly", async () => { + await avlTree.setUintDescComparator(); + + await avlTree.insertUint(4, 2); + await avlTree.insertUint(6, 3); + await avlTree.insertUint(2, 22); + await avlTree.insertUint(1, 12); + await avlTree.insertUint(8, 4); + + expect(await avlTree.rootUint()).to.be.equal(4); + + await avlTree.removeUint(4); + + expect(await avlTree.rootUint()).to.be.equal(2); + expect(await avlTree.sizeUint()).to.be.equal(4); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([8, 6, 2, 1]); + expect(traversal[1]).to.deep.equal([4, 3, 22, 12]); + }); + + it("should remove multiple nodes in the uint tree correctly", async () => { + await avlTree.insertUint(1, 2); + await avlTree.insertUint(4, 20); + await avlTree.insertUint(3, 4); + await avlTree.insertUint(7, 1); + await avlTree.insertUint(8, 10); + await avlTree.insertUint(9, 1); + await avlTree.insertUint(5, 12); + await avlTree.insertUint(6, 11); + + await avlTree.removeUint(8); + await avlTree.removeUint(3); + await avlTree.removeUint(5); + + expect(await avlTree.rootUint()).to.equal(6); + expect(await avlTree.sizeUint()).to.equal(5); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 6, 7, 9]); + expect(traversal[1]).to.deep.equal([2, 20, 11, 1, 1]); + }); + + it("should handle removing all the nodes in the uint tree correctly", async () => { + await avlTree.insertUint(2, 1); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(3, 2); + await avlTree.removeUint(4); + await avlTree.removeUint(2); + await avlTree.insertUint(2, 2); + await avlTree.insertUint(1, 2); + await avlTree.removeUint(2); + await avlTree.removeUint(1); + await avlTree.removeUint(3); + + expect(await avlTree.rootUint()).to.be.equal(0); + expect(await avlTree.sizeUint()).to.be.equal(0); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0].length).to.be.equal(0); + expect(traversal[1].length).to.be.equal(0); + }); + + it("should not allow to remove a node that doesn't exist in the uint tree", async () => { + await avlTree.insertUint(1, 10); + + await expect(avlTree.removeUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + await expect(avlTree.removeUint(0)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + }); + + it("should not allow to remove a node twice in the uint tree", async () => { + await avlTree.insertUint(1, 10); + await avlTree.insertUint(2, 20); + + await avlTree.removeUint(2); + + await expect(avlTree.removeUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + }); + + it("should not allow to remove nodes in the empty uint tree", async () => { + await expect(avlTree.removeUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + }); + }); + + describe("setComparator", () => { + it("should set a comparator for the uint tree correctly", async () => { + await avlTree.setUintDescComparator(); + + await avlTree.insertUint(2, 3); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(1, 4); + await avlTree.insertUint(6, 4); + await avlTree.insertUint(3, 200); + await avlTree.insertUint(5, 1); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.sizeUint()).to.equal(6); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(traversal[1]).to.deep.equal([4, 1, 10, 200, 3, 4]); + }); + + it("should not allow to set comparator for the uint tree if the tree is not empty", async () => { + await avlTree.insertUint(1, 10); + + await expect(avlTree.setUintDescComparator()).to.be.revertedWith("AvlTree: the tree must be empty"); + }); + }); + + describe("getters", () => { + it("should get value for the existing node in the uint tree correctly", async () => { + await avlTree.insertUint(1, 4); + await avlTree.insertUint(2, 0); + await avlTree.insertUint(3, 1); + + expect(await avlTree.getUint(1)).to.be.equal(4); + expect(await avlTree.tryGetUint(1)).to.deep.equal([true, 4]); + + expect(await avlTree.getUint(2)).to.deep.equal(0); + expect(await avlTree.tryGetUint(2)).to.deep.equal([true, 0]); + + expect(await avlTree.getUint(3)).to.be.equal(1); + expect(await avlTree.tryGetUint(3)).to.deep.equal([true, 1]); + + await expect(avlTree.getUint(4)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(4)).to.deep.equal([false, 0]); + + await expect(avlTree.getUint(6)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(6)).to.deep.equal([false, 0]); + }); + + it("should handle getting value for the non-existing node in the uint tree correctly", async () => { + await expect(avlTree.getUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(1)).to.deep.equal([false, 0]); + + await avlTree.insertUint(1, 30); + + await expect(avlTree.getUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(2)).to.deep.equal([false, 0]); + + await expect(avlTree.getUint(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(0)).to.deep.equal([false, 0]); + }); + + it("should get the treeSize correctly for the uint tree", async () => { + expect(await avlTree.sizeUint()).to.be.equal(0); + + await avlTree.insertUint(1, 10); + await avlTree.insertUint(2, 10); + await avlTree.removeUint(1); + await avlTree.insertUint(3, 4); + await avlTree.insertUint(7, 90); + + expect(await avlTree.sizeUint()).to.be.equal(3); + }); + + it("should check if custom comparator is set for the uint tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetUint()).to.be.false; + + await avlTree.setUintDescComparator(); + + expect(await avlTree.isCustomComparatorSetUint()).to.be.true; + }); + + it("should traverse the uint tree correctly", async () => { + await avlTree.insertUint(2, 10); + await avlTree.insertUint(4, 11); + await avlTree.insertUint(1, 12); + await avlTree.insertUint(6, 13); + await avlTree.insertUint(7, 14); + + let fullTraversal = await avlTree.traverseUint(); + expect(fullTraversal[0]).to.deep.equal([1, 2, 4, 6, 7]); + expect(fullTraversal[1]).to.deep.equal([12, 10, 11, 13, 14]); + + let backwardsTraversal = await avlTree.backwardsTraversalUint(); + expect(backwardsTraversal[0]).to.deep.equal([7, 6, 4, 2, 1]); + expect(backwardsTraversal[1]).to.deep.equal([14, 13, 11, 10, 12]); + + expect(await avlTree.nextOnLast()).to.deep.equal([0, 0]); + expect(await avlTree.prevOnFirst()).to.deep.equal([0, 0]); + + await expect(avlTree.brokenTraversalUint()).to.be.revertedWith("Traversal: no more nodes"); + }); + + it("should maintain idempotent traversal", async () => { + await avlTree.insertUint(1, 12); + await avlTree.insertUint(6, 22); + await avlTree.insertUint(3, 10); + + await avlTree.removeUint(1); + + await avlTree.insertUint(2, 0); + + await avlTree.removeUint(3); + + await avlTree.insertUint(5, 5); + await avlTree.insertUint(1, 15); + await avlTree.insertUint(3, 1000); + await avlTree.insertUint(4, 44); + + const traversal = await avlTree.backAndForthTraverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 3, 2, 3, 2, 1, 2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([15, 0, 1000, 0, 1000, 0, 15, 0, 1000, 44, 5, 22]); + }); + }); + }); + + describe("Bytes32 Tree", () => { + it("should insert nodes to the bytes32 tree correctly", async () => { + await avlTree.setBytes32DescComparator(); + + await avlTree.insertBytes32(2, encodeBytes32String("20")); + await avlTree.insertBytes32(4, encodeBytes32String("22")); + await avlTree.insertBytes32(1, encodeBytes32String("12")); + await avlTree.insertBytes32(6, encodeBytes32String("2")); + await avlTree.insertBytes32(3, encodeBytes32String("112")); + await avlTree.insertBytes32(5, encodeBytes32String("2")); + + expect(await avlTree.rootBytes32()).to.equal(4); + expect(await avlTree.getBytes32(4)).to.be.equal(encodeBytes32String("22")); + expect(await avlTree.tryGetBytes32(4)).to.deep.equal([true, encodeBytes32String("22")]); + + expect(await avlTree.sizeBytes32()).to.equal(6); + + let fullTraversal = await avlTree.traverseBytes32(); + expect(fullTraversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(fullTraversal[1]).to.deep.equal(uintToBytes32Array([2, 2, 22, 112, 20, 12])); + + let backwardsTraversal = await avlTree.backwardsTraversalBytes32(); + expect(backwardsTraversal[0]).to.deep.equal([1, 2, 3, 4, 5, 6]); + expect(backwardsTraversal[1]).to.deep.equal(uintToBytes32Array([12, 20, 112, 22, 2, 2])); + }); + + it("should remove nodes in the bytes32 tree correctly", async () => { + await avlTree.insertBytes32(2, encodeBytes32String("2")); + await avlTree.insertBytes32(1, encodeBytes32String("1")); + await avlTree.insertBytes32(5, encodeBytes32String("5")); + await avlTree.insertBytes32(4, encodeBytes32String("6")); + + await avlTree.removeBytes32(2); + + expect(await avlTree.rootBytes32()).to.equal(4); + + expect(await avlTree.sizeBytes32()).to.equal(3); + await expect(avlTree.getBytes32(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([false, 0]); + + let traversal = await avlTree.traverseBytes32(); + expect(traversal[0]).to.deep.equal([1, 4, 5]); + expect(traversal[1]).to.deep.equal(uintToBytes32Array([1, 6, 5])); + + await avlTree.insertBytes32(3, encodeBytes32String("3")); + await avlTree.insertBytes32(6, encodeBytes32String("4")); + await avlTree.insertBytes32(2, encodeBytes32String("2")); + + await avlTree.removeBytes32(1); + await avlTree.removeBytes32(3); + + expect(await avlTree.rootBytes32()).to.equal(4); + expect(await avlTree.sizeBytes32()).to.equal(4); + + expect(await avlTree.getBytes32(2)).to.be.equal(encodeBytes32String("2")); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([true, encodeBytes32String("2")]); + + await expect(avlTree.getBytes32(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([false, encodeBytes32String("")]); + + await expect(avlTree.getBytes32(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(3)).to.deep.equal([false, encodeBytes32String("")]); + + traversal = await avlTree.traverseBytes32(); + expect(traversal[0]).to.deep.equal([2, 4, 5, 6]); + expect(traversal[1]).to.deep.equal(uintToBytes32Array([2, 6, 5, 4])); + + await avlTree.removeBytes32(2); + await avlTree.removeBytes32(5); + await avlTree.removeBytes32(6); + await avlTree.removeBytes32(4); + + expect(await avlTree.rootBytes32()).to.equal(0); + expect(await avlTree.sizeBytes32()).to.equal(encodeBytes32String("")); + }); + + it("should handle getting value in the bytes32 tree correctly", async () => { + await expect(avlTree.getBytes32(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([false, encodeBytes32String("")]); + + await avlTree.insertBytes32(1, encodeBytes32String("")); + await avlTree.insertBytes32(2, encodeBytes32String("2")); + + expect(await avlTree.getBytes32(1)).to.be.equal(encodeBytes32String("")); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([true, encodeBytes32String("")]); + + expect(await avlTree.getBytes32(2)).to.be.equal(encodeBytes32String("2")); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([true, encodeBytes32String("2")]); + + await expect(avlTree.getBytes32(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(3)).to.deep.equal([false, encodeBytes32String("")]); + + await expect(avlTree.getBytes32(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(0)).to.deep.equal([false, encodeBytes32String("")]); + }); + + it("should check if custom comparator is set for the bytes32 tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetBytes32()).to.be.false; + + await avlTree.setBytes32DescComparator(); + + expect(await avlTree.isCustomComparatorSetBytes32()).to.be.true; + }); + }); + + describe("Address Tree", () => { + it("should insert nodes to the address tree correctly", async () => { + await avlTree.insertAddress(6, USER1); + await avlTree.insertAddress(4, USER2); + await avlTree.insertAddress(5, USER1); + await avlTree.insertAddress(3, USER4); + await avlTree.insertAddress(2, USER3); + + expect(await avlTree.rootAddress()).to.equal(5); + expect(await avlTree.getAddressValue(5)).to.be.equal(USER1); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([true, USER1.address]); + + expect(await avlTree.sizeAddress()).to.equal(5); + + let traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([USER3.address, USER4.address, USER2.address, USER1.address, USER1.address]); + + await avlTree.insertAddress(1, USER3); + + expect(await avlTree.rootAddress()).to.equal(3); + expect(await avlTree.getAddressValue(3)).to.be.equal(USER4); + expect(await avlTree.tryGetAddress(3)).to.deep.equal([true, USER4.address]); + + expect(await avlTree.sizeAddress()).to.equal(6); + + traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([1, 2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([ + USER3.address, + USER3.address, + USER4.address, + USER2.address, + USER1.address, + USER1.address, + ]); + + const backwardsTraversal = await avlTree.backwardsTraversalAddress(); + expect(backwardsTraversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(backwardsTraversal[1]).to.deep.equal([ + USER1.address, + USER1.address, + USER2.address, + USER4.address, + USER3.address, + USER3.address, + ]); + }); + + it("should remove nodes in the address tree correctly", async () => { + await avlTree.setAddressDescComparator(); + + await avlTree.insertAddress(2, USER2); + await avlTree.insertAddress(1, USER2); + await avlTree.insertAddress(5, USER3); + await avlTree.insertAddress(6, USER1); + + await avlTree.removeAddress(2); + + expect(await avlTree.rootAddress()).to.equal(5); + expect(await avlTree.sizeAddress()).to.equal(3); + + await expect(avlTree.getAddressValue(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(2)).to.deep.equal([false, ZERO_ADDR]); + + let traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([6, 5, 1]); + expect(traversal[1]).to.deep.equal([USER1.address, USER3.address, USER2.address]); + + await avlTree.insertAddress(3, USER2); + await avlTree.insertAddress(4, USER4); + await avlTree.insertAddress(2, USER3); + + await avlTree.removeAddress(2); + await avlTree.insertAddress(2, USER2); + + await avlTree.removeAddress(3); + await avlTree.removeAddress(5); + + expect(await avlTree.rootAddress()).to.equal(2); + expect(await avlTree.sizeAddress()).to.equal(4); + + expect(await avlTree.getAddressValue(1)).to.be.equal(USER2); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([true, USER2.address]); + + await expect(avlTree.getAddressValue(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(3)).to.deep.equal([false, 0]); + + await expect(avlTree.getAddressValue(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([false, ZERO_ADDR]); + + traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([6, 4, 2, 1]); + expect(traversal[1]).to.deep.equal([USER1.address, USER4.address, USER2.address, USER2.address]); + + await avlTree.removeAddress(2); + await avlTree.removeAddress(6); + await avlTree.removeAddress(4); + await avlTree.removeAddress(1); + + expect(await avlTree.rootAddress()).to.equal(0); + expect(await avlTree.sizeAddress()).to.equal(0); + + traversal = await avlTree.traverseAddress(); + expect(traversal[0].length).to.be.equal(0); + expect(traversal[1].length).to.be.equal(0); + }); + + it("should handle getting value in the address tree correctly", async () => { + await expect(avlTree.getAddressValue(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([false, ZERO_ADDR]); + + await avlTree.insertAddress(1, USER2); + await avlTree.insertAddress(2, ZERO_ADDR); + + expect(await avlTree.getAddressValue(1)).to.be.equal(USER2); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([true, USER2.address]); + + expect(await avlTree.getAddressValue(2)).to.be.equal(ZERO_ADDR); + expect(await avlTree.tryGetAddress(2)).to.deep.equal([true, ZERO_ADDR]); + + await expect(avlTree.getAddressValue(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([false, ZERO_ADDR]); + + await expect(avlTree.getAddressValue(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(0)).to.deep.equal([false, ZERO_ADDR]); + }); + + it("should check if custom comparator is set for the address tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetAddress()).to.be.false; + + await avlTree.setAddressDescComparator(); + + expect(await avlTree.isCustomComparatorSetAddress()).to.be.true; + }); + }); +});