diff --git a/package.json b/package.json index c36cd7a..108d206 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ }, "dependencies": { "@rhinestone/modulekit": "^0.4.8", - "stringutils": "github:Arachnid/solidity-stringutils" + "stringutils": "github:Arachnid/solidity-stringutils", + "enumerablemap": "https://github.com/erc7579/enumerablemap" }, "files": [ "src", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ceefa58..46c0381 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,9 @@ importers: '@rhinestone/modulekit': specifier: ^0.4.8 version: 0.4.8(ethers@5.7.2)(hardhat@2.22.6(typescript@4.9.5))(lodash@4.17.21)(typechain@5.2.0(typescript@4.9.5))(typescript@4.9.5) - parallelshell: - specifier: ^3.0.2 - version: 3.0.2 + enumerablemap: + specifier: https://github.com/erc7579/enumerablemap + version: https://codeload.github.com/erc7579/enumerablemap/tar.gz/19601b893043a92facd413fa249ea23734523be2 stringutils: specifier: github:Arachnid/solidity-stringutils version: solidity-stringutils@https://codeload.github.com/Arachnid/solidity-stringutils/tar.gz/4b2fcc43fa0426e19ce88b1f1ec16f5903a2e461 @@ -796,6 +796,10 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + enumerablemap@https://codeload.github.com/erc7579/enumerablemap/tar.gz/19601b893043a92facd413fa249ea23734523be2: + resolution: {tarball: https://codeload.github.com/erc7579/enumerablemap/tar.gz/19601b893043a92facd413fa249ea23734523be2} + version: 0.0.0 + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -1476,10 +1480,6 @@ packages: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} - parallelshell@3.0.2: - resolution: {integrity: sha512-aW73W8tmYiFZtQi41pweV3WWT6o/EvSxAVQHbumOhN53H47OuWQwrRc11xQ2i44GFvR5AjtzhD92r8Kv9X+7Iw==} - hasBin: true - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3106,6 +3106,8 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + enumerablemap@https://codeload.github.com/erc7579/enumerablemap/tar.gz/19601b893043a92facd413fa249ea23734523be2: {} + env-paths@2.2.1: {} erc7579-implementation@https://codeload.github.com/erc7579/erc7579-implementation/tar.gz/b3f8bcb2df3aae3217213ffa8b7a87c1eb42ec56(ethers@5.7.2)(hardhat@2.22.6(typescript@4.9.5))(lodash@4.17.21)(typechain@5.2.0(typescript@4.9.5)): @@ -3939,8 +3941,6 @@ snapshots: registry-url: 6.0.1 semver: 7.6.3 - parallelshell@3.0.2: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 diff --git a/remappings.txt b/remappings.txt index a85d0b0..3bec8eb 100644 --- a/remappings.txt +++ b/remappings.txt @@ -19,4 +19,5 @@ solarray/=node_modules/solarray/src/ @prb/math/=node_modules/@prb/math/src/ kernel/=node_modules/@zerodev/kernel/src/ ExcessivelySafeCall/=node_modules/excessively-safe-call/src/ -stringutils/=node_modules/stringutils/src/ \ No newline at end of file +stringutils/=node_modules/stringutils/src/ +enumerableset4337/=node_modules/enumerablemap/src/ \ No newline at end of file diff --git a/src/SampleK1ValidatorWithERC7739.sol b/src/SampleK1ValidatorWithERC7739.sol index 9373b82..522d272 100644 --- a/src/SampleK1ValidatorWithERC7739.sol +++ b/src/SampleK1ValidatorWithERC7739.sol @@ -5,7 +5,7 @@ import { ERC7739Validator } from "./ERC7739Validator.sol"; import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; import { ERC7579ValidatorBase } from "modulekit/Modules.sol"; import { PackedUserOperation } from "modulekit/external/ERC4337.sol"; -import { EnumerableSet } from "./utils/EnumerableSet4337.sol"; +import { EnumerableSet } from "enumerableset4337/EnumerableSet4337.sol"; import { ModuleInstallLib } from "./utils/ModuleInstallLib.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; diff --git a/src/utils/AssociatedArrayLib.sol b/src/utils/AssociatedArrayLib.sol deleted file mode 100644 index 20d928d..0000000 --- a/src/utils/AssociatedArrayLib.sol +++ /dev/null @@ -1,276 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -library AssociatedArrayLib { - using AssociatedArrayLib for *; - - error AssociatedArray_OutOfBounds(uint256 index); - - struct Array { - uint256 _spacer; - } - - function _slot(Array storage s, address account) private pure returns (bytes32 __slot) { - assembly { - mstore(0x00, account) - mstore(0x20, s.slot) - __slot := keccak256(0x00, 0x40) - } - } - - function _length(Array storage s, address account) private view returns (uint256 __length) { - bytes32 slot = _slot(s, account); - assembly { - __length := sload(slot) - } - } - - function _get(Array storage s, address account, uint256 index) private view returns (bytes32 value) { - return _get(_slot(s, account), index); - } - - function _get(bytes32 slot, uint256 index) private view returns (bytes32 value) { - assembly { - //if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index); - if iszero(lt(index, sload(slot))) { - mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)` - mstore(0x20, index) - revert(0x1c, 0x24) - } - value := sload(add(slot, mul(0x20, add(index, 1)))) - } - } - - function _getAll(Array storage s, address account) private view returns (bytes32[] memory values) { - bytes32 slot = _slot(s, account); - uint256 __length; - assembly { - __length := sload(slot) - } - values = new bytes32[](__length); - for (uint256 i; i < __length; i++) { - values[i] = _get(slot, i); - } - } - - // inefficient. complexity = O(n) - // use with caution - // in case of large arrays, consider using EnumerableSet4337 instead - function _contains(Array storage s, address account, bytes32 value) private view returns (bool) { - bytes32 slot = _slot(s, account); - uint256 __length; - assembly { - __length := sload(slot) - } - for (uint256 i; i < __length; i++) { - if (_get(slot, i) == value) { - return true; - } - } - return false; - } - - function _set(Array storage s, address account, uint256 index, bytes32 value) private { - _set(_slot(s, account), index, value); - } - - function _set(bytes32 slot, uint256 index, bytes32 value) private { - assembly { - //if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index); - if iszero(lt(index, sload(slot))) { - mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)` - mstore(0x20, index) - revert(0x1c, 0x24) - } - sstore(add(slot, mul(0x20, add(index, 1))), value) - } - } - - function _push(Array storage s, address account, bytes32 value) private { - bytes32 slot = _slot(s, account); - assembly { - // load length (stored @ slot), add 1 to it => index. - // mul index by 0x20 and add it to orig slot to get the next free slot - let index := add(sload(slot), 1) - sstore(add(slot, mul(0x20, index)), value) - sstore(slot, index) //increment length by 1 - } - } - - function _pop(Array storage s, address account) private { - bytes32 slot = _slot(s, account); - uint256 __length; - assembly { - __length := sload(slot) - } - if (__length == 0) return; - _set(slot, __length - 1, 0); - assembly { - sstore(slot, sub(__length, 1)) - } - } - - function _remove(Array storage s, address account, uint256 index) private { - bytes32 slot = _slot(s, account); - uint256 __length; - assembly { - __length := sload(slot) - if iszero(lt(index, __length)) { - mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)` - mstore(0x20, index) - revert(0x1c, 0x24) - } - } - _set(slot, index, _get(s, account, __length - 1)); - - assembly { - // clear the last slot - // this is the 'unchecked' version of _set(slot, __length - 1, 0) - // as we use length-1 as index, so the check is excessive. - // also removes extra -1 and +1 operations - sstore(add(slot, mul(0x20, __length)), 0) - // store new length - sstore(slot, sub(__length, 1)) - } - } - - struct Bytes32Array { - Array _inner; - } - - function length(Bytes32Array storage s, address account) internal view returns (uint256) { - return _length(s._inner, account); - } - - function get(Bytes32Array storage s, address account, uint256 index) internal view returns (bytes32) { - return _get(s._inner, account, index); - } - - function getAll(Bytes32Array storage s, address account) internal view returns (bytes32[] memory) { - return _getAll(s._inner, account); - } - - function contains(Bytes32Array storage s, address account, bytes32 value) internal view returns (bool) { - return _contains(s._inner, account, value); - } - - function add(Bytes32Array storage s, address account, bytes32 value) internal { - if (!_contains(s._inner, account, value)) { - _push(s._inner, account, value); - } - } - - function set(Bytes32Array storage s, address account, uint256 index, bytes32 value) internal { - _set(s._inner, account, index, value); - } - - function push(Bytes32Array storage s, address account, bytes32 value) internal { - _push(s._inner, account, value); - } - - function pop(Bytes32Array storage s, address account) internal { - _pop(s._inner, account); - } - - function remove(Bytes32Array storage s, address account, uint256 index) internal { - _remove(s._inner, account, index); - } - - struct AddressArray { - Array _inner; - } - - function length(AddressArray storage s, address account) internal view returns (uint256) { - return _length(s._inner, account); - } - - function get(AddressArray storage s, address account, uint256 index) internal view returns (address) { - return address(uint160(uint256(_get(s._inner, account, index)))); - } - - function getAll(AddressArray storage s, address account) internal view returns (address[] memory) { - bytes32[] memory bytes32Array = _getAll(s._inner, account); - address[] memory addressArray; - - /// @solidity memory-safe-assembly - assembly { - addressArray := bytes32Array - } - return addressArray; - } - - function contains(AddressArray storage s, address account, address value) internal view returns (bool) { - return _contains(s._inner, account, bytes32(uint256(uint160(value)))); - } - - function add(AddressArray storage s, address account, address value) internal { - if (!_contains(s._inner, account, bytes32(uint256(uint160(value))))) { - _push(s._inner, account, bytes32(uint256(uint160(value)))); - } - } - - function set(AddressArray storage s, address account, uint256 index, address value) internal { - _set(s._inner, account, index, bytes32(uint256(uint160(value)))); - } - - function push(AddressArray storage s, address account, address value) internal { - _push(s._inner, account, bytes32(uint256(uint160(value)))); - } - - function pop(AddressArray storage s, address account) internal { - _pop(s._inner, account); - } - - function remove(AddressArray storage s, address account, uint256 index) internal { - _remove(s._inner, account, index); - } - - struct UintArray { - Array _inner; - } - - function length(UintArray storage s, address account) internal view returns (uint256) { - return _length(s._inner, account); - } - - function get(UintArray storage s, address account, uint256 index) internal view returns (uint256) { - return uint256(_get(s._inner, account, index)); - } - - function getAll(UintArray storage s, address account) internal view returns (uint256[] memory) { - bytes32[] memory bytes32Array = _getAll(s._inner, account); - uint256[] memory uintArray; - - /// @solidity memory-safe-assembly - assembly { - uintArray := bytes32Array - } - return uintArray; - } - - function contains(UintArray storage s, address account, uint256 value) internal view returns (bool) { - return _contains(s._inner, account, bytes32(value)); - } - - function add(UintArray storage s, address account, uint256 value) internal { - if (!_contains(s._inner, account, bytes32(value))) { - _push(s._inner, account, bytes32(value)); - } - } - - function set(UintArray storage s, address account, uint256 index, uint256 value) internal { - _set(s._inner, account, index, bytes32(value)); - } - - function push(UintArray storage s, address account, uint256 value) internal { - _push(s._inner, account, bytes32(value)); - } - - function pop(UintArray storage s, address account) internal { - _pop(s._inner, account); - } - - function remove(UintArray storage s, address account, uint256 index) internal { - _remove(s._inner, account, index); - } -} diff --git a/src/utils/EnumerableSet4337.sol b/src/utils/EnumerableSet4337.sol deleted file mode 100644 index ad62b37..0000000 --- a/src/utils/EnumerableSet4337.sol +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.20; - -import "./AssociatedArrayLib.sol"; - -/** - * Fork of OZ's EnumerableSet that makes all storage access ERC-4337 compliant via associated storage - * @author zeroknots.eth (rhinestone) - */ -library EnumerableSet { - using AssociatedArrayLib for AssociatedArrayLib.Bytes32Array; - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - AssociatedArrayLib.Bytes32Array _values; - // Position is the index of the value in the `values` array plus 1. - // Position 0 is used to mean a value is not in the set. - mapping(bytes32 value => mapping(address account => uint256)) _positions; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, address account, bytes32 value) private returns (bool) { - if (!_contains(set, account, value)) { - set._values.push(account, value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._positions[value][account] = set._values.length(account); - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, address account, bytes32 value) private returns (bool) { - // We cache the value's position to prevent multiple reads from the same storage slot - uint256 position = set._positions[value][account]; - - if (position != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 valueIndex = position - 1; - uint256 lastIndex = set._values.length(account) - 1; - - if (valueIndex != lastIndex) { - bytes32 lastValue = set._values.get(account, lastIndex); - - // Move the lastValue to the index where the value to delete is - set._values.set(account, valueIndex, lastValue); - // Update the tracked position of the lastValue (that was just moved) - set._positions[lastValue][account] = position; - } - - // Delete the slot where the moved value was stored - set._values.pop(account); - - // Delete the tracked position for the deleted slot - delete set._positions[value][account]; - - return true; - } else { - return false; - } - } - - function _removeAll(Set storage set, address account) internal { - // get length of the array - uint256 len = _length(set, account); - for (uint256 i = 1; i <= len; i++) { - // get last value - bytes32 value = _at(set, account, len - i); - _remove(set, account, value); - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, address account, bytes32 value) private view returns (bool) { - return set._positions[value][account] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set, address account) private view returns (uint256) { - return set._values.length(account); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, address account, uint256 index) private view returns (bytes32) { - return set._values.get(account, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set, address account) private view returns (bytes32[] memory) { - return set._values.getAll(account); - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) { - return _add(set._inner, account, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) { - return _remove(set._inner, account, value); - } - - function removeAll(Bytes32Set storage set, address account) internal { - return _removeAll(set._inner, account); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, address account, bytes32 value) internal view returns (bool) { - return _contains(set._inner, account, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set, address account) internal view returns (uint256) { - return _length(set._inner, account); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, address account, uint256 index) internal view returns (bytes32) { - return _at(set._inner, account, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set, address account) internal view returns (bytes32[] memory) { - bytes32[] memory store = _values(set._inner, account); - bytes32[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address account, address value) internal returns (bool) { - return _add(set._inner, account, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address account, address value) internal returns (bool) { - return _remove(set._inner, account, bytes32(uint256(uint160(value)))); - } - - function removeAll(AddressSet storage set, address account) internal { - return _removeAll(set._inner, account); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address account, address value) internal view returns (bool) { - return _contains(set._inner, account, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set, address account) internal view returns (uint256) { - return _length(set._inner, account); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, address account, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, account, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set, address account) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner, account); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, address account, uint256 value) internal returns (bool) { - return _add(set._inner, account, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, address account, uint256 value) internal returns (bool) { - return _remove(set._inner, account, bytes32(value)); - } - - function removeAll(UintSet storage set, address account) internal { - return _removeAll(set._inner, account); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, address account, uint256 value) internal view returns (bool) { - return _contains(set._inner, account, bytes32(value)); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(UintSet storage set, address account) internal view returns (uint256) { - return _length(set._inner, account); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, address account, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, account, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set, address account) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner, account); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -}