From cf5355a104926c44e8c5a1df03eb2b9db1417ebc Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:41:08 -0700 Subject: [PATCH] refactor: [v0.8-develop] 6900 iteration setup (#43) --- .solhint-test.json | 4 +- package.json | 4 +- src/libraries/AssociatedLinkedListSetLib.sol | 1020 +++++++++-------- src/libraries/PluginStorageLib.sol | 103 +- src/plugins/owner/ISingleOwnerPlugin.sol | 2 - src/plugins/owner/SingleOwnerPlugin.sol | 61 +- .../plugins/ModularSessionKeyPlugin.sol | 735 ++++++------ src/samples/plugins/TokenSessionKeyPlugin.sol | 228 ++-- .../plugins/interfaces/ISessionKeyPlugin.sol | 187 +-- .../interfaces/ITokenSessionKeyPlugin.sol | 28 +- .../AssociatedLinkedListSetLib.t.sol | 334 +++--- test/libraries/PluginStorageLib.t.sol | 147 +-- .../plugins/BadTransferOwnershipPlugin.sol | 3 - .../ExecFromPluginPermissionsMocks.sol | 4 - test/mocks/plugins/ManifestValidityMocks.sol | 12 +- test/mocks/plugins/ReturnDataPluginMocks.sol | 13 +- .../plugins/ModularSessionKeyPlugin.t.sol | 740 ++++++------ 17 files changed, 1824 insertions(+), 1801 deletions(-) diff --git a/.solhint-test.json b/.solhint-test.json index c3efb843..cbd7bf02 100644 --- a/.solhint-test.json +++ b/.solhint-test.json @@ -11,7 +11,9 @@ "modifier-name-mixedcase": ["error"], "private-vars-leading-underscore": ["error"], "no-inline-assembly": "off", - "avoid-low-level-calls": "off" + "avoid-low-level-calls": "off", + "one-contract-per-file": "off", + "no-empty-blocks": "off" } } \ No newline at end of file diff --git a/package.json b/package.json index e6510e73..a9cc7c23 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "scripts": { "lint": "pnpm lint:src && pnpm lint:test", - "lint:src": "solhint -c .solhint-src.json ./src/**/*.sol", - "lint:test": "solhint -c .solhint-test.json ./test/**/*.sol" + "lint:src": "solhint -c .solhint-src.json './src/**/*.sol'", + "lint:test": "solhint -c .solhint-test.json './test/**/*.sol'" } } diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index a6fa8ebf..97974b2c 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -1,507 +1,519 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -type SetValue is bytes30; - -/// @dev The sentinel value is used to indicate the head and tail of the list. -bytes32 constant SENTINEL_VALUE = bytes32(uint256(1)); - -/// @dev Removing the last element will result in this flag not being set correctly, but all operations will -/// function normally, albeit with one extra sload for getAll. -bytes32 constant HAS_NEXT_FLAG = bytes32(uint256(2)); - -/// @dev Type representing the set, which is just a storage slot placeholder like the solidity mapping type. -struct AssociatedLinkedListSet { - bytes32 placeholder; -} - -/// @title Associated Linked List Set Library -/// @notice Provides a set data structure that is enumerable and held in address-associated storage (per the -/// ERC-4337 spec) -library AssociatedLinkedListSetLib { - // Mapping Entry Byte Layout - // | value | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA____ | - // | meta | 0x____________________________________________________________BBBB | - - // Bit-layout of the meta bytes (2 bytes) - // | user flags | 11111111 11111100 | - // | has next | 00000000 00000010 | - // | sentinel | 00000000 00000001 | - - // Mapping keys exclude the upper 15 bits of the meta bytes, which allows keys to be either a value or the - // sentinel. - - bytes4 internal constant _ASSOCIATED_STORAGE_PREFIX = 0x9cc6c923; // bytes4(keccak256("AssociatedLinkedListSet")) - - // A custom type representing the index of a storage slot - type StoragePointer is bytes32; - - // A custom type representing a pointer to a location in memory beyond the current free memory pointer. - // Holds a fixed-size buffer similar to "bytes memory", but without a length field. - // Care must be taken when using these, as they may be overwritten if ANY memory is allocated after allocating - // a TempBytesMemory. - type TempBytesMemory is bytes32; - - // INTERNAL METHODS - - /// @notice Adds a value to a set. - /// @param set The set to add the value to. - /// @param associated The address the set is associated with. - /// @param value The value to add. - /// @return True if the value was added, false if the value cannot be added (already exists or is zero). - function tryAdd(AssociatedLinkedListSet storage set, address associated, SetValue value) - internal - returns (bool) - { - bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); - if (unwrappedKey == bytes32(0)) { - // Cannot add the zero value - return false; - } - - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); - if (_load(valueSlot) != bytes32(0)) { - // Entry already exists - return false; - } - - // Load the head of the set - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - bytes32 prev = _load(sentinelSlot); - if (prev == bytes32(0) || isSentinel(prev)) { - // set is empty, need to do: - // map[SENTINEL_VALUE] = unwrappedKey; - // map[unwrappedKey] = SENTINEL_VALUE; - _store(sentinelSlot, unwrappedKey); - _store(valueSlot, SENTINEL_VALUE); - } else { - // set is not empty, need to do: - // map[SENTINEL_VALUE] = unwrappedKey | HAS_NEXT_FLAG; - // map[unwrappedKey] = prev; - _store(sentinelSlot, unwrappedKey | HAS_NEXT_FLAG); - _store(valueSlot, prev); - } - - return true; - } - - /// @notice Removes a value from a set. - /// @dev This is an O(n) operation, where n is the number of elements in the set. - /// @param set The set to remove the value from - /// @param associated The address the set is associated with - /// @param value The value to remove - /// @return True if the value was removed, false if the value does not exist - function tryRemove(AssociatedLinkedListSet storage set, address associated, SetValue value) - internal - returns (bool) - { - bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); - bytes32 nextValue = _load(valueSlot); - if (unwrappedKey == bytes32(0) || nextValue == bytes32(0)) { - // Entry does not exist - return false; - } - - bytes32 prevKey = SENTINEL_VALUE; - bytes32 currentVal; - do { - // Load the current entry - StoragePointer prevSlot = _mapLookup(keyBuffer, prevKey); - currentVal = _load(prevSlot); - bytes32 currentKey = clearFlags(currentVal); - if (currentKey == unwrappedKey) { - // Found the entry - // Set the previous value's next value to the next value, - // and the flags to the current value's flags. - // and the next value's `hasNext` flag to determine whether or not the next value is (or points to) - // the sentinel value. - - // Need to do: - // map[prevKey] = clearFlags(nextValue) | getUserFlags(currentVal) | (nextValue & HAS_NEXT_FLAG); - // map[currentKey] = bytes32(0); - - _store(prevSlot, clearFlags(nextValue) | getUserFlags(currentVal) | (nextValue & HAS_NEXT_FLAG)); - _store(valueSlot, bytes32(0)); - - return true; - } - prevKey = currentKey; - } while (!isSentinel(currentVal) && currentVal != bytes32(0)); - return false; - } - - /// @notice Removes a value from a set, given the previous value in the set. - /// @dev This is an O(1) operation but requires additional knowledge. - /// @param set The set to remove the value from - /// @param associated The address the set is associated with - /// @param value The value to remove - /// @param prev The previous value in the set - /// @return True if the value was removed, false if the value does not exist - function tryRemoveKnown(AssociatedLinkedListSet storage set, address associated, SetValue value, bytes32 prev) - internal - returns (bool) - { - bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - prev = clearFlags(prev); - - if (prev == bytes32(0) || unwrappedKey == bytes32(0)) { - return false; - } - - // assert that the previous key's next value is the value to be removed - StoragePointer prevSlot = _mapLookup(keyBuffer, prev); - bytes32 currentValue = _load(prevSlot); - if (clearFlags(currentValue) != unwrappedKey) { - return false; - } - - StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); - bytes32 next = _load(valueSlot); - if (next == bytes32(0)) { - // The set didn't actually contain the value - return false; - } - - // Need to do: - // map[prev] = clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG); - // map[unwrappedKey] = bytes32(0); - _store(prevSlot, clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG)); - _store(valueSlot, bytes32(0)); - - return true; - } - - /// @notice Removes all values from a set. - /// @dev This is an O(n) operation, where n is the number of elements in the set. - /// @param set The set to remove the values from - /// @param associated The address the set is associated with - function clear(AssociatedLinkedListSet storage set, address associated) internal { - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - bytes32 cursor = SENTINEL_VALUE; - - do { - bytes32 cleared = clearFlags(cursor); - StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); - bytes32 next = _load(cursorSlot); - _store(cursorSlot, bytes32(0)); - cursor = next; - } while (!isSentinel(cursor) && cursor != bytes32(0)); - - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - _store(sentinelSlot, bytes32(0)); - } - - /// @notice Set the flags on a value in the set. - /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the - /// sentinel and has next bit. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to set the flags on. - /// @param flags The flags to set. - /// @return True if the set contains the value and the operation succeeds, false otherwise. - function trySetFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) - internal - returns (bool) - { - bytes32 unwrappedKey = SetValue.unwrap(value); - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - // Ignore the lower 2 bits. - flags &= 0xFFFC; - - // If the set doesn't actually contain the value, return false; - StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); - bytes32 next = _load(valueSlot); - if (next == bytes32(0)) { - return false; - } - - // Set the flags - _store(valueSlot, clearUserFlags(next) | bytes32(uint256(flags))); - - return true; - } - - /// @notice Set the given flags on a value in the set, preserving the values of other flags. - /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the - /// sentinel and has next bit. - /// Short-circuits if the flags are already enabled, returning true. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to enable the flags on. - /// @param flags The flags to enable. - /// @return True if the operation succeeds or short-circuits due to the flags already being enabled. False - /// otherwise. - function tryEnableFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) - internal - returns (bool) - { - flags &= 0xFFFC; // Allow short-circuit if lower bits are accidentally set - uint16 currFlags = getFlags(set, associated, value); - if (currFlags & flags == flags) return true; // flags are already enabled - return trySetFlags(set, associated, value, currFlags | flags); - } - - /// @notice Clear the given flags on a value in the set, preserving the values of other flags. - /// @notice If the value is not in the set, this function will still return true. - /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the - /// sentinel and has next bit. - /// Short-circuits if the flags are already disabled, or if set does not contain the value. Short-circuits - /// return true. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to disable the flags on. - /// @param flags The flags to disable. - /// @return True if the operation succeeds, or short-circuits due to the flags already being disabled or if the - /// set does not contain the value. False otherwise. - function tryDisableFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) - internal - returns (bool) - { - flags &= 0xFFFC; // Allow short-circuit if lower bits are accidentally set - uint16 currFlags = getFlags(set, associated, value); - if (currFlags & flags == 0) return true; // flags are already disabled - return trySetFlags(set, associated, value, currFlags & ~flags); - } - - /// @notice Checks if a set contains a value - /// @dev This method does not clear the upper bits of `value`, that is expected to be done as part of casting - /// to the correct type. If this function is provided the sentinel value by using the upper bits, this function - /// may returns `true`. - /// @param set The set to check - /// @param associated The address the set is associated with - /// @param value The value to check for - /// @return True if the set contains the value, false otherwise - function contains(AssociatedLinkedListSet storage set, address associated, SetValue value) - internal - view - returns (bool) - { - bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - StoragePointer slot = _mapLookup(keyBuffer, unwrappedKey); - return _load(slot) != bytes32(0); - } - - /// @notice Checks if a set is empty - /// @param set The set to check - /// @param associated The address the set is associated with - /// @return True if the set is empty, false otherwise - function isEmpty(AssociatedLinkedListSet storage set, address associated) internal view returns (bool) { - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - bytes32 val = _load(sentinelSlot); - return val == bytes32(0) || isSentinel(val); // either the sentinel is unset, or points to itself - } - - /// @notice Get the flags on a value in the set. - /// @dev The reserved lower 2 bits will not be returned, as those are reserved for the sentinel and has next - /// bit. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to get the flags from. - /// @return The flags set on the value. - function getFlags(AssociatedLinkedListSet storage set, address associated, SetValue value) - internal - view - returns (uint16) - { - bytes32 unwrappedKey = SetValue.unwrap(value); - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - return uint16(uint256(_load(_mapLookup(keyBuffer, unwrappedKey))) & 0xFFFC); - } - - /// @notice Check if the flags on a value are enabled. - /// @dev The reserved lower 2 bits will be ignored, as those are reserved for the sentinel and has next bit. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to check the flags on. - /// @param flags The flags to check. - /// @return True if all of the flags are enabled, false otherwise. - function flagsEnabled(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) - internal - view - returns (bool) - { - flags &= 0xFFFC; - return getFlags(set, associated, value) & flags == flags; - } - - /// @notice Check if the flags on a value are disabled. - /// @dev The reserved lower 2 bits will be ignored, as those are reserved for the sentinel and has next bit. - /// @param set The set containing the value. - /// @param associated The address the set is associated with. - /// @param value The value to check the flags on. - /// @param flags The flags to check. - /// @return True if all of the flags are disabled, false otherwise. - function flagsDisabled(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) - internal - view - returns (bool) - { - flags &= 0xFFFC; - return ~(getFlags(set, associated, value)) & flags == flags; - } - - /// @notice Gets all elements in a set. - /// @dev This is an O(n) operation, where n is the number of elements in the set. - /// @param set The set to get the elements of. - /// @return res An array of all elements in the set. - function getAll(AssociatedLinkedListSet storage set, address associated) - internal - view - returns (SetValue[] memory res) - { - TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); - - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - bytes32 cursor = _load(sentinelSlot); - - uint256 count; - while (!isSentinel(cursor) && cursor != bytes32(0)) { - unchecked { - ++count; - } - bytes32 cleared = clearFlags(cursor); - - if (hasNext(cursor)) { - StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); - cursor = _load(cursorSlot); - } else { - cursor = bytes32(0); - } - } - - res = new SetValue[](count); - - if (count == 0) { - return res; - } - - // Re-allocate the key buffer because we just overwrote it! - keyBuffer = _allocateTempKeyBuffer(set, associated); - - cursor = SENTINEL_VALUE; - for (uint256 i = 0; i < count;) { - StoragePointer cursorSlot = _mapLookup(keyBuffer, cursor); - bytes32 cursorValue = _load(cursorSlot); - bytes32 cleared = clearFlags(cursorValue); - res[i] = SetValue.wrap(bytes30(cleared)); - cursor = cleared; - - unchecked { - ++i; - } - } - } - - function isSentinel(bytes32 value) internal pure returns (bool ret) { - assembly ("memory-safe") { - ret := and(value, 1) - } - } - - function hasNext(bytes32 value) internal pure returns (bool) { - return value & HAS_NEXT_FLAG != 0; - } - - function clearFlags(bytes32 val) internal pure returns (bytes32) { - return val & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0001; - } - - /// @dev Preserves the lower two bits - function clearUserFlags(bytes32 val) internal pure returns (bytes32) { - return val & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003; - } - - function getUserFlags(bytes32 val) internal pure returns (bytes32) { - return val & bytes32(uint256(0xFFFC)); - } - - // PRIVATE METHODS - - /// @notice Given an allocated key buffer, returns the storage slot for a given key - function _mapLookup(TempBytesMemory keyBuffer, bytes32 value) private pure returns (StoragePointer slot) { - assembly ("memory-safe") { - // Store the value in the last word. - let keyWord2 := value - mstore(add(keyBuffer, 0x60), keyWord2) - slot := keccak256(keyBuffer, 0x80) - } - } - - /// @notice Allocates a key buffer for a given ID and associated address into scratch space memory. - /// @dev The returned buffer must not be used if any additional memory is allocated after calling this - /// function. - /// @param set The set to allocate the key buffer for. - /// @param associated The address the set is associated with. - /// @return key A key buffer that can be used to lookup values in the set - function _allocateTempKeyBuffer(AssociatedLinkedListSet storage set, address associated) - private - pure - returns (TempBytesMemory key) - { - // Key derivation for an entry - // associated addr (left-padded) || prefix || uint224(0) batchIndex || set storage slot || entry - // Word 1: - // | zeros | 0x000000000000000000000000________________________________________ | - // | address | 0x________________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | - // Word 2: - // | prefix | 0xPPPPPPPP________________________________________________________ | - // | batch index (zero) | 0x________00000000000000000000000000000000000000000000000000000000 | - // Word 3: - // | set storage slot | 0xSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS | - // Word 4: - // | entry value | 0xVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV____ | - // | entry meta | 0x____________________________________________________________MMMM | - - // The batch index is for consistency with PluginStorageLib, and the prefix in front of it is - // to prevent any potential crafted collisions where the batch index may be equal to storage slot - // of the ALLS. The prefix is set to the upper bits of the batch index to make it infeasible to - // reach from just incrementing the value. - - // This segment is memory-safe because it only uses the scratch space memory after the value of the free - // memory pointer. - // See https://docs.soliditylang.org/en/v0.8.21/assembly.html#memory-safety - assembly ("memory-safe") { - // Clean upper bits of arguments - associated := and(associated, 0xffffffffffffffffffffffffffffffffffffffff) - - // Use memory past-the-free-memory-pointer without updating it, as this is just scratch space - key := mload(0x40) - // Store the associated address in the first word, left-padded with zeroes - mstore(key, associated) - // Store the prefix and a batch index of 0 - mstore(add(key, 0x20), _ASSOCIATED_STORAGE_PREFIX) - // Store the list's storage slot in the third word - mstore(add(key, 0x40), set.slot) - // Leaves the last word open for the value entry - } - - return key; - } - - /// @dev Loads a value from storage - function _load(StoragePointer ptr) private view returns (bytes32 val) { - assembly ("memory-safe") { - val := sload(ptr) - } - } - - /// @dev Writes a value into storage - function _store(StoragePointer ptr, bytes32 val) private { - assembly ("memory-safe") { - sstore(ptr, val) - } - } -} +// type SetValue is bytes30; + +// /// @dev The sentinel value is used to indicate the head and tail of the list. +// bytes32 constant SENTINEL_VALUE = bytes32(uint256(1)); + +// /// @dev Removing the last element will result in this flag not being set correctly, but all operations will +// /// function normally, albeit with one extra sload for getAll. +// bytes32 constant HAS_NEXT_FLAG = bytes32(uint256(2)); + +// /// @dev Type representing the set, which is just a storage slot placeholder like the solidity mapping type. +// struct AssociatedLinkedListSet { +// bytes32 placeholder; +// } + +// /// @title Associated Linked List Set Library +// /// @notice Provides a set data structure that is enumerable and held in address-associated storage (per the +// /// ERC-4337 spec) +// library AssociatedLinkedListSetLib { +// // Mapping Entry Byte Layout +// // | value | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA____ | +// // | meta | 0x____________________________________________________________BBBB | + +// // Bit-layout of the meta bytes (2 bytes) +// // | user flags | 11111111 11111100 | +// // | has next | 00000000 00000010 | +// // | sentinel | 00000000 00000001 | + +// // Mapping keys exclude the upper 15 bits of the meta bytes, which allows keys to be either a value or the +// // sentinel. + +// bytes4 internal constant _ASSOCIATED_STORAGE_PREFIX = 0x9cc6c923; // +// bytes4(keccak256("AssociatedLinkedListSet")) + +// // A custom type representing the index of a storage slot +// type StoragePointer is bytes32; + +// // A custom type representing a pointer to a location in memory beyond the current free memory pointer. +// // Holds a fixed-size buffer similar to "bytes memory", but without a length field. +// // Care must be taken when using these, as they may be overwritten if ANY memory is allocated after +// allocating +// // a TempBytesMemory. +// type TempBytesMemory is bytes32; + +// // INTERNAL METHODS + +// /// @notice Adds a value to a set. +// /// @param set The set to add the value to. +// /// @param associated The address the set is associated with. +// /// @param value The value to add. +// /// @return True if the value was added, false if the value cannot be added (already exists or is zero). +// function tryAdd(AssociatedLinkedListSet storage set, address associated, SetValue value) +// internal +// returns (bool) +// { +// bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); +// if (unwrappedKey == bytes32(0)) { +// // Cannot add the zero value +// return false; +// } + +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); +// if (_load(valueSlot) != bytes32(0)) { +// // Entry already exists +// return false; +// } + +// // Load the head of the set +// StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); +// bytes32 prev = _load(sentinelSlot); +// if (prev == bytes32(0) || isSentinel(prev)) { +// // set is empty, need to do: +// // map[SENTINEL_VALUE] = unwrappedKey; +// // map[unwrappedKey] = SENTINEL_VALUE; +// _store(sentinelSlot, unwrappedKey); +// _store(valueSlot, SENTINEL_VALUE); +// } else { +// // set is not empty, need to do: +// // map[SENTINEL_VALUE] = unwrappedKey | HAS_NEXT_FLAG; +// // map[unwrappedKey] = prev; +// _store(sentinelSlot, unwrappedKey | HAS_NEXT_FLAG); +// _store(valueSlot, prev); +// } + +// return true; +// } + +// /// @notice Removes a value from a set. +// /// @dev This is an O(n) operation, where n is the number of elements in the set. +// /// @param set The set to remove the value from +// /// @param associated The address the set is associated with +// /// @param value The value to remove +// /// @return True if the value was removed, false if the value does not exist +// function tryRemove(AssociatedLinkedListSet storage set, address associated, SetValue value) +// internal +// returns (bool) +// { +// bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); +// bytes32 nextValue = _load(valueSlot); +// if (unwrappedKey == bytes32(0) || nextValue == bytes32(0)) { +// // Entry does not exist +// return false; +// } + +// bytes32 prevKey = SENTINEL_VALUE; +// bytes32 currentVal; +// do { +// // Load the current entry +// StoragePointer prevSlot = _mapLookup(keyBuffer, prevKey); +// currentVal = _load(prevSlot); +// bytes32 currentKey = clearFlags(currentVal); +// if (currentKey == unwrappedKey) { +// // Found the entry +// // Set the previous value's next value to the next value, +// // and the flags to the current value's flags. +// // and the next value's `hasNext` flag to determine whether or not the next value is (or points +// to) +// // the sentinel value. + +// // Need to do: +// // map[prevKey] = clearFlags(nextValue) | getUserFlags(currentVal) | (nextValue & +// HAS_NEXT_FLAG); +// // map[currentKey] = bytes32(0); + +// _store(prevSlot, clearFlags(nextValue) | getUserFlags(currentVal) | (nextValue & +// HAS_NEXT_FLAG)); +// _store(valueSlot, bytes32(0)); + +// return true; +// } +// prevKey = currentKey; +// } while (!isSentinel(currentVal) && currentVal != bytes32(0)); +// return false; +// } + +// /// @notice Removes a value from a set, given the previous value in the set. +// /// @dev This is an O(1) operation but requires additional knowledge. +// /// @param set The set to remove the value from +// /// @param associated The address the set is associated with +// /// @param value The value to remove +// /// @param prev The previous value in the set +// /// @return True if the value was removed, false if the value does not exist +// function tryRemoveKnown(AssociatedLinkedListSet storage set, address associated, SetValue value, bytes32 +// prev) +// internal +// returns (bool) +// { +// bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// prev = clearFlags(prev); + +// if (prev == bytes32(0) || unwrappedKey == bytes32(0)) { +// return false; +// } + +// // assert that the previous key's next value is the value to be removed +// StoragePointer prevSlot = _mapLookup(keyBuffer, prev); +// bytes32 currentValue = _load(prevSlot); +// if (clearFlags(currentValue) != unwrappedKey) { +// return false; +// } + +// StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); +// bytes32 next = _load(valueSlot); +// if (next == bytes32(0)) { +// // The set didn't actually contain the value +// return false; +// } + +// // Need to do: +// // map[prev] = clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG); +// // map[unwrappedKey] = bytes32(0); +// _store(prevSlot, clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG)); +// _store(valueSlot, bytes32(0)); + +// return true; +// } + +// /// @notice Removes all values from a set. +// /// @dev This is an O(n) operation, where n is the number of elements in the set. +// /// @param set The set to remove the values from +// /// @param associated The address the set is associated with +// function clear(AssociatedLinkedListSet storage set, address associated) internal { +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// bytes32 cursor = SENTINEL_VALUE; + +// do { +// bytes32 cleared = clearFlags(cursor); +// StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); +// bytes32 next = _load(cursorSlot); +// _store(cursorSlot, bytes32(0)); +// cursor = next; +// } while (!isSentinel(cursor) && cursor != bytes32(0)); + +// StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); +// _store(sentinelSlot, bytes32(0)); +// } + +// /// @notice Set the flags on a value in the set. +// /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the +// /// sentinel and has next bit. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to set the flags on. +// /// @param flags The flags to set. +// /// @return True if the set contains the value and the operation succeeds, false otherwise. +// function trySetFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) +// internal +// returns (bool) +// { +// bytes32 unwrappedKey = SetValue.unwrap(value); +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// // Ignore the lower 2 bits. +// flags &= 0xFFFC; + +// // If the set doesn't actually contain the value, return false; +// StoragePointer valueSlot = _mapLookup(keyBuffer, unwrappedKey); +// bytes32 next = _load(valueSlot); +// if (next == bytes32(0)) { +// return false; +// } + +// // Set the flags +// _store(valueSlot, clearUserFlags(next) | bytes32(uint256(flags))); + +// return true; +// } + +// /// @notice Set the given flags on a value in the set, preserving the values of other flags. +// /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the +// /// sentinel and has next bit. +// /// Short-circuits if the flags are already enabled, returning true. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to enable the flags on. +// /// @param flags The flags to enable. +// /// @return True if the operation succeeds or short-circuits due to the flags already being enabled. False +// /// otherwise. +// function tryEnableFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 +// flags) +// internal +// returns (bool) +// { +// flags &= 0xFFFC; // Allow short-circuit if lower bits are accidentally set +// uint16 currFlags = getFlags(set, associated, value); +// if (currFlags & flags == flags) return true; // flags are already enabled +// return trySetFlags(set, associated, value, currFlags | flags); +// } + +// /// @notice Clear the given flags on a value in the set, preserving the values of other flags. +// /// @notice If the value is not in the set, this function will still return true. +// /// @dev The user flags can only be set on the upper 14 bits, because the lower two are reserved for the +// /// sentinel and has next bit. +// /// Short-circuits if the flags are already disabled, or if set does not contain the value. Short-circuits +// /// return true. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to disable the flags on. +// /// @param flags The flags to disable. +// /// @return True if the operation succeeds, or short-circuits due to the flags already being disabled or if +// the +// /// set does not contain the value. False otherwise. +// function tryDisableFlags(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 +// flags) +// internal +// returns (bool) +// { +// flags &= 0xFFFC; // Allow short-circuit if lower bits are accidentally set +// uint16 currFlags = getFlags(set, associated, value); +// if (currFlags & flags == 0) return true; // flags are already disabled +// return trySetFlags(set, associated, value, currFlags & ~flags); +// } + +// /// @notice Checks if a set contains a value +// /// @dev This method does not clear the upper bits of `value`, that is expected to be done as part of +// casting +// /// to the correct type. If this function is provided the sentinel value by using the upper bits, this +// function +// /// may returns `true`. +// /// @param set The set to check +// /// @param associated The address the set is associated with +// /// @param value The value to check for +// /// @return True if the set contains the value, false otherwise +// function contains(AssociatedLinkedListSet storage set, address associated, SetValue value) +// internal +// view +// returns (bool) +// { +// bytes32 unwrappedKey = bytes32(SetValue.unwrap(value)); +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// StoragePointer slot = _mapLookup(keyBuffer, unwrappedKey); +// return _load(slot) != bytes32(0); +// } + +// /// @notice Checks if a set is empty +// /// @param set The set to check +// /// @param associated The address the set is associated with +// /// @return True if the set is empty, false otherwise +// function isEmpty(AssociatedLinkedListSet storage set, address associated) internal view returns (bool) { +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); +// bytes32 val = _load(sentinelSlot); +// return val == bytes32(0) || isSentinel(val); // either the sentinel is unset, or points to itself +// } + +// /// @notice Get the flags on a value in the set. +// /// @dev The reserved lower 2 bits will not be returned, as those are reserved for the sentinel and has next +// /// bit. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to get the flags from. +// /// @return The flags set on the value. +// function getFlags(AssociatedLinkedListSet storage set, address associated, SetValue value) +// internal +// view +// returns (uint16) +// { +// bytes32 unwrappedKey = SetValue.unwrap(value); +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); +// return uint16(uint256(_load(_mapLookup(keyBuffer, unwrappedKey))) & 0xFFFC); +// } + +// /// @notice Check if the flags on a value are enabled. +// /// @dev The reserved lower 2 bits will be ignored, as those are reserved for the sentinel and has next bit. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to check the flags on. +// /// @param flags The flags to check. +// /// @return True if all of the flags are enabled, false otherwise. +// function flagsEnabled(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 flags) +// internal +// view +// returns (bool) +// { +// flags &= 0xFFFC; +// return getFlags(set, associated, value) & flags == flags; +// } + +// /// @notice Check if the flags on a value are disabled. +// /// @dev The reserved lower 2 bits will be ignored, as those are reserved for the sentinel and has next bit. +// /// @param set The set containing the value. +// /// @param associated The address the set is associated with. +// /// @param value The value to check the flags on. +// /// @param flags The flags to check. +// /// @return True if all of the flags are disabled, false otherwise. +// function flagsDisabled(AssociatedLinkedListSet storage set, address associated, SetValue value, uint16 +// flags) +// internal +// view +// returns (bool) +// { +// flags &= 0xFFFC; +// return ~(getFlags(set, associated, value)) & flags == flags; +// } + +// /// @notice Gets all elements in a set. +// /// @dev This is an O(n) operation, where n is the number of elements in the set. +// /// @param set The set to get the elements of. +// /// @return res An array of all elements in the set. +// function getAll(AssociatedLinkedListSet storage set, address associated) +// internal +// view +// returns (SetValue[] memory res) +// { +// TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + +// StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); +// bytes32 cursor = _load(sentinelSlot); + +// uint256 count; +// while (!isSentinel(cursor) && cursor != bytes32(0)) { +// unchecked { +// ++count; +// } +// bytes32 cleared = clearFlags(cursor); + +// if (hasNext(cursor)) { +// StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); +// cursor = _load(cursorSlot); +// } else { +// cursor = bytes32(0); +// } +// } + +// res = new SetValue[](count); + +// if (count == 0) { +// return res; +// } + +// // Re-allocate the key buffer because we just overwrote it! +// keyBuffer = _allocateTempKeyBuffer(set, associated); + +// cursor = SENTINEL_VALUE; +// for (uint256 i = 0; i < count;) { +// StoragePointer cursorSlot = _mapLookup(keyBuffer, cursor); +// bytes32 cursorValue = _load(cursorSlot); +// bytes32 cleared = clearFlags(cursorValue); +// res[i] = SetValue.wrap(bytes30(cleared)); +// cursor = cleared; + +// unchecked { +// ++i; +// } +// } +// } + +// function isSentinel(bytes32 value) internal pure returns (bool ret) { +// assembly ("memory-safe") { +// ret := and(value, 1) +// } +// } + +// function hasNext(bytes32 value) internal pure returns (bool) { +// return value & HAS_NEXT_FLAG != 0; +// } + +// function clearFlags(bytes32 val) internal pure returns (bytes32) { +// return val & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0001; +// } + +// /// @dev Preserves the lower two bits +// function clearUserFlags(bytes32 val) internal pure returns (bytes32) { +// return val & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0003; +// } + +// function getUserFlags(bytes32 val) internal pure returns (bytes32) { +// return val & bytes32(uint256(0xFFFC)); +// } + +// // PRIVATE METHODS + +// /// @notice Given an allocated key buffer, returns the storage slot for a given key +// function _mapLookup(TempBytesMemory keyBuffer, bytes32 value) private pure returns (StoragePointer slot) { +// assembly ("memory-safe") { +// // Store the value in the last word. +// let keyWord2 := value +// mstore(add(keyBuffer, 0x60), keyWord2) +// slot := keccak256(keyBuffer, 0x80) +// } +// } + +// /// @notice Allocates a key buffer for a given ID and associated address into scratch space memory. +// /// @dev The returned buffer must not be used if any additional memory is allocated after calling this +// /// function. +// /// @param set The set to allocate the key buffer for. +// /// @param associated The address the set is associated with. +// /// @return key A key buffer that can be used to lookup values in the set +// function _allocateTempKeyBuffer(AssociatedLinkedListSet storage set, address associated) +// private +// pure +// returns (TempBytesMemory key) +// { +// // Key derivation for an entry +// // associated addr (left-padded) || prefix || uint224(0) batchIndex || set storage slot || entry +// // Word 1: +// // | zeros | 0x000000000000000000000000________________________________________ | +// // | address | 0x________________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | +// // Word 2: +// // | prefix | 0xPPPPPPPP________________________________________________________ | +// // | batch index (zero) | 0x________00000000000000000000000000000000000000000000000000000000 | +// // Word 3: +// // | set storage slot | 0xSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS | +// // Word 4: +// // | entry value | 0xVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV____ | +// // | entry meta | 0x____________________________________________________________MMMM | + +// // The batch index is for consistency with PluginStorageLib, and the prefix in front of it is +// // to prevent any potential crafted collisions where the batch index may be equal to storage slot +// // of the ALLS. The prefix is set to the upper bits of the batch index to make it infeasible to +// // reach from just incrementing the value. + +// // This segment is memory-safe because it only uses the scratch space memory after the value of the free +// // memory pointer. +// // See https://docs.soliditylang.org/en/v0.8.21/assembly.html#memory-safety +// assembly ("memory-safe") { +// // Clean upper bits of arguments +// associated := and(associated, 0xffffffffffffffffffffffffffffffffffffffff) + +// // Use memory past-the-free-memory-pointer without updating it, as this is just scratch space +// key := mload(0x40) +// // Store the associated address in the first word, left-padded with zeroes +// mstore(key, associated) +// // Store the prefix and a batch index of 0 +// mstore(add(key, 0x20), _ASSOCIATED_STORAGE_PREFIX) +// // Store the list's storage slot in the third word +// mstore(add(key, 0x40), set.slot) +// // Leaves the last word open for the value entry +// } + +// return key; +// } + +// /// @dev Loads a value from storage +// function _load(StoragePointer ptr) private view returns (bytes32 val) { +// assembly ("memory-safe") { +// val := sload(ptr) +// } +// } + +// /// @dev Writes a value into storage +// function _store(StoragePointer ptr, bytes32 val) private { +// assembly ("memory-safe") { +// sstore(ptr, val) +// } +// } +// } diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index 503f504a..35aaa7bb 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -1,59 +1,62 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -type StoragePointer is bytes32; +// type StoragePointer is bytes32; -/// @title Plugin Storage Library -/// @notice Library for allocating and accessing ERC-4337 address-associated storage within plugins. -library PluginStorageLib { - /// @notice Allocates a memory buffer for an associated storage key, and sets the associated address and batch - /// index. - /// @param addr The address to associate with the storage key. - /// @param batchIndex The batch index to associate with the storage key. - /// @param keySize The size of the key in words, where each word is 32 bytes. Not inclusive of the address and - /// batch index. - /// @return key The allocated memory buffer. - function allocateAssociatedStorageKey(address addr, uint256 batchIndex, uint8 keySize) - internal - pure - returns (bytes memory key) - { - assembly ("memory-safe") { - // Clear any dirty upper bits of keySize to prevent overflow - keySize := and(keySize, 0xff) +// /// @title Plugin Storage Library +// /// @notice Library for allocating and accessing ERC-4337 address-associated storage within plugins. +// library PluginStorageLib { +// /// @notice Allocates a memory buffer for an associated storage key, and sets the associated address and +// batch +// /// index. +// /// @param addr The address to associate with the storage key. +// /// @param batchIndex The batch index to associate with the storage key. +// /// @param keySize The size of the key in words, where each word is 32 bytes. Not inclusive of the address +// and +// /// batch index. +// /// @return key The allocated memory buffer. +// function allocateAssociatedStorageKey(address addr, uint256 batchIndex, uint8 keySize) +// internal +// pure +// returns (bytes memory key) +// { +// assembly ("memory-safe") { +// // Clear any dirty upper bits of keySize to prevent overflow +// keySize := and(keySize, 0xff) - // compute the total size of the buffer, include the address and batch index - let totalSize := add(64, mul(32, keySize)) +// // compute the total size of the buffer, include the address and batch index +// let totalSize := add(64, mul(32, keySize)) - // Allocate memory for the key - key := mload(0x40) - mstore(0x40, add(add(key, totalSize), 32)) - mstore(key, totalSize) +// // Allocate memory for the key +// key := mload(0x40) +// mstore(0x40, add(add(key, totalSize), 32)) +// mstore(key, totalSize) - // Clear any dirty upper bits of address - addr := and(addr, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) - // Store the address and batch index in the key buffer - mstore(add(key, 32), addr) - mstore(add(key, 64), batchIndex) - } - } +// // Clear any dirty upper bits of address +// addr := and(addr, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) +// // Store the address and batch index in the key buffer +// mstore(add(key, 32), addr) +// mstore(add(key, 64), batchIndex) +// } +// } - function associatedStorageLookup(bytes memory key, bytes32 input) internal pure returns (StoragePointer ptr) { - assembly ("memory-safe") { - mstore(add(key, 96), input) - ptr := keccak256(add(key, 32), mload(key)) - } - } +// function associatedStorageLookup(bytes memory key, bytes32 input) internal pure returns (StoragePointer ptr) +// { +// assembly ("memory-safe") { +// mstore(add(key, 96), input) +// ptr := keccak256(add(key, 32), mload(key)) +// } +// } - function associatedStorageLookup(bytes memory key, bytes32 input1, bytes32 input2) - internal - pure - returns (StoragePointer ptr) - { - assembly ("memory-safe") { - mstore(add(key, 96), input1) - mstore(add(key, 128), input2) - ptr := keccak256(add(key, 32), mload(key)) - } - } -} +// function associatedStorageLookup(bytes memory key, bytes32 input1, bytes32 input2) +// internal +// pure +// returns (StoragePointer ptr) +// { +// assembly ("memory-safe") { +// mstore(add(key, 96), input1) +// mstore(add(key, 128), input2) +// ptr := keccak256(add(key, 32), mload(key)) +// } +// } +// } diff --git a/src/plugins/owner/ISingleOwnerPlugin.sol b/src/plugins/owner/ISingleOwnerPlugin.sol index fc0622c8..4f5257e5 100644 --- a/src/plugins/owner/ISingleOwnerPlugin.sol +++ b/src/plugins/owner/ISingleOwnerPlugin.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; - interface ISingleOwnerPlugin { enum FunctionId { VALIDATION_OWNER_OR_SELF diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol index 29103945..3c3507e2 100644 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ b/src/plugins/owner/SingleOwnerPlugin.sol @@ -60,34 +60,6 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 { _transferOwnership(newOwner); } - /// @inheritdoc IERC1271 - /// @dev The signature is valid if it is signed by the owner's private key - /// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the - /// owner (if the owner is a contract). Note that unlike the signature - /// validation used in `validateUserOp`, this does///*not** wrap the digest in - /// an "Ethereum Signed Message" envelope before checking the signature in - /// the EOA-owner case. - function isValidSignature(bytes32 digest, bytes memory signature) public view override returns (bytes4) { - if (SignatureChecker.isValidSignatureNow(_owners[msg.sender], digest, signature)) { - return _1271_MAGIC_VALUE; - } - return 0xffffffff; - } - - /// @inheritdoc ISingleOwnerPlugin - function owner() external view returns (address) { - return _owners[msg.sender]; - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc ISingleOwnerPlugin - function ownerOf(address account) external view returns (address) { - return _owners[account]; - } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Plugin interface functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ @@ -136,6 +108,38 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 { revert NotImplemented(); } + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Execution view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc IERC1271 + /// @dev The signature is valid if it is signed by the owner's private key + /// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the + /// owner (if the owner is a contract). Note that unlike the signature + /// validation used in `validateUserOp`, this does///*not** wrap the digest in + /// an "Ethereum Signed Message" envelope before checking the signature in + /// the EOA-owner case. + function isValidSignature(bytes32 digest, bytes memory signature) external view override returns (bytes4) { + if (SignatureChecker.isValidSignatureNow(_owners[msg.sender], digest, signature)) { + return _1271_MAGIC_VALUE; + } + return 0xffffffff; + } + + /// @inheritdoc ISingleOwnerPlugin + function owner() external view returns (address) { + return _owners[msg.sender]; + } + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc ISingleOwnerPlugin + function ownerOf(address account) external view returns (address) { + return _owners[account]; + } + /// @inheritdoc BasePlugin function pluginManifest() external pure override returns (PluginManifest memory) { PluginManifest memory manifest; @@ -212,6 +216,7 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 { return metadata; } + // ┏━━━━━━━━━━━━━━━┓ // ┃ EIP-165 ┃ // ┗━━━━━━━━━━━━━━━┛ diff --git a/src/samples/plugins/ModularSessionKeyPlugin.sol b/src/samples/plugins/ModularSessionKeyPlugin.sol index 7af6bb1c..035b62fa 100644 --- a/src/samples/plugins/ModularSessionKeyPlugin.sol +++ b/src/samples/plugins/ModularSessionKeyPlugin.sol @@ -1,368 +1,373 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; -import {UpgradeableModularAccount} from "../../account/UpgradeableModularAccount.sol"; -import { - ManifestFunction, - ManifestAssociatedFunctionType, - ManifestAssociatedFunction, - PluginManifest, - PluginMetadata, - SelectorPermission -} from "../../interfaces/IPlugin.sol"; -import {BasePlugin} from "../../plugins/BasePlugin.sol"; -import {IModularSessionKeyPlugin} from "./interfaces/ISessionKeyPlugin.sol"; -import {ISingleOwnerPlugin} from "../../plugins/owner/ISingleOwnerPlugin.sol"; -import {SingleOwnerPlugin} from "../../plugins/owner/SingleOwnerPlugin.sol"; -import {PluginStorageLib, StoragePointer} from "../../libraries/PluginStorageLib.sol"; - -/// @title Modular Session Key Plugin -/// @author Decipher ERC-6900 Team -/// @notice This plugin allows some designated EOA or smart contract to temporarily -/// own a modular account. Note that this plugin is ONLY for demonstrating the purpose -/// of the functionalities of ERC-6900, and MUST not be used at the production level. -/// This modular session key plugin acts as a 'parent plugin' for all specific session -/// keys. Using dependency, this plugin can be thought as a parent contract that stores -/// session key duration information, and validation functions for session keys. All -/// logics for session keys will be implemented in child plugins. -/// It allows for session key owners to access MSCA both through user operation and -/// runtime, with its own validation functions. -/// Also, it has a dependency on SingleOwnerPlugin, to make sure that only the owner of -/// the MSCA can add or remove session keys. -contract ModularSessionKeyPlugin is BasePlugin, IModularSessionKeyPlugin { - using ECDSA for bytes32; - using PluginStorageLib for address; - using PluginStorageLib for bytes; - using EnumerableSet for EnumerableSet.Bytes32Set; - - string public constant NAME = "Modular Session Key Plugin"; - string public constant VERSION = "1.0.0"; - string public constant AUTHOR = "Decipher ERC-6900 Team"; - - uint256 internal constant _SIG_VALIDATION_FAILED = 1; - - mapping(address account => EnumerableSet.Bytes32Set) private _sessionKeySet; - - struct SessionInfo { - uint48 validAfter; - uint48 validUntil; - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Execution functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IModularSessionKeyPlugin - function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 validAfter, uint48 validUntil) - external - { - _addSessionKey(msg.sender, sessionKey, allowedSelector, validAfter, validUntil); - emit SessionKeyAdded(msg.sender, sessionKey, allowedSelector, validAfter, validUntil); - } - - /// @inheritdoc IModularSessionKeyPlugin - function removeSessionKey(address sessionKey, bytes4 allowedSelector) external { - _removeSessionKey(msg.sender, sessionKey, allowedSelector); - emit SessionKeyRemoved(msg.sender, sessionKey, allowedSelector); - } - - /// @inheritdoc IModularSessionKeyPlugin - function addSessionKeyBatch( - address[] calldata sessionKeys, - bytes4[] calldata allowedSelectors, - uint48[] calldata validAfters, - uint48[] calldata validUntils - ) external { - if ( - sessionKeys.length != allowedSelectors.length || sessionKeys.length != validAfters.length - || sessionKeys.length != validUntils.length - ) { - revert WrongDataLength(); - } - for (uint256 i = 0; i < sessionKeys.length;) { - _addSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i], validAfters[i], validUntils[i]); - - unchecked { - ++i; - } - } - emit SessionKeysAdded(msg.sender, sessionKeys, allowedSelectors, validAfters, validUntils); - } - - function removeSessionKeyBatch(address[] calldata sessionKeys, bytes4[] calldata allowedSelectors) external { - if (sessionKeys.length != allowedSelectors.length) { - revert WrongDataLength(); - } - for (uint256 i = 0; i < sessionKeys.length;) { - _removeSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i]); - - unchecked { - ++i; - } - } - emit SessionKeysRemoved(msg.sender, sessionKeys, allowedSelectors); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IModularSessionKeyPlugin - function getSessionDuration(address account, address sessionKey, bytes4 allowedSelector) - external - view - returns (uint48 validAfter, uint48 validUntil) - { - bytes memory key = account.allocateAssociatedStorageKey(0, 1); - StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, allowedSelector))); - SessionInfo storage sessionInfo = _castPtrToStruct(ptr); - validAfter = sessionInfo.validAfter; - validUntil = sessionInfo.validUntil; - } - - /// @inheritdoc IModularSessionKeyPlugin - function getSessionKeysAndSelectors(address account) - external - view - returns (address[] memory sessionKeys, bytes4[] memory selectors) - { - EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; - uint256 length = sessionKeySet.length(); - sessionKeys = new address[](length); - selectors = new bytes4[](length); - for (uint256 i = 0; i < length;) { - (sessionKeys[i], selectors[i]) = _castToAddressAndBytes4(sessionKeySet.at(i)); - - unchecked { - ++i; - } - } - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function onInstall(bytes calldata data) external override { - if (data.length != 0) { - ( - address[] memory sessionKeys, - bytes4[] memory allowedSelectors, - uint48[] memory validAfters, - uint48[] memory validUntils - ) = abi.decode(data, (address[], bytes4[], uint48[], uint48[])); - if ( - sessionKeys.length != allowedSelectors.length || sessionKeys.length != validAfters.length - || sessionKeys.length != validUntils.length - ) { - revert WrongDataLength(); - } - for (uint256 i = 0; i < sessionKeys.length;) { - _addSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i], validAfters[i], validUntils[i]); - - unchecked { - ++i; - } - } - } - } - - /// @inheritdoc BasePlugin - function onUninstall(bytes calldata) external override { - EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[msg.sender]; - uint256 length = sessionKeySet.length(); - for (uint256 i = 0; i < length;) { - (address sessionKey, bytes4 allowedSelecor) = _castToAddressAndBytes4(sessionKeySet.at(i)); - _removeSessionKey(msg.sender, sessionKey, allowedSelecor); - - unchecked { - ++i; - } - } - } - - /// @inheritdoc BasePlugin - function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) - external - view - override - returns (uint256) - { - if (functionId == uint8(FunctionId.VALIDATION_TEMPORARY_OWNER)) { - (address signer, ECDSA.RecoverError err) = - userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature); - if (err != ECDSA.RecoverError.NoError) { - revert InvalidSignature(); - } - bytes4 selector = bytes4(userOp.callData[0:4]); - bytes memory key = msg.sender.allocateAssociatedStorageKey(0, 1); - StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(signer, selector))); - SessionInfo storage duration = _castPtrToStruct(ptr); - uint48 validAfter = duration.validAfter; - uint48 validUntil = duration.validUntil; - - return _packValidationData(validUntil == 0, validUntil, validAfter); - } - revert NotImplemented(); - } - - /// @inheritdoc BasePlugin - function runtimeValidationFunction(uint8 functionId, address sender, uint256, bytes calldata data) - external - view - override - { - if (functionId == uint8(FunctionId.VALIDATION_TEMPORARY_OWNER)) { - bytes4 selector = bytes4(data[0:4]); - bytes memory key = msg.sender.allocateAssociatedStorageKey(0, 1); - StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sender, selector))); - SessionInfo storage duration = _castPtrToStruct(ptr); - uint48 validAfter = duration.validAfter; - uint48 validUntil = duration.validUntil; - - if (validUntil != 0) { - if (block.timestamp < validAfter || block.timestamp > validUntil) { - revert WrongTimeRangeForSession(); - } - return; - } - revert NotAuthorized(); - } - revert NotImplemented(); - } - - /// @inheritdoc BasePlugin - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - - manifest.executionFunctions = new bytes4[](4); - manifest.executionFunctions[0] = this.addSessionKey.selector; - manifest.executionFunctions[1] = this.removeSessionKey.selector; - manifest.executionFunctions[2] = this.addSessionKeyBatch.selector; - manifest.executionFunctions[3] = this.removeSessionKeyBatch.selector; - - ManifestFunction memory ownerValidationFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, // Unused. - dependencyIndex: 0 // Used as first index. - }); - manifest.validationFunctions = new ManifestAssociatedFunction[](5); - manifest.validationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.addSessionKey.selector, - associatedFunction: ownerValidationFunction - }); - manifest.validationFunctions[1] = ManifestAssociatedFunction({ - executionSelector: this.removeSessionKey.selector, - associatedFunction: ownerValidationFunction - }); - manifest.validationFunctions[2] = ManifestAssociatedFunction({ - executionSelector: this.addSessionKeyBatch.selector, - associatedFunction: ownerValidationFunction - }); - manifest.validationFunctions[3] = ManifestAssociatedFunction({ - executionSelector: this.removeSessionKeyBatch.selector, - associatedFunction: ownerValidationFunction - }); - - ManifestFunction memory alwaysAllowFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, - functionId: 0, // Unused. - dependencyIndex: 0 // Unused. - }); - - manifest.validationFunctions[4] = ManifestAssociatedFunction({ - executionSelector: this.getSessionDuration.selector, - associatedFunction: alwaysAllowFunction - }); - - manifest.dependencyInterfaceIds = new bytes4[](1); - manifest.dependencyInterfaceIds[0] = type(ISingleOwnerPlugin).interfaceId; - - return manifest; - } - - /// @inheritdoc BasePlugin - function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { - PluginMetadata memory metadata; - metadata.name = NAME; - metadata.version = VERSION; - metadata.author = AUTHOR; - - return metadata; - } - - // ┏━━━━━━━━━━━━━━━┓ - // ┃ EIP-165 ┃ - // ┗━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function supportsInterface(bytes4 interfaceId) public view override returns (bool) { - return interfaceId == type(IModularSessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Internal / Private functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - function _addSessionKey( - address account, - address sessionKey, - bytes4 allowedSelector, - uint48 validAfter, - uint48 validUntil - ) internal { - if (validUntil <= validAfter) { - revert WrongTimeRangeForSession(); - } - bytes memory key = account.allocateAssociatedStorageKey(0, 1); - StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, allowedSelector))); - SessionInfo storage sessionInfo = _castPtrToStruct(ptr); - sessionInfo.validAfter = validAfter; - sessionInfo.validUntil = validUntil; - - EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; - sessionKeySet.add(_castToBytes32(sessionKey, allowedSelector)); - } - - function _removeSessionKey(address account, address sessionKey, bytes4 allowedSelector) internal { - bytes memory key = account.allocateAssociatedStorageKey(0, 1); - StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, allowedSelector))); - SessionInfo storage sessionInfo = _castPtrToStruct(ptr); - sessionInfo.validAfter = 0; - sessionInfo.validUntil = 0; - - EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; - sessionKeySet.remove(_castToBytes32(sessionKey, allowedSelector)); - } - - function _castPtrToStruct(StoragePointer ptr) internal pure returns (SessionInfo storage val) { - assembly ("memory-safe") { - val.slot := ptr - } - } - - function _castToBytes32(address addr, bytes4 b4) internal pure returns (bytes32 res) { - assembly { - res := or(shl(32, addr), b4) - } - } - - function _castToAddressAndBytes4(bytes32 b32) internal pure returns (address addr, bytes4 b4) { - assembly { - addr := shr(32, b32) - b4 := and(b32, 0xFFFFFFFF) - } - } - - function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) - internal - pure - returns (uint256) - { - return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); - } -} +// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +// import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +// import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; +// import {UpgradeableModularAccount} from "../../account/UpgradeableModularAccount.sol"; +// import { +// ManifestFunction, +// ManifestAssociatedFunctionType, +// ManifestAssociatedFunction, +// PluginManifest, +// PluginMetadata, +// SelectorPermission +// } from "../../interfaces/IPlugin.sol"; +// import {BasePlugin} from "../../plugins/BasePlugin.sol"; +// import {IModularSessionKeyPlugin} from "./interfaces/ISessionKeyPlugin.sol"; +// import {ISingleOwnerPlugin} from "../../plugins/owner/ISingleOwnerPlugin.sol"; +// import {SingleOwnerPlugin} from "../../plugins/owner/SingleOwnerPlugin.sol"; +// import {PluginStorageLib, StoragePointer} from "../../libraries/PluginStorageLib.sol"; + +// /// @title Modular Session Key Plugin +// /// @author Decipher ERC-6900 Team +// /// @notice This plugin allows some designated EOA or smart contract to temporarily +// /// own a modular account. Note that this plugin is ONLY for demonstrating the purpose +// /// of the functionalities of ERC-6900, and MUST not be used at the production level. +// /// This modular session key plugin acts as a 'parent plugin' for all specific session +// /// keys. Using dependency, this plugin can be thought as a parent contract that stores +// /// session key duration information, and validation functions for session keys. All +// /// logics for session keys will be implemented in child plugins. +// /// It allows for session key owners to access MSCA both through user operation and +// /// runtime, with its own validation functions. +// /// Also, it has a dependency on SingleOwnerPlugin, to make sure that only the owner of +// /// the MSCA can add or remove session keys. +// contract ModularSessionKeyPlugin is BasePlugin, IModularSessionKeyPlugin { +// using ECDSA for bytes32; +// using PluginStorageLib for address; +// using PluginStorageLib for bytes; +// using EnumerableSet for EnumerableSet.Bytes32Set; + +// string public constant NAME = "Modular Session Key Plugin"; +// string public constant VERSION = "1.0.0"; +// string public constant AUTHOR = "Decipher ERC-6900 Team"; + +// uint256 internal constant _SIG_VALIDATION_FAILED = 1; + +// mapping(address account => EnumerableSet.Bytes32Set) private _sessionKeySet; + +// struct SessionInfo { +// uint48 validAfter; +// uint48 validUntil; +// } + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Execution functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc IModularSessionKeyPlugin +// function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 validAfter, uint48 validUntil) +// external +// { +// _addSessionKey(msg.sender, sessionKey, allowedSelector, validAfter, validUntil); +// emit SessionKeyAdded(msg.sender, sessionKey, allowedSelector, validAfter, validUntil); +// } + +// /// @inheritdoc IModularSessionKeyPlugin +// function removeSessionKey(address sessionKey, bytes4 allowedSelector) external { +// _removeSessionKey(msg.sender, sessionKey, allowedSelector); +// emit SessionKeyRemoved(msg.sender, sessionKey, allowedSelector); +// } + +// /// @inheritdoc IModularSessionKeyPlugin +// function addSessionKeyBatch( +// address[] calldata sessionKeys, +// bytes4[] calldata allowedSelectors, +// uint48[] calldata validAfters, +// uint48[] calldata validUntils +// ) external { +// if ( +// sessionKeys.length != allowedSelectors.length || sessionKeys.length != validAfters.length +// || sessionKeys.length != validUntils.length +// ) { +// revert WrongDataLength(); +// } +// for (uint256 i = 0; i < sessionKeys.length;) { +// _addSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i], validAfters[i], validUntils[i]); + +// unchecked { +// ++i; +// } +// } +// emit SessionKeysAdded(msg.sender, sessionKeys, allowedSelectors, validAfters, validUntils); +// } + +// function removeSessionKeyBatch(address[] calldata sessionKeys, bytes4[] calldata allowedSelectors) external +// { +// if (sessionKeys.length != allowedSelectors.length) { +// revert WrongDataLength(); +// } +// for (uint256 i = 0; i < sessionKeys.length;) { +// _removeSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i]); + +// unchecked { +// ++i; +// } +// } +// emit SessionKeysRemoved(msg.sender, sessionKeys, allowedSelectors); +// } + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Plugin view functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc IModularSessionKeyPlugin +// function getSessionDuration(address account, address sessionKey, bytes4 allowedSelector) +// external +// view +// returns (uint48 validAfter, uint48 validUntil) +// { +// bytes memory key = account.allocateAssociatedStorageKey(0, 1); +// StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, +// allowedSelector))); +// SessionInfo storage sessionInfo = _castPtrToStruct(ptr); +// validAfter = sessionInfo.validAfter; +// validUntil = sessionInfo.validUntil; +// } + +// /// @inheritdoc IModularSessionKeyPlugin +// function getSessionKeysAndSelectors(address account) +// external +// view +// returns (address[] memory sessionKeys, bytes4[] memory selectors) +// { +// EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; +// uint256 length = sessionKeySet.length(); +// sessionKeys = new address[](length); +// selectors = new bytes4[](length); +// for (uint256 i = 0; i < length;) { +// (sessionKeys[i], selectors[i]) = _castToAddressAndBytes4(sessionKeySet.at(i)); + +// unchecked { +// ++i; +// } +// } +// } + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Plugin interface functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc BasePlugin +// function onInstall(bytes calldata data) external override { +// if (data.length != 0) { +// ( +// address[] memory sessionKeys, +// bytes4[] memory allowedSelectors, +// uint48[] memory validAfters, +// uint48[] memory validUntils +// ) = abi.decode(data, (address[], bytes4[], uint48[], uint48[])); +// if ( +// sessionKeys.length != allowedSelectors.length || sessionKeys.length != validAfters.length +// || sessionKeys.length != validUntils.length +// ) { +// revert WrongDataLength(); +// } +// for (uint256 i = 0; i < sessionKeys.length;) { +// _addSessionKey(msg.sender, sessionKeys[i], allowedSelectors[i], validAfters[i], validUntils[i]); + +// unchecked { +// ++i; +// } +// } +// } +// } + +// /// @inheritdoc BasePlugin +// function onUninstall(bytes calldata) external override { +// EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[msg.sender]; +// uint256 length = sessionKeySet.length(); +// for (uint256 i = 0; i < length;) { +// (address sessionKey, bytes4 allowedSelecor) = _castToAddressAndBytes4(sessionKeySet.at(i)); +// _removeSessionKey(msg.sender, sessionKey, allowedSelecor); + +// unchecked { +// ++i; +// } +// } +// } + +// /// @inheritdoc BasePlugin +// function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) +// external +// view +// override +// returns (uint256) +// { +// if (functionId == uint8(FunctionId.VALIDATION_TEMPORARY_OWNER)) { +// (address signer, ECDSA.RecoverError err) = +// userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature); +// if (err != ECDSA.RecoverError.NoError) { +// revert InvalidSignature(); +// } +// bytes4 selector = bytes4(userOp.callData[0:4]); +// bytes memory key = msg.sender.allocateAssociatedStorageKey(0, 1); +// StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(signer, selector))); +// SessionInfo storage duration = _castPtrToStruct(ptr); +// uint48 validAfter = duration.validAfter; +// uint48 validUntil = duration.validUntil; + +// return _packValidationData(validUntil == 0, validUntil, validAfter); +// } +// revert NotImplemented(); +// } + +// /// @inheritdoc BasePlugin +// function runtimeValidationFunction(uint8 functionId, address sender, uint256, bytes calldata data) +// external +// view +// override +// { +// if (functionId == uint8(FunctionId.VALIDATION_TEMPORARY_OWNER)) { +// bytes4 selector = bytes4(data[0:4]); +// bytes memory key = msg.sender.allocateAssociatedStorageKey(0, 1); +// StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sender, selector))); +// SessionInfo storage duration = _castPtrToStruct(ptr); +// uint48 validAfter = duration.validAfter; +// uint48 validUntil = duration.validUntil; + +// if (validUntil != 0) { +// if (block.timestamp < validAfter || block.timestamp > validUntil) { +// revert WrongTimeRangeForSession(); +// } +// return; +// } +// revert NotAuthorized(); +// } +// revert NotImplemented(); +// } + +// /// @inheritdoc BasePlugin +// function pluginManifest() external pure override returns (PluginManifest memory) { +// PluginManifest memory manifest; + +// manifest.executionFunctions = new bytes4[](4); +// manifest.executionFunctions[0] = this.addSessionKey.selector; +// manifest.executionFunctions[1] = this.removeSessionKey.selector; +// manifest.executionFunctions[2] = this.addSessionKeyBatch.selector; +// manifest.executionFunctions[3] = this.removeSessionKeyBatch.selector; + +// ManifestFunction memory ownerValidationFunction = ManifestFunction({ +// functionType: ManifestAssociatedFunctionType.DEPENDENCY, +// functionId: 0, // Unused. +// dependencyIndex: 0 // Used as first index. +// }); +// manifest.validationFunctions = new ManifestAssociatedFunction[](5); +// manifest.validationFunctions[0] = ManifestAssociatedFunction({ +// executionSelector: this.addSessionKey.selector, +// associatedFunction: ownerValidationFunction +// }); +// manifest.validationFunctions[1] = ManifestAssociatedFunction({ +// executionSelector: this.removeSessionKey.selector, +// associatedFunction: ownerValidationFunction +// }); +// manifest.validationFunctions[2] = ManifestAssociatedFunction({ +// executionSelector: this.addSessionKeyBatch.selector, +// associatedFunction: ownerValidationFunction +// }); +// manifest.validationFunctions[3] = ManifestAssociatedFunction({ +// executionSelector: this.removeSessionKeyBatch.selector, +// associatedFunction: ownerValidationFunction +// }); + +// ManifestFunction memory alwaysAllowFunction = ManifestFunction({ +// functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, +// functionId: 0, // Unused. +// dependencyIndex: 0 // Unused. +// }); + +// manifest.validationFunctions[4] = ManifestAssociatedFunction({ +// executionSelector: this.getSessionDuration.selector, +// associatedFunction: alwaysAllowFunction +// }); + +// manifest.dependencyInterfaceIds = new bytes4[](1); +// manifest.dependencyInterfaceIds[0] = type(ISingleOwnerPlugin).interfaceId; + +// return manifest; +// } + +// /// @inheritdoc BasePlugin +// function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { +// PluginMetadata memory metadata; +// metadata.name = NAME; +// metadata.version = VERSION; +// metadata.author = AUTHOR; + +// return metadata; +// } + +// // ┏━━━━━━━━━━━━━━━┓ +// // ┃ EIP-165 ┃ +// // ┗━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc BasePlugin +// function supportsInterface(bytes4 interfaceId) public view override returns (bool) { +// return interfaceId == type(IModularSessionKeyPlugin).interfaceId || +// super.supportsInterface(interfaceId); +// } + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Internal / Private functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// function _addSessionKey( +// address account, +// address sessionKey, +// bytes4 allowedSelector, +// uint48 validAfter, +// uint48 validUntil +// ) internal { +// if (validUntil <= validAfter) { +// revert WrongTimeRangeForSession(); +// } +// bytes memory key = account.allocateAssociatedStorageKey(0, 1); +// StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, +// allowedSelector))); +// SessionInfo storage sessionInfo = _castPtrToStruct(ptr); +// sessionInfo.validAfter = validAfter; +// sessionInfo.validUntil = validUntil; + +// EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; +// sessionKeySet.add(_castToBytes32(sessionKey, allowedSelector)); +// } + +// function _removeSessionKey(address account, address sessionKey, bytes4 allowedSelector) internal { +// bytes memory key = account.allocateAssociatedStorageKey(0, 1); +// StoragePointer ptr = key.associatedStorageLookup(keccak256(abi.encodePacked(sessionKey, +// allowedSelector))); +// SessionInfo storage sessionInfo = _castPtrToStruct(ptr); +// sessionInfo.validAfter = 0; +// sessionInfo.validUntil = 0; + +// EnumerableSet.Bytes32Set storage sessionKeySet = _sessionKeySet[account]; +// sessionKeySet.remove(_castToBytes32(sessionKey, allowedSelector)); +// } + +// function _castPtrToStruct(StoragePointer ptr) internal pure returns (SessionInfo storage val) { +// assembly ("memory-safe") { +// val.slot := ptr +// } +// } + +// function _castToBytes32(address addr, bytes4 b4) internal pure returns (bytes32 res) { +// assembly { +// res := or(shl(32, addr), b4) +// } +// } + +// function _castToAddressAndBytes4(bytes32 b32) internal pure returns (address addr, bytes4 b4) { +// assembly { +// addr := shr(32, b32) +// b4 := and(b32, 0xFFFFFFFF) +// } +// } + +// function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) +// internal +// pure +// returns (uint256) +// { +// return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); +// } +// } diff --git a/src/samples/plugins/TokenSessionKeyPlugin.sol b/src/samples/plugins/TokenSessionKeyPlugin.sol index ebc58eda..82cc6d24 100644 --- a/src/samples/plugins/TokenSessionKeyPlugin.sol +++ b/src/samples/plugins/TokenSessionKeyPlugin.sol @@ -1,117 +1,117 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import { - ManifestFunction, - ManifestAssociatedFunctionType, - ManifestAssociatedFunction, - PluginManifest, - PluginMetadata, - SelectorPermission, - ManifestExternalCallPermission -} from "../../interfaces/IPlugin.sol"; -import {BasePlugin} from "../../plugins/BasePlugin.sol"; -import {ModularSessionKeyPlugin} from "./ModularSessionKeyPlugin.sol"; -import {ITokenSessionKeyPlugin} from "./interfaces/ITokenSessionKeyPlugin.sol"; -import {IModularSessionKeyPlugin} from "./interfaces/ISessionKeyPlugin.sol"; -import {IPluginExecutor} from "../../interfaces/IPluginExecutor.sol"; - -/// @title Token Session Key Plugin -/// @author Decipher ERC-6900 Team -/// @notice This plugin acts as a 'child plugin' for ModularSessionKeyPlugin. -/// It implements the logic for session keys that are allowed to call ERC20 -/// transferFrom function. It allows for session key owners to access MSCA -/// with `transferFromSessionKey` function, which calls `executeFromPluginExternal` -/// function in PluginExecutor contract. -/// The target ERC20 contract and the selector for transferFrom function are hardcoded -/// in this plugin, since the pluginManifest function requires the information of -/// permitted external calls not to be changed in the future. For other child session -/// key plugins, there can be a set of permitted external calls according to the -/// specific needs. -contract TokenSessionKeyPlugin is BasePlugin, ITokenSessionKeyPlugin { - string public constant NAME = "Token Session Key Plugin"; - string public constant VERSION = "1.0.0"; - string public constant AUTHOR = "Decipher ERC-6900 Team"; - - // Mock address of target ERC20 contract - address public constant TARGET_ERC20_CONTRACT = 0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD; - bytes4 public constant TRANSFERFROM_SELECTOR = - bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Execution functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc ITokenSessionKeyPlugin - function transferFromSessionKey(address target, address from, address to, uint256 amount) - external - returns (bytes memory returnData) - { - bytes memory data = abi.encodeWithSelector(TRANSFERFROM_SELECTOR, from, to, amount); - returnData = IPluginExecutor(msg.sender).executeFromPluginExternal(target, 0, data); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function onInstall(bytes calldata data) external override {} - - /// @inheritdoc BasePlugin - function onUninstall(bytes calldata data) external override {} - - /// @inheritdoc BasePlugin - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - - manifest.executionFunctions = new bytes4[](1); - manifest.executionFunctions[0] = this.transferFromSessionKey.selector; - - ManifestFunction memory tempOwnerValidationFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, // Unused - dependencyIndex: 0 // Used as first index - }); - - manifest.validationFunctions = new ManifestAssociatedFunction[](1); - manifest.validationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.transferFromSessionKey.selector, - associatedFunction: tempOwnerValidationFunction - }); - - manifest.dependencyInterfaceIds = new bytes4[](1); - manifest.dependencyInterfaceIds[0] = type(IModularSessionKeyPlugin).interfaceId; - - bytes4[] memory permittedExternalSelectors = new bytes4[](1); - permittedExternalSelectors[0] = TRANSFERFROM_SELECTOR; - - manifest.permittedExternalCalls = new ManifestExternalCallPermission[](1); - manifest.permittedExternalCalls[0] = ManifestExternalCallPermission({ - externalAddress: TARGET_ERC20_CONTRACT, - permitAnySelector: false, - selectors: permittedExternalSelectors - }); - - return manifest; - } - - /// @inheritdoc BasePlugin - function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { - PluginMetadata memory metadata; - metadata.name = NAME; - metadata.version = VERSION; - metadata.author = AUTHOR; - - return metadata; - } - - // ┏━━━━━━━━━━━━━━━┓ - // ┃ EIP-165 ┃ - // ┗━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function supportsInterface(bytes4 interfaceId) public view override returns (bool) { - return interfaceId == type(ITokenSessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); - } -} +// import { +// ManifestFunction, +// ManifestAssociatedFunctionType, +// ManifestAssociatedFunction, +// PluginManifest, +// PluginMetadata, +// SelectorPermission, +// ManifestExternalCallPermission +// } from "../../interfaces/IPlugin.sol"; +// import {BasePlugin} from "../../plugins/BasePlugin.sol"; +// import {ModularSessionKeyPlugin} from "./ModularSessionKeyPlugin.sol"; +// import {ITokenSessionKeyPlugin} from "./interfaces/ITokenSessionKeyPlugin.sol"; +// import {IModularSessionKeyPlugin} from "./interfaces/ISessionKeyPlugin.sol"; +// import {IPluginExecutor} from "../../interfaces/IPluginExecutor.sol"; + +// /// @title Token Session Key Plugin +// /// @author Decipher ERC-6900 Team +// /// @notice This plugin acts as a 'child plugin' for ModularSessionKeyPlugin. +// /// It implements the logic for session keys that are allowed to call ERC20 +// /// transferFrom function. It allows for session key owners to access MSCA +// /// with `transferFromSessionKey` function, which calls `executeFromPluginExternal` +// /// function in PluginExecutor contract. +// /// The target ERC20 contract and the selector for transferFrom function are hardcoded +// /// in this plugin, since the pluginManifest function requires the information of +// /// permitted external calls not to be changed in the future. For other child session +// /// key plugins, there can be a set of permitted external calls according to the +// /// specific needs. +// contract TokenSessionKeyPlugin is BasePlugin, ITokenSessionKeyPlugin { +// string public constant NAME = "Token Session Key Plugin"; +// string public constant VERSION = "1.0.0"; +// string public constant AUTHOR = "Decipher ERC-6900 Team"; + +// // Mock address of target ERC20 contract +// address public constant TARGET_ERC20_CONTRACT = 0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD; +// bytes4 public constant TRANSFERFROM_SELECTOR = +// bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Execution functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc ITokenSessionKeyPlugin +// function transferFromSessionKey(address target, address from, address to, uint256 amount) +// external +// returns (bytes memory returnData) +// { +// bytes memory data = abi.encodeWithSelector(TRANSFERFROM_SELECTOR, from, to, amount); +// returnData = IPluginExecutor(msg.sender).executeFromPluginExternal(target, 0, data); +// } + +// // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// // ┃ Plugin interface functions ┃ +// // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc BasePlugin +// function onInstall(bytes calldata data) external override {} + +// /// @inheritdoc BasePlugin +// function onUninstall(bytes calldata data) external override {} + +// /// @inheritdoc BasePlugin +// function pluginManifest() external pure override returns (PluginManifest memory) { +// PluginManifest memory manifest; + +// manifest.executionFunctions = new bytes4[](1); +// manifest.executionFunctions[0] = this.transferFromSessionKey.selector; + +// ManifestFunction memory tempOwnerValidationFunction = ManifestFunction({ +// functionType: ManifestAssociatedFunctionType.DEPENDENCY, +// functionId: 0, // Unused +// dependencyIndex: 0 // Used as first index +// }); + +// manifest.validationFunctions = new ManifestAssociatedFunction[](1); +// manifest.validationFunctions[0] = ManifestAssociatedFunction({ +// executionSelector: this.transferFromSessionKey.selector, +// associatedFunction: tempOwnerValidationFunction +// }); + +// manifest.dependencyInterfaceIds = new bytes4[](1); +// manifest.dependencyInterfaceIds[0] = type(IModularSessionKeyPlugin).interfaceId; + +// bytes4[] memory permittedExternalSelectors = new bytes4[](1); +// permittedExternalSelectors[0] = TRANSFERFROM_SELECTOR; + +// manifest.permittedExternalCalls = new ManifestExternalCallPermission[](1); +// manifest.permittedExternalCalls[0] = ManifestExternalCallPermission({ +// externalAddress: TARGET_ERC20_CONTRACT, +// permitAnySelector: false, +// selectors: permittedExternalSelectors +// }); + +// return manifest; +// } + +// /// @inheritdoc BasePlugin +// function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { +// PluginMetadata memory metadata; +// metadata.name = NAME; +// metadata.version = VERSION; +// metadata.author = AUTHOR; + +// return metadata; +// } + +// // ┏━━━━━━━━━━━━━━━┓ +// // ┃ EIP-165 ┃ +// // ┗━━━━━━━━━━━━━━━┛ + +// /// @inheritdoc BasePlugin +// function supportsInterface(bytes4 interfaceId) public view override returns (bool) { +// return interfaceId == type(ITokenSessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); +// } +// } diff --git a/src/samples/plugins/interfaces/ISessionKeyPlugin.sol b/src/samples/plugins/interfaces/ISessionKeyPlugin.sol index 18a2204c..3d378d29 100644 --- a/src/samples/plugins/interfaces/ISessionKeyPlugin.sol +++ b/src/samples/plugins/interfaces/ISessionKeyPlugin.sol @@ -1,108 +1,109 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; +// import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; -interface IModularSessionKeyPlugin { - enum FunctionId { - VALIDATION_TEMPORARY_OWNER - } +// interface IModularSessionKeyPlugin { +// enum FunctionId { +// VALIDATION_TEMPORARY_OWNER +// } - /// @notice This event is emitted when a session key is added to the account. - /// @param account The account whose session key is updated. - /// @param sessionKey The address of the session key. - /// @param selector The selector of the function that the session key is allowed to call. - /// @param validAfter The time after which the owner is valid. - /// @param validUntil The time until which the owner is valid. - event SessionKeyAdded( - address indexed account, address indexed sessionKey, bytes4 selector, uint48 validAfter, uint48 validUntil - ); +// /// @notice This event is emitted when a session key is added to the account. +// /// @param account The account whose session key is updated. +// /// @param sessionKey The address of the session key. +// /// @param selector The selector of the function that the session key is allowed to call. +// /// @param validAfter The time after which the owner is valid. +// /// @param validUntil The time until which the owner is valid. +// event SessionKeyAdded( +// address indexed account, address indexed sessionKey, bytes4 selector, uint48 validAfter, uint48 +// validUntil +// ); - /// @notice This event is emitted when a session key is removed from the account. - /// @param account The account whose session key is updated. - /// @param sessionKey The address of the session key. - /// @param selector The selector of the function that the session key is allowed to call. - event SessionKeyRemoved(address indexed account, address indexed sessionKey, bytes4 selector); +// /// @notice This event is emitted when a session key is removed from the account. +// /// @param account The account whose session key is updated. +// /// @param sessionKey The address of the session key. +// /// @param selector The selector of the function that the session key is allowed to call. +// event SessionKeyRemoved(address indexed account, address indexed sessionKey, bytes4 selector); - /// @notice This event is emitted when session keys are added to the account. - /// @param account The account whose session keys are updated. - /// @param sessionKeys The addresses of the session keys. - /// @param selectors The selectors of the functions that the session keys are allowed to call. - /// @param validAfters The times after which the owners are valid. - /// @param validUntils The times until which the owners are valid. - event SessionKeysAdded( - address indexed account, - address[] sessionKeys, - bytes4[] selectors, - uint48[] validAfters, - uint48[] validUntils - ); +// /// @notice This event is emitted when session keys are added to the account. +// /// @param account The account whose session keys are updated. +// /// @param sessionKeys The addresses of the session keys. +// /// @param selectors The selectors of the functions that the session keys are allowed to call. +// /// @param validAfters The times after which the owners are valid. +// /// @param validUntils The times until which the owners are valid. +// event SessionKeysAdded( +// address indexed account, +// address[] sessionKeys, +// bytes4[] selectors, +// uint48[] validAfters, +// uint48[] validUntils +// ); - /// @notice This event is emitted when session keys are removed from the account. - /// @param account The account whose session keys are updated. - /// @param sessionKeys The addresses of the session keys. - /// @param selectors The selectors of the functions that the session keys are allowed to call. - event SessionKeysRemoved(address indexed account, address[] sessionKeys, bytes4[] selectors); +// /// @notice This event is emitted when session keys are removed from the account. +// /// @param account The account whose session keys are updated. +// /// @param sessionKeys The addresses of the session keys. +// /// @param selectors The selectors of the functions that the session keys are allowed to call. +// event SessionKeysRemoved(address indexed account, address[] sessionKeys, bytes4[] selectors); - error InvalidSignature(); - error NotAuthorized(); - error WrongTimeRangeForSession(); - error WrongDataLength(); +// error InvalidSignature(); +// error NotAuthorized(); +// error WrongTimeRangeForSession(); +// error WrongDataLength(); - /// @notice Add a session key to the account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. The function selector installed by a child session key plugin - /// is passed as a parameter, which enforces its own permissions on the calls it can make. - /// @param sessionKey The address of the session key. - /// @param allowedSelector The selector of the function that the session key is allowed to call. - /// @param validAfter The time after which the owner is valid. - /// @param validUntil The time until which the owner is valid. - function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 validAfter, uint48 validUntil) - external; +// /// @notice Add a session key to the account. +// /// @dev This function is installed on the account as part of plugin installation, and should +// /// only be called from an account. The function selector installed by a child session key plugin +// /// is passed as a parameter, which enforces its own permissions on the calls it can make. +// /// @param sessionKey The address of the session key. +// /// @param allowedSelector The selector of the function that the session key is allowed to call. +// /// @param validAfter The time after which the owner is valid. +// /// @param validUntil The time until which the owner is valid. +// function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 validAfter, uint48 validUntil) +// external; - /// @notice Remove a session key from the account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. - /// @param sessionKey The address of the session key. - /// @param allowedSelector The selector of the function that the session key is allowed to call. - function removeSessionKey(address sessionKey, bytes4 allowedSelector) external; +// /// @notice Remove a session key from the account. +// /// @dev This function is installed on the account as part of plugin installation, and should +// /// only be called from an account. +// /// @param sessionKey The address of the session key. +// /// @param allowedSelector The selector of the function that the session key is allowed to call. +// function removeSessionKey(address sessionKey, bytes4 allowedSelector) external; - /// @notice Add session keys to the account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. - /// @param sessionKeys The addresses of the session keys. - /// @param allowedSelectors The selectors of the functions that the session keys are allowed to call. - /// @param validAfters The times after which the owners are valid. - /// @param validUntils The times until which the owners are valid. - function addSessionKeyBatch( - address[] calldata sessionKeys, - bytes4[] calldata allowedSelectors, - uint48[] calldata validAfters, - uint48[] calldata validUntils - ) external; +// /// @notice Add session keys to the account. +// /// @dev This function is installed on the account as part of plugin installation, and should +// /// only be called from an account. +// /// @param sessionKeys The addresses of the session keys. +// /// @param allowedSelectors The selectors of the functions that the session keys are allowed to call. +// /// @param validAfters The times after which the owners are valid. +// /// @param validUntils The times until which the owners are valid. +// function addSessionKeyBatch( +// address[] calldata sessionKeys, +// bytes4[] calldata allowedSelectors, +// uint48[] calldata validAfters, +// uint48[] calldata validUntils +// ) external; - /// @notice Remove session keys from the account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. - /// @param sessionKeys The addresses of the session keys. - /// @param allowedSelectors The selectors of the functions that the session keys are allowed to call. - function removeSessionKeyBatch(address[] calldata sessionKeys, bytes4[] calldata allowedSelectors) external; +// /// @notice Remove session keys from the account. +// /// @dev This function is installed on the account as part of plugin installation, and should +// /// only be called from an account. +// /// @param sessionKeys The addresses of the session keys. +// /// @param allowedSelectors The selectors of the functions that the session keys are allowed to call. +// function removeSessionKeyBatch(address[] calldata sessionKeys, bytes4[] calldata allowedSelectors) external; - /// @notice Get Session data for a given account and session key. - /// @param account The account to get session data for. - /// @param sessionKey The address of the session key. - /// @param allowedSelector The selector of the function that the session key is allowed to call. - function getSessionDuration(address account, address sessionKey, bytes4 allowedSelector) - external - view - returns (uint48 validAfter, uint48 validUntil); +// /// @notice Get Session data for a given account and session key. +// /// @param account The account to get session data for. +// /// @param sessionKey The address of the session key. +// /// @param allowedSelector The selector of the function that the session key is allowed to call. +// function getSessionDuration(address account, address sessionKey, bytes4 allowedSelector) +// external +// view +// returns (uint48 validAfter, uint48 validUntil); - /// @notice Get all session keys and selectors for a given account. - /// @param account The account to get session keys and selectors for. - /// @return sessionKeys The addresses of the session keys. - /// @return selectors The selectors of the functions that the session keys are allowed to call. - function getSessionKeysAndSelectors(address account) - external - view - returns (address[] memory sessionKeys, bytes4[] memory selectors); -} +// /// @notice Get all session keys and selectors for a given account. +// /// @param account The account to get session keys and selectors for. +// /// @return sessionKeys The addresses of the session keys. +// /// @return selectors The selectors of the functions that the session keys are allowed to call. +// function getSessionKeysAndSelectors(address account) +// external +// view +// returns (address[] memory sessionKeys, bytes4[] memory selectors); +// } diff --git a/src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol b/src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol index 65e64113..2e990ef7 100644 --- a/src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol +++ b/src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol @@ -1,19 +1,19 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; +// import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; -interface ITokenSessionKeyPlugin { - error NotAuthorized(); +// interface ITokenSessionKeyPlugin { +// error NotAuthorized(); - /// @notice Route call to executeFromPluginExternal at the MSCA. - /// @dev This function will call with value = 0, since sending ether - /// to ERC20 contract is not a normal case. - /// @param target The target address to execute the call on. - /// @param from The address to transfer tokens from. - /// @param to The address to transfer tokens to. - /// @param amount The amount of tokens to transfer. - function transferFromSessionKey(address target, address from, address to, uint256 amount) - external - returns (bytes memory returnData); -} +// /// @notice Route call to executeFromPluginExternal at the MSCA. +// /// @dev This function will call with value = 0, since sending ether +// /// to ERC20 contract is not a normal case. +// /// @param target The target address to execute the call on. +// /// @param from The address to transfer tokens from. +// /// @param to The address to transfer tokens to. +// /// @param amount The amount of tokens to transfer. +// function transferFromSessionKey(address target, address from, address to, uint256 amount) +// external +// returns (bytes memory returnData); +// } diff --git a/test/libraries/AssociatedLinkedListSetLib.t.sol b/test/libraries/AssociatedLinkedListSetLib.t.sol index 21dd59ef..99ed5e25 100644 --- a/test/libraries/AssociatedLinkedListSetLib.t.sol +++ b/test/libraries/AssociatedLinkedListSetLib.t.sol @@ -1,220 +1,220 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; -import { - AssociatedLinkedListSet, - AssociatedLinkedListSetLib, - SENTINEL_VALUE, - SetValue -} from "../../src/libraries/AssociatedLinkedListSetLib.sol"; +// import {Test} from "forge-std/Test.sol"; +// import { +// AssociatedLinkedListSet, +// AssociatedLinkedListSetLib, +// SENTINEL_VALUE, +// SetValue +// } from "../../src/libraries/AssociatedLinkedListSetLib.sol"; -contract AssociatedLinkedListSetLibTest is Test { - using AssociatedLinkedListSetLib for AssociatedLinkedListSet; +// contract AssociatedLinkedListSetLibTest is Test { +// using AssociatedLinkedListSetLib for AssociatedLinkedListSet; - AssociatedLinkedListSet internal _set1; - AssociatedLinkedListSet internal _set2; +// AssociatedLinkedListSet internal _set1; +// AssociatedLinkedListSet internal _set2; - address internal _associated = address(this); +// address internal _associated = address(this); - // User-defined function for wrapping from bytes30 (uint240) to SetValue - // Can define a custom one for addresses, uints, etc. - function _getListValue(uint240 value) internal pure returns (SetValue) { - return SetValue.wrap(bytes30(value)); - } +// // User-defined function for wrapping from bytes30 (uint240) to SetValue +// // Can define a custom one for addresses, uints, etc. +// function _getListValue(uint240 value) internal pure returns (SetValue) { +// return SetValue.wrap(bytes30(value)); +// } - function test_add_contains() public { - SetValue value = _getListValue(12); +// function test_add_contains() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); - } +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); +// } - function test_empty() public { - SetValue value = _getListValue(12); +// function test_empty() public { +// SetValue value = _getListValue(12); - assertFalse(_set1.contains(_associated, value)); - assertTrue(_set1.isEmpty(_associated)); - } +// assertFalse(_set1.contains(_associated, value)); +// assertTrue(_set1.isEmpty(_associated)); +// } - function test_remove() public { - SetValue value = _getListValue(12); +// function test_remove() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - assertTrue(_set1.tryRemove(_associated, value)); - assertFalse(_set1.contains(_associated, value)); - } +// assertTrue(_set1.tryRemove(_associated, value)); +// assertFalse(_set1.contains(_associated, value)); +// } - function test_remove_empty() public { - SetValue value = _getListValue(12); +// function test_remove_empty() public { +// SetValue value = _getListValue(12); - assertFalse(_set1.tryRemove(_associated, value)); - } +// assertFalse(_set1.tryRemove(_associated, value)); +// } - function test_remove_nonexistent() public { - SetValue value = _getListValue(12); +// function test_remove_nonexistent() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - SetValue value2 = _getListValue(13); - assertFalse(_set1.tryRemove(_associated, value2)); - assertTrue(_set1.contains(_associated, value)); - } +// SetValue value2 = _getListValue(13); +// assertFalse(_set1.tryRemove(_associated, value2)); +// assertTrue(_set1.contains(_associated, value)); +// } - function test_remove_nonexistent_empty() public { - SetValue value = _getListValue(12); +// function test_remove_nonexistent_empty() public { +// SetValue value = _getListValue(12); - assertFalse(_set1.tryRemove(_associated, value)); - } +// assertFalse(_set1.tryRemove(_associated, value)); +// } - function test_remove_nonexistent_empty2() public { - SetValue value = _getListValue(12); +// function test_remove_nonexistent_empty2() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - SetValue value2 = _getListValue(13); - assertFalse(_set1.tryRemove(_associated, value2)); - assertTrue(_set1.contains(_associated, value)); - } +// SetValue value2 = _getListValue(13); +// assertFalse(_set1.tryRemove(_associated, value2)); +// assertTrue(_set1.contains(_associated, value)); +// } - function test_add_remove_add() public { - SetValue value = _getListValue(12); +// function test_add_remove_add() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - assertTrue(_set1.tryRemove(_associated, value)); - assertFalse(_set1.contains(_associated, value)); +// assertTrue(_set1.tryRemove(_associated, value)); +// assertFalse(_set1.contains(_associated, value)); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); - } +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); +// } - function test_add_remove_add_empty() public { - SetValue value = _getListValue(12); +// function test_add_remove_add_empty() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - assertTrue(_set1.tryRemove(_associated, value)); - assertFalse(_set1.contains(_associated, value)); +// assertTrue(_set1.tryRemove(_associated, value)); +// assertFalse(_set1.contains(_associated, value)); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); - } +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); +// } - function test_no_address_collision() public { - SetValue value = _getListValue(12); +// function test_no_address_collision() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); - assertFalse(_set2.contains(_associated, value)); - } +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); +// assertFalse(_set2.contains(_associated, value)); +// } - function test_clear() public { - SetValue value = _getListValue(12); +// function test_clear() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - _set1.clear(_associated); +// _set1.clear(_associated); - assertFalse(_set1.contains(_associated, value)); - assertTrue(_set1.isEmpty(_associated)); - } +// assertFalse(_set1.contains(_associated, value)); +// assertTrue(_set1.isEmpty(_associated)); +// } - function test_getAll() public { - SetValue value = _getListValue(12); - SetValue value2 = _getListValue(13); +// function test_getAll() public { +// SetValue value = _getListValue(12); +// SetValue value2 = _getListValue(13); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.tryAdd(_associated, value2)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value2)); - SetValue[] memory values = _set1.getAll(_associated); - assertEq(values.length, 2); - // Returned set will be in reverse order of added elements - assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value)); - assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); - } +// SetValue[] memory values = _set1.getAll(_associated); +// assertEq(values.length, 2); +// // Returned set will be in reverse order of added elements +// assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value)); +// assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); +// } - function test_getAll2() public { - SetValue value = _getListValue(12); - SetValue value2 = _getListValue(13); - SetValue value3 = _getListValue(14); +// function test_getAll2() public { +// SetValue value = _getListValue(12); +// SetValue value2 = _getListValue(13); +// SetValue value3 = _getListValue(14); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.tryAdd(_associated, value2)); - assertTrue(_set1.tryAdd(_associated, value3)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value2)); +// assertTrue(_set1.tryAdd(_associated, value3)); - SetValue[] memory values = _set1.getAll(_associated); - assertEq(values.length, 3); - // Returned set will be in reverse order of added elements - assertEq(SetValue.unwrap(values[2]), SetValue.unwrap(value)); - assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value2)); - assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value3)); - } +// SetValue[] memory values = _set1.getAll(_associated); +// assertEq(values.length, 3); +// // Returned set will be in reverse order of added elements +// assertEq(SetValue.unwrap(values[2]), SetValue.unwrap(value)); +// assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value2)); +// assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value3)); +// } - function test_getAll_empty() public { - SetValue[] memory values = _set1.getAll(_associated); - assertEq(values.length, 0); - } +// function test_getAll_empty() public { +// SetValue[] memory values = _set1.getAll(_associated); +// assertEq(values.length, 0); +// } - function test_tryRemoveKnown1() public { - SetValue value = _getListValue(12); +// function test_tryRemoveKnown1() public { +// SetValue value = _getListValue(12); - assertTrue(_set1.tryAdd(_associated, value)); - assertTrue(_set1.contains(_associated, value)); +// assertTrue(_set1.tryAdd(_associated, value)); +// assertTrue(_set1.contains(_associated, value)); - assertTrue(_set1.tryRemoveKnown(_associated, value, SENTINEL_VALUE)); - assertFalse(_set1.contains(_associated, value)); - assertTrue(_set1.isEmpty(_associated)); - } +// assertTrue(_set1.tryRemoveKnown(_associated, value, SENTINEL_VALUE)); +// assertFalse(_set1.contains(_associated, value)); +// assertTrue(_set1.isEmpty(_associated)); +// } - function test_tryRemoveKnown2() public { - SetValue value1 = _getListValue(12); - SetValue value2 = _getListValue(13); +// function test_tryRemoveKnown2() public { +// SetValue value1 = _getListValue(12); +// SetValue value2 = _getListValue(13); - assertTrue(_set1.tryAdd(_associated, value1)); - assertTrue(_set1.tryAdd(_associated, value2)); - assertTrue(_set1.contains(_associated, value1)); - assertTrue(_set1.contains(_associated, value2)); +// assertTrue(_set1.tryAdd(_associated, value1)); +// assertTrue(_set1.tryAdd(_associated, value2)); +// assertTrue(_set1.contains(_associated, value1)); +// assertTrue(_set1.contains(_associated, value2)); - // Assert that getAll returns the correct values - SetValue[] memory values = _set1.getAll(_associated); - assertEq(values.length, 2); - assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value1)); - assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); +// // Assert that getAll returns the correct values +// SetValue[] memory values = _set1.getAll(_associated); +// assertEq(values.length, 2); +// assertEq(SetValue.unwrap(values[1]), SetValue.unwrap(value1)); +// assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); - assertTrue(_set1.tryRemoveKnown(_associated, value1, bytes32(SetValue.unwrap(value2)))); - assertFalse(_set1.contains(_associated, value1)); - assertTrue(_set1.contains(_associated, value2)); +// assertTrue(_set1.tryRemoveKnown(_associated, value1, bytes32(SetValue.unwrap(value2)))); +// assertFalse(_set1.contains(_associated, value1)); +// assertTrue(_set1.contains(_associated, value2)); - // Assert that getAll returns the correct values - values = _set1.getAll(_associated); - assertEq(values.length, 1); - assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); - - assertTrue(_set1.tryRemoveKnown(_associated, value2, SENTINEL_VALUE)); - assertFalse(_set1.contains(_associated, value1)); - - assertTrue(_set1.isEmpty(_associated)); - } - - function test_tryRemoveKnown_invalid1() public { - SetValue value1 = _getListValue(12); - SetValue value2 = _getListValue(13); - - assertTrue(_set1.tryAdd(_associated, value1)); - assertTrue(_set1.tryAdd(_associated, value2)); - - assertFalse(_set1.tryRemoveKnown(_associated, value1, bytes32(SetValue.unwrap(value1)))); - assertTrue(_set1.contains(_associated, value1)); - - assertFalse(_set1.tryRemoveKnown(_associated, value2, bytes32(SetValue.unwrap(value2)))); - assertTrue(_set1.contains(_associated, value2)); - } -} +// // Assert that getAll returns the correct values +// values = _set1.getAll(_associated); +// assertEq(values.length, 1); +// assertEq(SetValue.unwrap(values[0]), SetValue.unwrap(value2)); + +// assertTrue(_set1.tryRemoveKnown(_associated, value2, SENTINEL_VALUE)); +// assertFalse(_set1.contains(_associated, value1)); + +// assertTrue(_set1.isEmpty(_associated)); +// } + +// function test_tryRemoveKnown_invalid1() public { +// SetValue value1 = _getListValue(12); +// SetValue value2 = _getListValue(13); + +// assertTrue(_set1.tryAdd(_associated, value1)); +// assertTrue(_set1.tryAdd(_associated, value2)); + +// assertFalse(_set1.tryRemoveKnown(_associated, value1, bytes32(SetValue.unwrap(value1)))); +// assertTrue(_set1.contains(_associated, value1)); + +// assertFalse(_set1.tryRemoveKnown(_associated, value2, bytes32(SetValue.unwrap(value2)))); +// assertTrue(_set1.contains(_associated, value2)); +// } +// } diff --git a/test/libraries/PluginStorageLib.t.sol b/test/libraries/PluginStorageLib.t.sol index 2dc58e1f..3bd1dc76 100644 --- a/test/libraries/PluginStorageLib.t.sol +++ b/test/libraries/PluginStorageLib.t.sol @@ -1,88 +1,89 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; -import {PluginStorageLib, StoragePointer} from "../../src/libraries/PluginStorageLib.sol"; +// import {Test} from "forge-std/Test.sol"; +// import {PluginStorageLib, StoragePointer} from "../../src/libraries/PluginStorageLib.sol"; -contract PluginStorageLibTest is Test { - using PluginStorageLib for bytes; - using PluginStorageLib for bytes32; +// contract PluginStorageLibTest is Test { +// using PluginStorageLib for bytes; +// using PluginStorageLib for bytes32; - uint256 public constant FUZZ_ARR_SIZE = 32; +// uint256 public constant FUZZ_ARR_SIZE = 32; - address public account1; +// address public account1; - struct TestStruct { - uint256 a; - uint256 b; - } +// struct TestStruct { +// uint256 a; +// uint256 b; +// } - function setUp() public { - account1 = makeAddr("account1"); - } +// function setUp() public { +// account1 = makeAddr("account1"); +// } - function test_storagePointer() public { - bytes memory key = PluginStorageLib.allocateAssociatedStorageKey(account1, 0, 1); +// function test_storagePointer() public { +// bytes memory key = PluginStorageLib.allocateAssociatedStorageKey(account1, 0, 1); - StoragePointer ptr = PluginStorageLib.associatedStorageLookup( - key, hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" - ); - TestStruct storage val = _castPtrToStruct(ptr); +// StoragePointer ptr = PluginStorageLib.associatedStorageLookup( +// key, hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" +// ); +// TestStruct storage val = _castPtrToStruct(ptr); - vm.record(); - val.a = 0xdeadbeef; - val.b = 123; - (, bytes32[] memory accountWrites) = vm.accesses(address(this)); +// vm.record(); +// val.a = 0xdeadbeef; +// val.b = 123; +// (, bytes32[] memory accountWrites) = vm.accesses(address(this)); - assertEq(accountWrites.length, 2); - bytes32 expectedKey = keccak256( - abi.encodePacked( - uint256(uint160(account1)), - uint256(0), - hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" - ) - ); - assertEq(accountWrites[0], expectedKey); - assertEq(vm.load(address(this), expectedKey), bytes32(uint256(0xdeadbeef))); - assertEq(accountWrites[1], bytes32(uint256(expectedKey) + 1)); - assertEq(vm.load(address(this), bytes32(uint256(expectedKey) + 1)), bytes32(uint256(123))); - } +// assertEq(accountWrites.length, 2); +// bytes32 expectedKey = keccak256( +// abi.encodePacked( +// uint256(uint160(account1)), +// uint256(0), +// hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" +// ) +// ); +// assertEq(accountWrites[0], expectedKey); +// assertEq(vm.load(address(this), expectedKey), bytes32(uint256(0xdeadbeef))); +// assertEq(accountWrites[1], bytes32(uint256(expectedKey) + 1)); +// assertEq(vm.load(address(this), bytes32(uint256(expectedKey) + 1)), bytes32(uint256(123))); +// } - function testFuzz_storagePointer( - address account, - uint256 batchIndex, - bytes32 inputKey, - uint256[FUZZ_ARR_SIZE] calldata values - ) public { - bytes memory key = PluginStorageLib.allocateAssociatedStorageKey(account, batchIndex, 1); - uint256[FUZZ_ARR_SIZE] storage val = - _castPtrToArray(PluginStorageLib.associatedStorageLookup(key, inputKey)); - // Write values to storage - vm.record(); - for (uint256 i = 0; i < FUZZ_ARR_SIZE; i++) { - val[i] = values[i]; - } - // Assert the writes took place in the right location, and the correct value is stored there - (, bytes32[] memory accountWrites) = vm.accesses(address(this)); - assertEq(accountWrites.length, FUZZ_ARR_SIZE); - for (uint256 i = 0; i < FUZZ_ARR_SIZE; i++) { - bytes32 expectedKey = bytes32( - uint256(keccak256(abi.encodePacked(uint256(uint160(account)), uint256(batchIndex), inputKey))) + i - ); - assertEq(accountWrites[i], expectedKey); - assertEq(vm.load(address(this), expectedKey), bytes32(uint256(values[i]))); - } - } +// function testFuzz_storagePointer( +// address account, +// uint256 batchIndex, +// bytes32 inputKey, +// uint256[FUZZ_ARR_SIZE] calldata values +// ) public { +// bytes memory key = PluginStorageLib.allocateAssociatedStorageKey(account, batchIndex, 1); +// uint256[FUZZ_ARR_SIZE] storage val = +// _castPtrToArray(PluginStorageLib.associatedStorageLookup(key, inputKey)); +// // Write values to storage +// vm.record(); +// for (uint256 i = 0; i < FUZZ_ARR_SIZE; i++) { +// val[i] = values[i]; +// } +// // Assert the writes took place in the right location, and the correct value is stored there +// (, bytes32[] memory accountWrites) = vm.accesses(address(this)); +// assertEq(accountWrites.length, FUZZ_ARR_SIZE); +// for (uint256 i = 0; i < FUZZ_ARR_SIZE; i++) { +// bytes32 expectedKey = bytes32( +// uint256(keccak256(abi.encodePacked(uint256(uint160(account)), uint256(batchIndex), inputKey))) + +// i +// ); +// assertEq(accountWrites[i], expectedKey); +// assertEq(vm.load(address(this), expectedKey), bytes32(uint256(values[i]))); +// } +// } - function _castPtrToArray(StoragePointer ptr) internal pure returns (uint256[FUZZ_ARR_SIZE] storage val) { - assembly ("memory-safe") { - val.slot := ptr - } - } +// function _castPtrToArray(StoragePointer ptr) internal pure returns (uint256[FUZZ_ARR_SIZE] storage val) { +// assembly ("memory-safe") { +// val.slot := ptr +// } +// } - function _castPtrToStruct(StoragePointer ptr) internal pure returns (TestStruct storage val) { - assembly ("memory-safe") { - val.slot := ptr - } - } -} +// function _castPtrToStruct(StoragePointer ptr) internal pure returns (TestStruct storage val) { +// assembly ("memory-safe") { +// val.slot := ptr +// } +// } +// } diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index 03da8019..9d07687e 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -2,17 +2,14 @@ pragma solidity ^0.8.19; import { - ManifestExecutionHook, ManifestFunction, ManifestAssociatedFunctionType, ManifestAssociatedFunction, PluginManifest, PluginMetadata } from "../../../src/interfaces/IPlugin.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; import {ISingleOwnerPlugin} from "../../../src/plugins/owner/ISingleOwnerPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; contract BadTransferOwnershipPlugin is BasePlugin { diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 3eea3ac0..663e5f7f 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -1,18 +1,14 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {FunctionReference} from "../../../src/helpers/FunctionReferenceLib.sol"; import { ManifestFunction, ManifestAssociatedFunctionType, ManifestAssociatedFunction, ManifestExternalCallPermission, - ManifestExecutionHook, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; -import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; import {ResultCreatorPlugin} from "./ReturnDataPluginMocks.sol"; diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index f19c67ca..d2083db6 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -1,21 +1,17 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {FunctionReference} from "../../../src/helpers/FunctionReferenceLib.sol"; import { ManifestFunction, ManifestAssociatedFunctionType, ManifestAssociatedFunction, ManifestExecutionHook, - ManifestExternalCallPermission, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; -import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; -import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; +// solhint-disable-next-line contract-name-camelcase contract BadValidationMagicValue_PreRuntimeValidationHook_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -56,6 +52,7 @@ contract BadValidationMagicValue_PreRuntimeValidationHook_Plugin is BaseTestPlug } } +// solhint-disable-next-line contract-name-camelcase contract BadValidationMagicValue_PreUserOpValidationHook_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -96,6 +93,7 @@ contract BadValidationMagicValue_PreUserOpValidationHook_Plugin is BaseTestPlugi } } +// solhint-disable-next-line contract-name-camelcase contract BadValidationMagicValue_PreExecHook_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -132,6 +130,7 @@ contract BadValidationMagicValue_PreExecHook_Plugin is BaseTestPlugin { } } +// solhint-disable-next-line contract-name-camelcase contract BadValidationMagicValue_PostExecHook_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -167,6 +166,7 @@ contract BadValidationMagicValue_PostExecHook_Plugin is BaseTestPlugin { } } +// solhint-disable-next-line contract-name-camelcase contract BadHookMagicValue_UserOpValidationFunction_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -196,6 +196,7 @@ contract BadHookMagicValue_UserOpValidationFunction_Plugin is BaseTestPlugin { } } +// solhint-disable-next-line contract-name-camelcase contract BadHookMagicValue_RuntimeValidationFunction_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} @@ -225,6 +226,7 @@ contract BadHookMagicValue_RuntimeValidationFunction_Plugin is BaseTestPlugin { } } +// solhint-disable-next-line contract-name-camelcase contract BadHookMagicValue_PostExecHook_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 85925c0e..153a4340 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {FunctionReference} from "../../../src/helpers/FunctionReferenceLib.sol"; import { ManifestFunction, ManifestAssociatedFunctionType, @@ -9,9 +8,7 @@ import { ManifestExternalCallPermission, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; -import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; @@ -60,12 +57,12 @@ contract ResultCreatorPlugin is BaseTestPlugin { } contract ResultConsumerPlugin is BaseTestPlugin { - ResultCreatorPlugin public immutable resultCreator; - RegularResultContract public immutable regularResultContract; + ResultCreatorPlugin public immutable RESULT_CREATOR; + RegularResultContract public immutable REGULAR_RESULT_CONTRACT; constructor(ResultCreatorPlugin _resultCreator, RegularResultContract _regularResultContract) { - resultCreator = _resultCreator; - regularResultContract = _regularResultContract; + RESULT_CREATOR = _resultCreator; + REGULAR_RESULT_CONTRACT = _regularResultContract; } // Check the return data through the executeFromPlugin fallback case @@ -147,7 +144,7 @@ contract ResultConsumerPlugin is BaseTestPlugin { bytes4[] memory allowedSelectors = new bytes4[](1); allowedSelectors[0] = RegularResultContract.foo.selector; manifest.permittedExternalCalls[0] = ManifestExternalCallPermission({ - externalAddress: address(regularResultContract), + externalAddress: address(REGULAR_RESULT_CONTRACT), permitAnySelector: false, selectors: allowedSelectors }); diff --git a/test/samples/plugins/ModularSessionKeyPlugin.t.sol b/test/samples/plugins/ModularSessionKeyPlugin.t.sol index e20cbfca..5001e56f 100644 --- a/test/samples/plugins/ModularSessionKeyPlugin.t.sol +++ b/test/samples/plugins/ModularSessionKeyPlugin.t.sol @@ -1,371 +1,375 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - -import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -import {SingleOwnerPlugin} from "../../../src/plugins/owner/SingleOwnerPlugin.sol"; -import {ISingleOwnerPlugin} from "../../../src/plugins/owner/ISingleOwnerPlugin.sol"; -import {ModularSessionKeyPlugin} from "../../../src/samples/plugins/ModularSessionKeyPlugin.sol"; -import {IModularSessionKeyPlugin} from "../../../src/samples/plugins/interfaces/ISessionKeyPlugin.sol"; -import {TokenSessionKeyPlugin} from "../../../src/samples/plugins/TokenSessionKeyPlugin.sol"; -import {ITokenSessionKeyPlugin} from "../../../src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol"; - -import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; -import {MSCAFactoryFixture} from "../../mocks/MSCAFactoryFixture.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../src/helpers/FunctionReferenceLib.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; -import {MockERC20} from "../../mocks/MockERC20.sol"; - -contract ModularSessionKeyPluginTest is Test { - using ECDSA for bytes32; - - SingleOwnerPlugin public ownerPlugin; - ModularSessionKeyPlugin public modularSessionKeyPlugin; - TokenSessionKeyPlugin public tokenSessionKeyPlugin; - EntryPoint public entryPoint; - MSCAFactoryFixture public factory; - UpgradeableModularAccount public account; - - MockERC20 public mockERC20impl; - MockERC20 public mockERC20; - address public mockEmptyERC20Addr; - - address public owner; - uint256 public ownerKey; - - address public maliciousOwner; - - address public tempOwner; - uint256 public tempOwnerKey; - - address public target; - - address payable public beneficiary; - - uint256 public constant CALL_GAS_LIMIT = 150000; - uint256 public constant VERIFICATION_GAS_LIMIT = 3600000; - - bytes4 public constant TRANSFERFROM_SESSIONKEY_SELECTOR = - ITokenSessionKeyPlugin.transferFromSessionKey.selector; - - // Event declarations (needed for vm.expectEmit) - event UserOperationRevertReason( - bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason - ); - event SessionKeyAdded( - address indexed account, address indexed sessionKey, bytes4 allowedSelector, uint48 _after, uint48 _until - ); - event SessionKeyRemoved(address indexed account, address indexed sessionKey, bytes4 allowedSelector); - event SessionKeysAdded( - address indexed account, address[] sessionKeys, bytes4[] allowedSelectors, uint48[] afters, uint48[] untils - ); - event SessionKeysRemoved(address indexed account, address[] sessionKeys, bytes4[] allowedSelectors); - event PluginUninstalled(address indexed plugin, bool indexed onUninstallSuccess); - - function setUp() public { - ownerPlugin = new SingleOwnerPlugin(); - modularSessionKeyPlugin = new ModularSessionKeyPlugin(); - tokenSessionKeyPlugin = new TokenSessionKeyPlugin(); - - entryPoint = new EntryPoint(); - factory = new MSCAFactoryFixture(entryPoint, ownerPlugin); - mockERC20impl = new MockERC20("Mock", "MCK"); - - // Etching MockERC20 code into hardcoded address at TokenSessionKeyPlugin - mockEmptyERC20Addr = tokenSessionKeyPlugin.TARGET_ERC20_CONTRACT(); - bytes memory code = address(mockERC20impl).code; - vm.etch(mockEmptyERC20Addr, code); - mockERC20 = MockERC20(mockEmptyERC20Addr); - - (owner, ownerKey) = makeAddrAndKey("owner"); - (maliciousOwner,) = makeAddrAndKey("maliciousOwner"); - (tempOwner, tempOwnerKey) = makeAddrAndKey("tempOwner"); - target = makeAddr("target"); - - beneficiary = payable(makeAddr("beneficiary")); - vm.deal(beneficiary, 1 wei); - vm.deal(owner, 10 ether); - - // Here, SingleOwnerPlugin already installed in factory - account = factory.createAccount(owner, 0); - - // Mint Mock ERC20 Tokens to account - mockERC20.mint(address(account), 1 ether); - // Fund the account with some ether - vm.deal(address(account), 1 ether); - - vm.startPrank(owner); - FunctionReference[] memory modularSessionDependency = new FunctionReference[](1); - modularSessionDependency[0] = FunctionReferenceLib.pack( - address(ownerPlugin), uint8(ISingleOwnerPlugin.FunctionId.VALIDATION_OWNER_OR_SELF) - ); - - bytes32 modularSessionKeyManifestHash = keccak256(abi.encode(modularSessionKeyPlugin.pluginManifest())); - - address[] memory tempOwners = new address[](1); - tempOwners[0] = address(tempOwner); - - bytes4[] memory allowedSelectors = new bytes4[](1); - allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; - - uint48[] memory afters = new uint48[](1); - afters[0] = 0; - - uint48[] memory untils = new uint48[](1); - untils[0] = 2; - - bytes memory data = abi.encode(tempOwners, allowedSelectors, afters, untils); - - account.installPlugin({ - plugin: address(modularSessionKeyPlugin), - manifestHash: modularSessionKeyManifestHash, - pluginInstallData: data, - dependencies: modularSessionDependency - }); - - FunctionReference[] memory tokenSessionDependency = new FunctionReference[](1); - tokenSessionDependency[0] = FunctionReferenceLib.pack( - address(modularSessionKeyPlugin), uint8(IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER) - ); - bytes32 tokenSessionKeyManifestHash = keccak256(abi.encode(tokenSessionKeyPlugin.pluginManifest())); - - account.installPlugin({ - plugin: address(tokenSessionKeyPlugin), - manifestHash: tokenSessionKeyManifestHash, - pluginInstallData: "", - dependencies: tokenSessionDependency - }); - vm.stopPrank(); - - vm.startPrank(address(account)); - mockERC20.approve(address(account), 1 ether); - - (uint48 _after, uint48 _until) = modularSessionKeyPlugin.getSessionDuration( - address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR - ); - - assertEq(_after, 0); - assertEq(_until, 2); - vm.stopPrank(); - } - - function test_sessionKey_batch() public { - address tempOwner2 = makeAddr("tempOwner2"); - address tempOwner3 = makeAddr("tempOwner3"); - - address[] memory tempOwners = new address[](2); - tempOwners[0] = tempOwner2; - tempOwners[1] = tempOwner3; - - bytes4[] memory allowedSelectors = new bytes4[](2); - allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; - allowedSelectors[1] = TRANSFERFROM_SESSIONKEY_SELECTOR; - - uint48[] memory afters = new uint48[](2); - afters[0] = 0; - afters[1] = 0; - - uint48[] memory untils = new uint48[](2); - untils[0] = 2; - untils[1] = 2; - - vm.expectEmit(true, true, true, true); - emit SessionKeysAdded(address(account), tempOwners, allowedSelectors, afters, untils); - vm.prank(address(account)); - modularSessionKeyPlugin.addSessionKeyBatch(tempOwners, allowedSelectors, afters, untils); - - vm.prank(tempOwner3); - TokenSessionKeyPlugin(address(account)).transferFromSessionKey( - address(mockERC20), address(account), target, 1 ether - ); - - assertEq(mockERC20.balanceOf(address(account)), 0); - assertEq(mockERC20.balanceOf(target), 1 ether); - - vm.expectEmit(true, true, true, true); - emit SessionKeysRemoved(address(account), tempOwners, allowedSelectors); - vm.prank(address(account)); - modularSessionKeyPlugin.removeSessionKeyBatch(tempOwners, allowedSelectors); - } - - function test_sessionKey_userOp() public { - UserOperation[] memory userOps = new UserOperation[](1); - - (, UserOperation memory userOp) = _constructUserOp(address(mockERC20), address(account), target, 1 ether); - userOps[0] = userOp; - - entryPoint.handleOps(userOps, beneficiary); - - assertEq(mockERC20.balanceOf(address(account)), 0); - assertEq(mockERC20.balanceOf(target), 1 ether); - } - - function test_sessionKey_runtime() public { - vm.prank(address(tempOwner)); - TokenSessionKeyPlugin(address(account)).transferFromSessionKey( - address(mockERC20), address(account), target, 1 ether - ); - - assertEq(mockERC20.balanceOf(address(account)), 0); - assertEq(mockERC20.balanceOf(target), 1 ether); - } - - function test_sessionKey_removeTempOwner() public { - vm.startPrank(address(account)); - - vm.expectEmit(true, true, true, true); - emit SessionKeyRemoved(address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR); - modularSessionKeyPlugin.removeSessionKey(tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR); - - vm.stopPrank(); - - (uint48 _after, uint48 _until) = modularSessionKeyPlugin.getSessionDuration( - address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR - ); - assertEq(_after, 0); - assertEq(_until, 0); - - // Check if tempOwner can still send user operations - vm.startPrank(address(tempOwner)); - - bytes memory revertReason = abi.encodeWithSelector(IModularSessionKeyPlugin.NotAuthorized.selector); - vm.expectRevert( - abi.encodeWithSelector( - UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, - address(modularSessionKeyPlugin), - IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, - revertReason - ) - ); - TokenSessionKeyPlugin(address(account)).transferFromSessionKey( - address(mockERC20), address(account), target, 1 ether - ); - } - - function test_sessionKey_invalidContractFails() public { - address wrongERC20Contract = makeAddr("wrongERC20Contract"); - (bytes32 userOpHash, UserOperation memory userOp) = - _constructUserOp(address(wrongERC20Contract), address(account), target, 1 ether); - - UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = userOp; - - bytes memory revertCallData = abi.encodeWithSelector( - tokenSessionKeyPlugin.TRANSFERFROM_SELECTOR(), address(account), target, 1 ether - ); - bytes memory revertReason = abi.encodeWithSelector( - UpgradeableModularAccount.ExecFromPluginExternalNotPermitted.selector, - address(tokenSessionKeyPlugin), - address(wrongERC20Contract), - 0, - revertCallData - ); - vm.expectEmit(true, true, true, true); - emit UserOperationRevertReason(userOpHash, address(account), 0, revertReason); - - entryPoint.handleOps(userOps, beneficiary); - } - - function test_sessionKey_unregisteredTempOwnerFails() public { - vm.prank(address(maliciousOwner)); - bytes memory revertReason = abi.encodeWithSelector(IModularSessionKeyPlugin.NotAuthorized.selector); - - vm.expectRevert( - abi.encodeWithSelector( - UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, - address(modularSessionKeyPlugin), - IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, - revertReason - ) - ); - TokenSessionKeyPlugin(address(account)).transferFromSessionKey( - address(mockERC20), address(account), target, 1 ether - ); - } - - function test_sessionKey_invalidSessionDurationFails() public { - // Move block.timestamp to 12345 - vm.warp(12345); - - vm.startPrank(address(tempOwner)); - - bytes memory revertReason = - abi.encodeWithSelector(IModularSessionKeyPlugin.WrongTimeRangeForSession.selector); - - vm.expectRevert( - abi.encodeWithSelector( - UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, - address(modularSessionKeyPlugin), - IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, - revertReason - ) - ); - TokenSessionKeyPlugin(address(account)).transferFromSessionKey( - address(mockERC20), address(account), target, 1 ether - ); - } - - function test_sessionKey_uninstallModularSessionKeyPlugin() public { - address[] memory tempOwners = new address[](1); - tempOwners[0] = address(tempOwner); - - bytes4[] memory allowedSelectors = new bytes4[](1); - allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; - - vm.startPrank(owner); - - vm.expectEmit(true, true, true, true); - - emit PluginUninstalled(address(tokenSessionKeyPlugin), true); - account.uninstallPlugin({ - plugin: address(tokenSessionKeyPlugin), - config: bytes(""), - pluginUninstallData: "" - }); - - vm.expectEmit(true, true, true, true); - emit PluginUninstalled(address(modularSessionKeyPlugin), true); - account.uninstallPlugin({ - plugin: address(modularSessionKeyPlugin), - config: bytes(""), - pluginUninstallData: "" - }); - - vm.stopPrank(); - } - - // Internal Function - function _constructUserOp(address targetContract, address from, address to, uint256 amount) - internal - view - returns (bytes32, UserOperation memory) - { - bytes memory userOpCallData = - abi.encodeCall(TokenSessionKeyPlugin.transferFromSessionKey, (targetContract, from, to, amount)); - - UserOperation memory userOp = UserOperation({ - sender: address(account), - nonce: 0, - initCode: "", - callData: userOpCallData, - callGasLimit: CALL_GAS_LIMIT, - verificationGasLimit: VERIFICATION_GAS_LIMIT, - preVerificationGas: 0, - maxFeePerGas: 2, - maxPriorityFeePerGas: 1, - paymasterAndData: "", - signature: "" - }); - - // Generate signature - bytes32 userOpHash = entryPoint.getUserOpHash(userOp); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(tempOwnerKey, userOpHash.toEthSignedMessageHash()); - userOp.signature = abi.encodePacked(r, s, v); - - return (userOpHash, userOp); - } -} +// import {Test} from "forge-std/Test.sol"; + +// import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +// import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; +// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +// import {SingleOwnerPlugin} from "../../../src/plugins/owner/SingleOwnerPlugin.sol"; +// import {ISingleOwnerPlugin} from "../../../src/plugins/owner/ISingleOwnerPlugin.sol"; +// import {ModularSessionKeyPlugin} from "../../../src/samples/plugins/ModularSessionKeyPlugin.sol"; +// import {IModularSessionKeyPlugin} from "../../../src/samples/plugins/interfaces/ISessionKeyPlugin.sol"; +// import {TokenSessionKeyPlugin} from "../../../src/samples/plugins/TokenSessionKeyPlugin.sol"; +// import {ITokenSessionKeyPlugin} from "../../../src/samples/plugins/interfaces/ITokenSessionKeyPlugin.sol"; + +// import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; +// import {MSCAFactoryFixture} from "../../mocks/MSCAFactoryFixture.sol"; +// import {FunctionReference, FunctionReferenceLib} from "../../../src/helpers/FunctionReferenceLib.sol"; +// import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +// import {MockERC20} from "../../mocks/MockERC20.sol"; + +// contract ModularSessionKeyPluginTest is Test { +// using ECDSA for bytes32; + +// SingleOwnerPlugin public ownerPlugin; +// ModularSessionKeyPlugin public modularSessionKeyPlugin; +// TokenSessionKeyPlugin public tokenSessionKeyPlugin; +// EntryPoint public entryPoint; +// MSCAFactoryFixture public factory; +// UpgradeableModularAccount public account; + +// MockERC20 public mockERC20impl; +// MockERC20 public mockERC20; +// address public mockEmptyERC20Addr; + +// address public owner; +// uint256 public ownerKey; + +// address public maliciousOwner; + +// address public tempOwner; +// uint256 public tempOwnerKey; + +// address public target; + +// address payable public beneficiary; + +// uint256 public constant CALL_GAS_LIMIT = 150000; +// uint256 public constant VERIFICATION_GAS_LIMIT = 3600000; + +// bytes4 public constant TRANSFERFROM_SESSIONKEY_SELECTOR = +// ITokenSessionKeyPlugin.transferFromSessionKey.selector; + +// // Event declarations (needed for vm.expectEmit) +// event UserOperationRevertReason( +// bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason +// ); +// event SessionKeyAdded( +// address indexed account, address indexed sessionKey, bytes4 allowedSelector, uint48 _after, uint48 +// _until +// ); +// event SessionKeyRemoved(address indexed account, address indexed sessionKey, bytes4 allowedSelector); +// event SessionKeysAdded( +// address indexed account, address[] sessionKeys, bytes4[] allowedSelectors, uint48[] afters, uint48[] +// untils +// ); +// event SessionKeysRemoved(address indexed account, address[] sessionKeys, bytes4[] allowedSelectors); +// event PluginUninstalled(address indexed plugin, bool indexed onUninstallSuccess); + +// function setUp() public { +// ownerPlugin = new SingleOwnerPlugin(); +// modularSessionKeyPlugin = new ModularSessionKeyPlugin(); +// tokenSessionKeyPlugin = new TokenSessionKeyPlugin(); + +// entryPoint = new EntryPoint(); +// factory = new MSCAFactoryFixture(entryPoint, ownerPlugin); +// mockERC20impl = new MockERC20("Mock", "MCK"); + +// // Etching MockERC20 code into hardcoded address at TokenSessionKeyPlugin +// mockEmptyERC20Addr = tokenSessionKeyPlugin.TARGET_ERC20_CONTRACT(); +// bytes memory code = address(mockERC20impl).code; +// vm.etch(mockEmptyERC20Addr, code); +// mockERC20 = MockERC20(mockEmptyERC20Addr); + +// (owner, ownerKey) = makeAddrAndKey("owner"); +// (maliciousOwner,) = makeAddrAndKey("maliciousOwner"); +// (tempOwner, tempOwnerKey) = makeAddrAndKey("tempOwner"); +// target = makeAddr("target"); + +// beneficiary = payable(makeAddr("beneficiary")); +// vm.deal(beneficiary, 1 wei); +// vm.deal(owner, 10 ether); + +// // Here, SingleOwnerPlugin already installed in factory +// account = factory.createAccount(owner, 0); + +// // Mint Mock ERC20 Tokens to account +// mockERC20.mint(address(account), 1 ether); +// // Fund the account with some ether +// vm.deal(address(account), 1 ether); + +// vm.startPrank(owner); +// FunctionReference[] memory modularSessionDependency = new FunctionReference[](1); +// modularSessionDependency[0] = FunctionReferenceLib.pack( +// address(ownerPlugin), uint8(ISingleOwnerPlugin.FunctionId.VALIDATION_OWNER_OR_SELF) +// ); + +// bytes32 modularSessionKeyManifestHash = keccak256(abi.encode(modularSessionKeyPlugin.pluginManifest())); + +// address[] memory tempOwners = new address[](1); +// tempOwners[0] = address(tempOwner); + +// bytes4[] memory allowedSelectors = new bytes4[](1); +// allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; + +// uint48[] memory afters = new uint48[](1); +// afters[0] = 0; + +// uint48[] memory untils = new uint48[](1); +// untils[0] = 2; + +// bytes memory data = abi.encode(tempOwners, allowedSelectors, afters, untils); + +// account.installPlugin({ +// plugin: address(modularSessionKeyPlugin), +// manifestHash: modularSessionKeyManifestHash, +// pluginInstallData: data, +// dependencies: modularSessionDependency +// }); + +// FunctionReference[] memory tokenSessionDependency = new FunctionReference[](1); +// tokenSessionDependency[0] = FunctionReferenceLib.pack( +// address(modularSessionKeyPlugin), +// uint8(IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER) +// ); +// bytes32 tokenSessionKeyManifestHash = keccak256(abi.encode(tokenSessionKeyPlugin.pluginManifest())); + +// account.installPlugin({ +// plugin: address(tokenSessionKeyPlugin), +// manifestHash: tokenSessionKeyManifestHash, +// pluginInstallData: "", +// dependencies: tokenSessionDependency +// }); +// vm.stopPrank(); + +// vm.startPrank(address(account)); +// mockERC20.approve(address(account), 1 ether); + +// (uint48 _after, uint48 _until) = modularSessionKeyPlugin.getSessionDuration( +// address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR +// ); + +// assertEq(_after, 0); +// assertEq(_until, 2); +// vm.stopPrank(); +// } + +// function test_sessionKey_batch() public { +// address tempOwner2 = makeAddr("tempOwner2"); +// address tempOwner3 = makeAddr("tempOwner3"); + +// address[] memory tempOwners = new address[](2); +// tempOwners[0] = tempOwner2; +// tempOwners[1] = tempOwner3; + +// bytes4[] memory allowedSelectors = new bytes4[](2); +// allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; +// allowedSelectors[1] = TRANSFERFROM_SESSIONKEY_SELECTOR; + +// uint48[] memory afters = new uint48[](2); +// afters[0] = 0; +// afters[1] = 0; + +// uint48[] memory untils = new uint48[](2); +// untils[0] = 2; +// untils[1] = 2; + +// vm.expectEmit(true, true, true, true); +// emit SessionKeysAdded(address(account), tempOwners, allowedSelectors, afters, untils); +// vm.prank(address(account)); +// modularSessionKeyPlugin.addSessionKeyBatch(tempOwners, allowedSelectors, afters, untils); + +// vm.prank(tempOwner3); +// TokenSessionKeyPlugin(address(account)).transferFromSessionKey( +// address(mockERC20), address(account), target, 1 ether +// ); + +// assertEq(mockERC20.balanceOf(address(account)), 0); +// assertEq(mockERC20.balanceOf(target), 1 ether); + +// vm.expectEmit(true, true, true, true); +// emit SessionKeysRemoved(address(account), tempOwners, allowedSelectors); +// vm.prank(address(account)); +// modularSessionKeyPlugin.removeSessionKeyBatch(tempOwners, allowedSelectors); +// } + +// function test_sessionKey_userOp() public { +// UserOperation[] memory userOps = new UserOperation[](1); + +// (, UserOperation memory userOp) = _constructUserOp(address(mockERC20), address(account), target, 1 +// ether); +// userOps[0] = userOp; + +// entryPoint.handleOps(userOps, beneficiary); + +// assertEq(mockERC20.balanceOf(address(account)), 0); +// assertEq(mockERC20.balanceOf(target), 1 ether); +// } + +// function test_sessionKey_runtime() public { +// vm.prank(address(tempOwner)); +// TokenSessionKeyPlugin(address(account)).transferFromSessionKey( +// address(mockERC20), address(account), target, 1 ether +// ); + +// assertEq(mockERC20.balanceOf(address(account)), 0); +// assertEq(mockERC20.balanceOf(target), 1 ether); +// } + +// function test_sessionKey_removeTempOwner() public { +// vm.startPrank(address(account)); + +// vm.expectEmit(true, true, true, true); +// emit SessionKeyRemoved(address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR); +// modularSessionKeyPlugin.removeSessionKey(tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR); + +// vm.stopPrank(); + +// (uint48 _after, uint48 _until) = modularSessionKeyPlugin.getSessionDuration( +// address(account), tempOwner, TRANSFERFROM_SESSIONKEY_SELECTOR +// ); +// assertEq(_after, 0); +// assertEq(_until, 0); + +// // Check if tempOwner can still send user operations +// vm.startPrank(address(tempOwner)); + +// bytes memory revertReason = abi.encodeWithSelector(IModularSessionKeyPlugin.NotAuthorized.selector); +// vm.expectRevert( +// abi.encodeWithSelector( +// UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, +// address(modularSessionKeyPlugin), +// IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, +// revertReason +// ) +// ); +// TokenSessionKeyPlugin(address(account)).transferFromSessionKey( +// address(mockERC20), address(account), target, 1 ether +// ); +// } + +// function test_sessionKey_invalidContractFails() public { +// address wrongERC20Contract = makeAddr("wrongERC20Contract"); +// (bytes32 userOpHash, UserOperation memory userOp) = +// _constructUserOp(address(wrongERC20Contract), address(account), target, 1 ether); + +// UserOperation[] memory userOps = new UserOperation[](1); +// userOps[0] = userOp; + +// bytes memory revertCallData = abi.encodeWithSelector( +// tokenSessionKeyPlugin.TRANSFERFROM_SELECTOR(), address(account), target, 1 ether +// ); +// bytes memory revertReason = abi.encodeWithSelector( +// UpgradeableModularAccount.ExecFromPluginExternalNotPermitted.selector, +// address(tokenSessionKeyPlugin), +// address(wrongERC20Contract), +// 0, +// revertCallData +// ); +// vm.expectEmit(true, true, true, true); +// emit UserOperationRevertReason(userOpHash, address(account), 0, revertReason); + +// entryPoint.handleOps(userOps, beneficiary); +// } + +// function test_sessionKey_unregisteredTempOwnerFails() public { +// vm.prank(address(maliciousOwner)); +// bytes memory revertReason = abi.encodeWithSelector(IModularSessionKeyPlugin.NotAuthorized.selector); + +// vm.expectRevert( +// abi.encodeWithSelector( +// UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, +// address(modularSessionKeyPlugin), +// IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, +// revertReason +// ) +// ); +// TokenSessionKeyPlugin(address(account)).transferFromSessionKey( +// address(mockERC20), address(account), target, 1 ether +// ); +// } + +// function test_sessionKey_invalidSessionDurationFails() public { +// // Move block.timestamp to 12345 +// vm.warp(12345); + +// vm.startPrank(address(tempOwner)); + +// bytes memory revertReason = +// abi.encodeWithSelector(IModularSessionKeyPlugin.WrongTimeRangeForSession.selector); + +// vm.expectRevert( +// abi.encodeWithSelector( +// UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, +// address(modularSessionKeyPlugin), +// IModularSessionKeyPlugin.FunctionId.VALIDATION_TEMPORARY_OWNER, +// revertReason +// ) +// ); +// TokenSessionKeyPlugin(address(account)).transferFromSessionKey( +// address(mockERC20), address(account), target, 1 ether +// ); +// } + +// function test_sessionKey_uninstallModularSessionKeyPlugin() public { +// address[] memory tempOwners = new address[](1); +// tempOwners[0] = address(tempOwner); + +// bytes4[] memory allowedSelectors = new bytes4[](1); +// allowedSelectors[0] = TRANSFERFROM_SESSIONKEY_SELECTOR; + +// vm.startPrank(owner); + +// vm.expectEmit(true, true, true, true); + +// emit PluginUninstalled(address(tokenSessionKeyPlugin), true); +// account.uninstallPlugin({ +// plugin: address(tokenSessionKeyPlugin), +// config: bytes(""), +// pluginUninstallData: "" +// }); + +// vm.expectEmit(true, true, true, true); +// emit PluginUninstalled(address(modularSessionKeyPlugin), true); +// account.uninstallPlugin({ +// plugin: address(modularSessionKeyPlugin), +// config: bytes(""), +// pluginUninstallData: "" +// }); + +// vm.stopPrank(); +// } + +// // Internal Function +// function _constructUserOp(address targetContract, address from, address to, uint256 amount) +// internal +// view +// returns (bytes32, UserOperation memory) +// { +// bytes memory userOpCallData = +// abi.encodeCall(TokenSessionKeyPlugin.transferFromSessionKey, (targetContract, from, to, amount)); + +// UserOperation memory userOp = UserOperation({ +// sender: address(account), +// nonce: 0, +// initCode: "", +// callData: userOpCallData, +// callGasLimit: CALL_GAS_LIMIT, +// verificationGasLimit: VERIFICATION_GAS_LIMIT, +// preVerificationGas: 0, +// maxFeePerGas: 2, +// maxPriorityFeePerGas: 1, +// paymasterAndData: "", +// signature: "" +// }); + +// // Generate signature +// bytes32 userOpHash = entryPoint.getUserOpHash(userOp); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(tempOwnerKey, userOpHash.toEthSignedMessageHash()); +// userOp.signature = abi.encodePacked(r, s, v); + +// return (userOpHash, userOp); +// } +// }