Merge account abstraction work into master (#5274)

Co-authored-by: Ernesto García <ernestognw@gmail.com>
Co-authored-by: Elias Rad <146735585+nnsW3@users.noreply.github.com>
Co-authored-by: cairo <cairoeth@protonmail.com>
Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
This commit is contained in:
Hadrien Croubois
2024-10-23 09:19:13 +02:00
committed by GitHub
parent 2fa4d103fe
commit 28aed34dc5
21 changed files with 2494 additions and 95 deletions

View File

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`Packing`: Add variants for packing `bytes10` and `bytes22`

View File

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`ERC7579Utils`: Add a reusable library to interact with ERC-7579 modular accounts

View File

@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---
`ERC4337Utils`: Add a reusable library to manipulate user operations and interact with ERC-4337 contracts

View File

@ -13,3 +13,4 @@ coverage:
ignore:
- "test"
- "contracts/mocks"
- "contracts/vendor"

View File

@ -133,4 +133,4 @@ jobs:
with:
check_hidden: true
check_filenames: true
skip: package-lock.json,*.pdf
skip: package-lock.json,*.pdf,vendor

View File

@ -0,0 +1,12 @@
= Account
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/account
This directory includes contracts to build accounts for ERC-4337.
== Utilities
{{ERC4337Utils}}
{{ERC7579Utils}}

View File

@ -0,0 +1,150 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IEntryPoint, PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
import {Math} from "../../utils/math/Math.sol";
import {Packing} from "../../utils/Packing.sol";
/**
* @dev Library with common ERC-4337 utility functions.
*
* See https://eips.ethereum.org/EIPS/eip-4337[ERC-4337].
*/
library ERC4337Utils {
using Packing for *;
/// @dev For simulation purposes, validateUserOp (and validatePaymasterUserOp) return this value on success.
uint256 internal constant SIG_VALIDATION_SUCCESS = 0;
/// @dev For simulation purposes, validateUserOp (and validatePaymasterUserOp) must return this value in case of signature failure, instead of revert.
uint256 internal constant SIG_VALIDATION_FAILED = 1;
/// @dev Parses the validation data into its components. See {packValidationData}.
function parseValidationData(
uint256 validationData
) internal pure returns (address aggregator, uint48 validAfter, uint48 validUntil) {
validAfter = uint48(bytes32(validationData).extract_32_6(0x00));
validUntil = uint48(bytes32(validationData).extract_32_6(0x06));
aggregator = address(bytes32(validationData).extract_32_20(0x0c));
if (validUntil == 0) validUntil = type(uint48).max;
}
/// @dev Packs the validation data into a single uint256. See {parseValidationData}.
function packValidationData(
address aggregator,
uint48 validAfter,
uint48 validUntil
) internal pure returns (uint256) {
return uint256(bytes6(validAfter).pack_6_6(bytes6(validUntil)).pack_12_20(bytes20(aggregator)));
}
/// @dev Same as {packValidationData}, but with a boolean signature success flag.
function packValidationData(bool sigSuccess, uint48 validAfter, uint48 validUntil) internal pure returns (uint256) {
return
packValidationData(
address(uint160(Math.ternary(sigSuccess, SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED))),
validAfter,
validUntil
);
}
/**
* @dev Combines two validation data into a single one.
*
* The `aggregator` is set to {SIG_VALIDATION_SUCCESS} if both are successful, while
* the `validAfter` is the maximum and the `validUntil` is the minimum of both.
*/
function combineValidationData(uint256 validationData1, uint256 validationData2) internal pure returns (uint256) {
(address aggregator1, uint48 validAfter1, uint48 validUntil1) = parseValidationData(validationData1);
(address aggregator2, uint48 validAfter2, uint48 validUntil2) = parseValidationData(validationData2);
bool success = aggregator1 == address(0) && aggregator2 == address(0);
uint48 validAfter = uint48(Math.max(validAfter1, validAfter2));
uint48 validUntil = uint48(Math.min(validUntil1, validUntil2));
return packValidationData(success, validAfter, validUntil);
}
/// @dev Returns the aggregator of the `validationData` and whether it is out of time range.
function getValidationData(uint256 validationData) internal view returns (address aggregator, bool outOfTimeRange) {
(address aggregator_, uint48 validAfter, uint48 validUntil) = parseValidationData(validationData);
return (aggregator_, block.timestamp < validAfter || validUntil < block.timestamp);
}
/// @dev Computes the hash of a user operation with the current entrypoint and chainid.
function hash(PackedUserOperation calldata self) internal view returns (bytes32) {
return hash(self, address(this), block.chainid);
}
/// @dev Sames as {hash}, but with a custom entrypoint and chainid.
function hash(
PackedUserOperation calldata self,
address entrypoint,
uint256 chainid
) internal pure returns (bytes32) {
bytes32 result = keccak256(
abi.encode(
keccak256(
abi.encode(
self.sender,
self.nonce,
keccak256(self.initCode),
keccak256(self.callData),
self.accountGasLimits,
self.preVerificationGas,
self.gasFees,
keccak256(self.paymasterAndData)
)
),
entrypoint,
chainid
)
);
return result;
}
/// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
function verificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(self.accountGasLimits.extract_32_16(0x00));
}
/// @dev Returns `accountGasLimits` from the {PackedUserOperation}.
function callGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(self.accountGasLimits.extract_32_16(0x10));
}
/// @dev Returns the first section of `gasFees` from the {PackedUserOperation}.
function maxPriorityFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(self.gasFees.extract_32_16(0x00));
}
/// @dev Returns the second section of `gasFees` from the {PackedUserOperation}.
function maxFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(self.gasFees.extract_32_16(0x10));
}
/// @dev Returns the total gas price for the {PackedUserOperation} (ie. `maxFeePerGas` or `maxPriorityFeePerGas + basefee`).
function gasPrice(PackedUserOperation calldata self) internal view returns (uint256) {
unchecked {
// Following values are "per gas"
uint256 maxPriorityFee = maxPriorityFeePerGas(self);
uint256 maxFee = maxFeePerGas(self);
return Math.ternary(maxFee == maxPriorityFee, maxFee, Math.min(maxFee, maxPriorityFee + block.basefee));
}
}
/// @dev Returns the first section of `paymasterAndData` from the {PackedUserOperation}.
function paymaster(PackedUserOperation calldata self) internal pure returns (address) {
return address(bytes20(self.paymasterAndData[0:20]));
}
/// @dev Returns the second section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterVerificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(bytes16(self.paymasterAndData[20:36]));
}
/// @dev Returns the third section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterPostOpGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(bytes16(self.paymasterAndData[36:52]));
}
}

View File

@ -0,0 +1,242 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Execution} from "../../interfaces/draft-IERC7579.sol";
import {Packing} from "../../utils/Packing.sol";
import {Address} from "../../utils/Address.sol";
type Mode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
/**
* @dev Library with common ERC-7579 utility functions.
*
* See https://eips.ethereum.org/EIPS/eip-7579[ERC-7579].
*/
// slither-disable-next-line unused-state
library ERC7579Utils {
using Packing for *;
/// @dev A single `call` execution.
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
/// @dev A batch of `call` executions.
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
/// @dev A `delegatecall` execution.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
/// @dev Default execution type that reverts on failure.
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
/// @dev Execution type that does not revert on failure.
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
/// @dev Emits when an {EXECTYPE_TRY} execution fails.
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes result);
/// @dev The provided {CallType} is not supported.
error ERC7579UnsupportedCallType(CallType callType);
/// @dev The provided {ExecType} is not supported.
error ERC7579UnsupportedExecType(ExecType execType);
/// @dev The provided module doesn't match the provided module type.
error ERC7579MismatchedModuleTypeId(uint256 moduleTypeId, address module);
/// @dev The module is not installed.
error ERC7579UninstalledModule(uint256 moduleTypeId, address module);
/// @dev The module is already installed.
error ERC7579AlreadyInstalledModule(uint256 moduleTypeId, address module);
/// @dev The module type is not supported.
error ERC7579UnsupportedModuleType(uint256 moduleTypeId);
/// @dev Executes a single call.
function execSingle(
ExecType execType,
bytes calldata executionCalldata
) internal returns (bytes[] memory returnData) {
(address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
returnData = new bytes[](1);
returnData[0] = _call(0, execType, target, value, callData);
}
/// @dev Executes a batch of calls.
function execBatch(
ExecType execType,
bytes calldata executionCalldata
) internal returns (bytes[] memory returnData) {
Execution[] calldata executionBatch = decodeBatch(executionCalldata);
returnData = new bytes[](executionBatch.length);
for (uint256 i = 0; i < executionBatch.length; ++i) {
returnData[i] = _call(
i,
execType,
executionBatch[i].target,
executionBatch[i].value,
executionBatch[i].callData
);
}
}
/// @dev Executes a delegate call.
function execDelegateCall(
ExecType execType,
bytes calldata executionCalldata
) internal returns (bytes[] memory returnData) {
(address target, bytes calldata callData) = decodeDelegate(executionCalldata);
returnData = new bytes[](1);
returnData[0] = _delegatecall(0, execType, target, callData);
}
/// @dev Encodes the mode with the provided parameters. See {decodeMode}.
function encodeMode(
CallType callType,
ExecType execType,
ModeSelector selector,
ModePayload payload
) internal pure returns (Mode mode) {
return
Mode.wrap(
CallType
.unwrap(callType)
.pack_1_1(ExecType.unwrap(execType))
.pack_2_4(bytes4(0))
.pack_6_4(ModeSelector.unwrap(selector))
.pack_10_22(ModePayload.unwrap(payload))
);
}
/// @dev Decodes the mode into its parameters. See {encodeMode}.
function decodeMode(
Mode mode
) internal pure returns (CallType callType, ExecType execType, ModeSelector selector, ModePayload payload) {
return (
CallType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 0)),
ExecType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 1)),
ModeSelector.wrap(Packing.extract_32_4(Mode.unwrap(mode), 6)),
ModePayload.wrap(Packing.extract_32_22(Mode.unwrap(mode), 10))
);
}
/// @dev Encodes a single call execution. See {decodeSingle}.
function encodeSingle(
address target,
uint256 value,
bytes calldata callData
) internal pure returns (bytes memory executionCalldata) {
return abi.encodePacked(target, value, callData);
}
/// @dev Decodes a single call execution. See {encodeSingle}.
function decodeSingle(
bytes calldata executionCalldata
) internal pure returns (address target, uint256 value, bytes calldata callData) {
target = address(bytes20(executionCalldata[0:20]));
value = uint256(bytes32(executionCalldata[20:52]));
callData = executionCalldata[52:];
}
/// @dev Encodes a delegate call execution. See {decodeDelegate}.
function encodeDelegate(
address target,
bytes calldata callData
) internal pure returns (bytes memory executionCalldata) {
return abi.encodePacked(target, callData);
}
/// @dev Decodes a delegate call execution. See {encodeDelegate}.
function decodeDelegate(
bytes calldata executionCalldata
) internal pure returns (address target, bytes calldata callData) {
target = address(bytes20(executionCalldata[0:20]));
callData = executionCalldata[20:];
}
/// @dev Encodes a batch of executions. See {decodeBatch}.
function encodeBatch(Execution[] memory executionBatch) internal pure returns (bytes memory executionCalldata) {
return abi.encode(executionBatch);
}
/// @dev Decodes a batch of executions. See {encodeBatch}.
function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
assembly ("memory-safe") {
let ptr := add(executionCalldata.offset, calldataload(executionCalldata.offset))
// Extract the ERC7579 Executions
executionBatch.offset := add(ptr, 32)
executionBatch.length := calldataload(ptr)
}
}
/// @dev Executes a `call` to the target with the provided {ExecType}.
function _call(
uint256 index,
ExecType execType,
address target,
uint256 value,
bytes calldata data
) private returns (bytes memory) {
(bool success, bytes memory returndata) = target.call{value: value}(data);
return _validateExecutionMode(index, execType, success, returndata);
}
/// @dev Executes a `delegatecall` to the target with the provided {ExecType}.
function _delegatecall(
uint256 index,
ExecType execType,
address target,
bytes calldata data
) private returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return _validateExecutionMode(index, execType, success, returndata);
}
/// @dev Validates the execution mode and returns the returndata.
function _validateExecutionMode(
uint256 index,
ExecType execType,
bool success,
bytes memory returndata
) private returns (bytes memory) {
if (execType == ERC7579Utils.EXECTYPE_DEFAULT) {
Address.verifyCallResult(success, returndata);
} else if (execType == ERC7579Utils.EXECTYPE_TRY) {
if (!success) emit ERC7579TryExecuteFail(index, returndata);
} else {
revert ERC7579UnsupportedExecType(execType);
}
return returndata;
}
}
// Operators
using {eqCallType as ==} for CallType global;
using {eqExecType as ==} for ExecType global;
using {eqModeSelector as ==} for ModeSelector global;
using {eqModePayload as ==} for ModePayload global;
/// @dev Compares two `CallType` values for equality.
function eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
/// @dev Compares two `ExecType` values for equality.
function eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
/// @dev Compares two `ModeSelector` values for equality.
function eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
}
/// @dev Compares two `ModePayload` values for equality.
function eqModePayload(ModePayload a, ModePayload b) pure returns (bool) {
return ModePayload.unwrap(a) == ModePayload.unwrap(b);
}

View File

@ -0,0 +1,215 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev A https://github.com/ethereum/ercs/blob/master/ERCS/erc-4337.md#useroperation[user operation] is composed of the following elements:
* - `sender` (`address`): The account making the operation
* - `nonce` (`uint256`): Anti-replay parameter (see “Semi-abstracted Nonce Support” )
* - `factory` (`address`): account factory, only for new accounts
* - `factoryData` (`bytes`): data for account factory (only if account factory exists)
* - `callData` (`bytes`): The data to pass to the sender during the main execution call
* - `callGasLimit` (`uint256`): The amount of gas to allocate the main execution call
* - `verificationGasLimit` (`uint256`): The amount of gas to allocate for the verification step
* - `preVerificationGas` (`uint256`): Extra gas to pay the bunder
* - `maxFeePerGas` (`uint256`): Maximum fee per gas (similar to EIP-1559 max_fee_per_gas)
* - `maxPriorityFeePerGas` (`uint256`): Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas)
* - `paymaster` (`address`): Address of paymaster contract, (or empty, if account pays for itself)
* - `paymasterVerificationGasLimit` (`uint256`): The amount of gas to allocate for the paymaster validation code
* - `paymasterPostOpGasLimit` (`uint256`): The amount of gas to allocate for the paymaster post-operation code
* - `paymasterData` (`bytes`): Data for paymaster (only if paymaster exists)
* - `signature` (`bytes`): Data passed into the account to verify authorization
*
* When passed to on-chain contacts, the following packed version is used.
* - `sender` (`address`)
* - `nonce` (`uint256`)
* - `initCode` (`bytes`): concatenation of factory address and factoryData (or empty)
* - `callData` (`bytes`)
* - `accountGasLimits` (`bytes32`): concatenation of verificationGas (16 bytes) and callGas (16 bytes)
* - `preVerificationGas` (`uint256`)
* - `gasFees` (`bytes32`): concatenation of maxPriorityFee (16 bytes) and maxFeePerGas (16 bytes)
* - `paymasterAndData` (`bytes`): concatenation of paymaster fields (or empty)
* - `signature` (`bytes`)
*/
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode; // `abi.encodePacked(factory, factoryData)`
bytes callData;
bytes32 accountGasLimits; // `abi.encodePacked(verificationGasLimit, callGasLimit)` 16 bytes each
uint256 preVerificationGas;
bytes32 gasFees; // `abi.encodePacked(maxPriorityFee, maxFeePerGas)` 16 bytes each
bytes paymasterAndData; // `abi.encodePacked(paymaster, paymasterVerificationGasLimit, paymasterPostOpGasLimit, paymasterData)`
bytes signature;
}
/**
* @dev Aggregates and validates multiple signatures for a batch of user operations.
*/
interface IAggregator {
/**
* @dev Validates the signature for a user operation.
*/
function validateUserOpSignature(
PackedUserOperation calldata userOp
) external view returns (bytes memory sigForUserOp);
/**
* @dev Returns an aggregated signature for a batch of user operation's signatures.
*/
function aggregateSignatures(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatesSignature);
/**
* @dev Validates that the aggregated signature is valid for the user operations.
*
* Requirements:
*
* - The aggregated signature MUST match the given list of operations.
*/
function validateSignatures(PackedUserOperation[] calldata userOps, bytes calldata signature) external view;
}
/**
* @dev Handle nonce management for accounts.
*/
interface IEntryPointNonces {
/**
* @dev Returns the nonce for a `sender` account and a `key`.
*
* Nonces for a certain `key` are always increasing.
*/
function getNonce(address sender, uint192 key) external view returns (uint256 nonce);
}
/**
* @dev Handle stake management for accounts.
*/
interface IEntryPointStake {
/**
* @dev Returns the balance of the account.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Deposits `msg.value` to the account.
*/
function depositTo(address account) external payable;
/**
* @dev Withdraws `withdrawAmount` from the account to `withdrawAddress`.
*/
function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
/**
* @dev Adds stake to the account with an unstake delay of `unstakeDelaySec`.
*/
function addStake(uint32 unstakeDelaySec) external payable;
/**
* @dev Unlocks the stake of the account.
*/
function unlockStake() external;
/**
* @dev Withdraws the stake of the account to `withdrawAddress`.
*/
function withdrawStake(address payable withdrawAddress) external;
}
/**
* @dev Entry point for user operations.
*/
interface IEntryPoint is IEntryPointNonces, IEntryPointStake {
/**
* @dev A user operation at `opIndex` failed with `reason`.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* @dev A user operation at `opIndex` failed with `reason` and `inner` returned data.
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
/**
* @dev Batch of aggregated user operations per aggregator.
*/
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
IAggregator aggregator;
bytes signature;
}
/**
* @dev Executes a batch of user operations.
*/
function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external;
/**
* @dev Executes a batch of aggregated user operations per aggregator.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
}
/**
* @dev Base interface for an account.
*/
interface IAccount {
/**
* @dev Validates a user operation.
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}
/**
* @dev Support for executing user operations by prepending the {executeUserOp} function selector
* to the UserOperation's `callData`.
*/
interface IAccountExecute {
/**
* @dev Executes a user operation.
*/
function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external;
}
/**
* @dev Interface for a paymaster contract that agrees to pay for the gas costs of a user operation.
*
* NOTE: A paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
*/
interface IPaymaster {
enum PostOpMode {
opSucceeded,
opReverted,
postOpReverted
}
/**
* @dev Validates whether the paymaster is willing to pay for the user operation.
*
* NOTE: Bundlers will reject this method if it modifies the state, unless it's whitelisted.
*/
function validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData);
/**
* @dev Verifies the sender is the entrypoint.
*/
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) external;
}

View File

@ -0,0 +1,196 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PackedUserOperation} from "./draft-IERC4337.sol";
uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
interface IERC7579Module {
/**
* @dev This function is called by the smart account during installation of the module
* @param data arbitrary data that may be required on the module during `onInstall` initialization
*
* MUST revert on error (e.g. if module is already enabled)
*/
function onInstall(bytes calldata data) external;
/**
* @dev This function is called by the smart account during uninstallation of the module
* @param data arbitrary data that may be required on the module during `onUninstall` de-initialization
*
* MUST revert on error
*/
function onUninstall(bytes calldata data) external;
/**
* @dev Returns boolean value if module is a certain type
* @param moduleTypeId the module type ID according the ERC-7579 spec
*
* MUST return true if the module is of the given type and false otherwise
*/
function isModuleType(uint256 moduleTypeId) external view returns (bool);
}
interface IERC7579Validator is IERC7579Module {
/**
* @dev Validates a UserOperation
* @param userOp the ERC-4337 PackedUserOperation
* @param userOpHash the hash of the ERC-4337 PackedUserOperation
*
* MUST validate that the signature is a valid signature of the userOpHash
* SHOULD return ERC-4337's SIG_VALIDATION_FAILED (and not revert) on signature mismatch
*/
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256);
/**
* @dev Validates a signature using ERC-1271
* @param sender the address that sent the ERC-1271 request to the smart account
* @param hash the hash of the ERC-1271 request
* @param signature the signature of the ERC-1271 request
*
* MUST return the ERC-1271 `MAGIC_VALUE` if the signature is valid
* MUST NOT modify state
*/
function isValidSignatureWithSender(
address sender,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4);
}
interface IERC7579Hook is IERC7579Module {
/**
* @dev Called by the smart account before execution
* @param msgSender the address that called the smart account
* @param value the value that was sent to the smart account
* @param msgData the data that was sent to the smart account
*
* MAY return arbitrary data in the `hookData` return value
*/
function preCheck(
address msgSender,
uint256 value,
bytes calldata msgData
) external returns (bytes memory hookData);
/**
* @dev Called by the smart account after execution
* @param hookData the data that was returned by the `preCheck` function
*
* MAY validate the `hookData` to validate transaction context of the `preCheck` function
*/
function postCheck(bytes calldata hookData) external;
}
struct Execution {
address target;
uint256 value;
bytes callData;
}
interface IERC7579Execution {
/**
* @dev Executes a transaction on behalf of the account.
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*
* MUST ensure adequate authorization control: e.g. onlyEntryPointOrSelf if used with ERC-4337
* If a mode is requested that is not supported by the Account, it MUST revert
*/
function execute(bytes32 mode, bytes calldata executionCalldata) external;
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by Executor Modules
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*
* MUST ensure adequate authorization control: i.e. onlyExecutorModule
* If a mode is requested that is not supported by the Account, it MUST revert
*/
function executeFromExecutor(
bytes32 mode,
bytes calldata executionCalldata
) external returns (bytes[] memory returnData);
}
interface IERC7579AccountConfig {
/**
* @dev Returns the account id of the smart account
* @return accountImplementationId the account id of the smart account
*
* MUST return a non-empty string
* The accountId SHOULD be structured like so:
* "vendorname.accountname.semver"
* The id SHOULD be unique across all smart accounts
*/
function accountId() external view returns (string memory accountImplementationId);
/**
* @dev Function to check if the account supports a certain execution mode (see above)
* @param encodedMode the encoded mode
*
* MUST return true if the account supports the mode and false otherwise
*/
function supportsExecutionMode(bytes32 encodedMode) external view returns (bool);
/**
* @dev Function to check if the account supports a certain module typeId
* @param moduleTypeId the module type ID according to the ERC-7579 spec
*
* MUST return true if the account supports the module type and false otherwise
*/
function supportsModule(uint256 moduleTypeId) external view returns (bool);
}
interface IERC7579ModuleConfig {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
/**
* @dev Installs a Module of a certain type on the smart account
* @param moduleTypeId the module type ID according to the ERC-7579 spec
* @param module the module address
* @param initData arbitrary data that may be required on the module during `onInstall`
* initialization.
*
* MUST implement authorization control
* MUST call `onInstall` on the module with the `initData` parameter if provided
* MUST emit ModuleInstalled event
* MUST revert if the module is already installed or the initialization on the module failed
*/
function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external;
/**
* @dev Uninstalls a Module of a certain type on the smart account
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param deInitData arbitrary data that may be required on the module during `onInstall`
* initialization.
*
* MUST implement authorization control
* MUST call `onUninstall` on the module with the `deInitData` parameter if provided
* MUST emit ModuleUninstalled event
* MUST revert if the module is not installed or the deInitialization on the module failed
*/
function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external;
/**
* @dev Returns whether a module is installed on the smart account
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param additionalContext arbitrary data that may be required to determine if the module is installed
*
* MUST return true if the module is installed and false otherwise
*/
function isModuleInstalled(
uint256 moduleTypeId,
address module,
bytes calldata additionalContext
) external view returns (bool);
}

View File

@ -25,6 +25,8 @@ import {ERC165} from "../utils/introspection/ERC165.sol";
import {ERC165Checker} from "../utils/introspection/ERC165Checker.sol";
import {ERC1967Utils} from "../proxy/ERC1967/ERC1967Utils.sol";
import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
import {ERC4337Utils} from "../account/utils/draft-ERC4337Utils.sol";
import {ERC7579Utils} from "../account/utils/draft-ERC7579Utils.sol";
import {Heap} from "../utils/structs/Heap.sol";
import {Math} from "../utils/math/Math.sol";
import {MerkleProof} from "../utils/cryptography/MerkleProof.sol";

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {CallType, ExecType, ModeSelector, ModePayload} from "../../../account/utils/draft-ERC7579Utils.sol";
contract ERC7579UtilsGlobalMock {
function eqCallTypeGlobal(CallType callType1, CallType callType2) internal pure returns (bool) {
return callType1 == callType2;
}
function eqExecTypeGlobal(ExecType execType1, ExecType execType2) internal pure returns (bool) {
return execType1 == execType2;
}
function eqModeSelectorGlobal(ModeSelector modeSelector1, ModeSelector modeSelector2) internal pure returns (bool) {
return modeSelector1 == modeSelector2;
}
function eqModePayloadGlobal(ModePayload modePayload1, ModePayload modePayload2) internal pure returns (bool) {
return modePayload1 == modePayload2;
}
}

View File

@ -68,6 +68,38 @@ library Packing {
}
}
function pack_2_8(bytes2 left, bytes8 right) internal pure returns (bytes10 result) {
assembly ("memory-safe") {
left := and(left, shl(240, not(0)))
right := and(right, shl(192, not(0)))
result := or(left, shr(16, right))
}
}
function pack_2_10(bytes2 left, bytes10 right) internal pure returns (bytes12 result) {
assembly ("memory-safe") {
left := and(left, shl(240, not(0)))
right := and(right, shl(176, not(0)))
result := or(left, shr(16, right))
}
}
function pack_2_20(bytes2 left, bytes20 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(240, not(0)))
right := and(right, shl(96, not(0)))
result := or(left, shr(16, right))
}
}
function pack_2_22(bytes2 left, bytes22 right) internal pure returns (bytes24 result) {
assembly ("memory-safe") {
left := and(left, shl(240, not(0)))
right := and(right, shl(80, not(0)))
result := or(left, shr(16, right))
}
}
function pack_4_2(bytes4 left, bytes2 right) internal pure returns (bytes6 result) {
assembly ("memory-safe") {
left := and(left, shl(224, not(0)))
@ -84,6 +116,14 @@ library Packing {
}
}
function pack_4_6(bytes4 left, bytes6 right) internal pure returns (bytes10 result) {
assembly ("memory-safe") {
left := and(left, shl(224, not(0)))
right := and(right, shl(208, not(0)))
result := or(left, shr(32, right))
}
}
function pack_4_8(bytes4 left, bytes8 right) internal pure returns (bytes12 result) {
assembly ("memory-safe") {
left := and(left, shl(224, not(0)))
@ -140,6 +180,14 @@ library Packing {
}
}
function pack_6_4(bytes6 left, bytes4 right) internal pure returns (bytes10 result) {
assembly ("memory-safe") {
left := and(left, shl(208, not(0)))
right := and(right, shl(224, not(0)))
result := or(left, shr(48, right))
}
}
function pack_6_6(bytes6 left, bytes6 right) internal pure returns (bytes12 result) {
assembly ("memory-safe") {
left := and(left, shl(208, not(0)))
@ -148,6 +196,38 @@ library Packing {
}
}
function pack_6_10(bytes6 left, bytes10 right) internal pure returns (bytes16 result) {
assembly ("memory-safe") {
left := and(left, shl(208, not(0)))
right := and(right, shl(176, not(0)))
result := or(left, shr(48, right))
}
}
function pack_6_16(bytes6 left, bytes16 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(208, not(0)))
right := and(right, shl(128, not(0)))
result := or(left, shr(48, right))
}
}
function pack_6_22(bytes6 left, bytes22 right) internal pure returns (bytes28 result) {
assembly ("memory-safe") {
left := and(left, shl(208, not(0)))
right := and(right, shl(80, not(0)))
result := or(left, shr(48, right))
}
}
function pack_8_2(bytes8 left, bytes2 right) internal pure returns (bytes10 result) {
assembly ("memory-safe") {
left := and(left, shl(192, not(0)))
right := and(right, shl(240, not(0)))
result := or(left, shr(64, right))
}
}
function pack_8_4(bytes8 left, bytes4 right) internal pure returns (bytes12 result) {
assembly ("memory-safe") {
left := and(left, shl(192, not(0)))
@ -196,6 +276,46 @@ library Packing {
}
}
function pack_10_2(bytes10 left, bytes2 right) internal pure returns (bytes12 result) {
assembly ("memory-safe") {
left := and(left, shl(176, not(0)))
right := and(right, shl(240, not(0)))
result := or(left, shr(80, right))
}
}
function pack_10_6(bytes10 left, bytes6 right) internal pure returns (bytes16 result) {
assembly ("memory-safe") {
left := and(left, shl(176, not(0)))
right := and(right, shl(208, not(0)))
result := or(left, shr(80, right))
}
}
function pack_10_10(bytes10 left, bytes10 right) internal pure returns (bytes20 result) {
assembly ("memory-safe") {
left := and(left, shl(176, not(0)))
right := and(right, shl(176, not(0)))
result := or(left, shr(80, right))
}
}
function pack_10_12(bytes10 left, bytes12 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(176, not(0)))
right := and(right, shl(160, not(0)))
result := or(left, shr(80, right))
}
}
function pack_10_22(bytes10 left, bytes22 right) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
left := and(left, shl(176, not(0)))
right := and(right, shl(80, not(0)))
result := or(left, shr(80, right))
}
}
function pack_12_4(bytes12 left, bytes4 right) internal pure returns (bytes16 result) {
assembly ("memory-safe") {
left := and(left, shl(160, not(0)))
@ -212,6 +332,14 @@ library Packing {
}
}
function pack_12_10(bytes12 left, bytes10 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(160, not(0)))
right := and(right, shl(176, not(0)))
result := or(left, shr(96, right))
}
}
function pack_12_12(bytes12 left, bytes12 right) internal pure returns (bytes24 result) {
assembly ("memory-safe") {
left := and(left, shl(160, not(0)))
@ -244,6 +372,14 @@ library Packing {
}
}
function pack_16_6(bytes16 left, bytes6 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(128, not(0)))
right := and(right, shl(208, not(0)))
result := or(left, shr(128, right))
}
}
function pack_16_8(bytes16 left, bytes8 right) internal pure returns (bytes24 result) {
assembly ("memory-safe") {
left := and(left, shl(128, not(0)))
@ -268,6 +404,14 @@ library Packing {
}
}
function pack_20_2(bytes20 left, bytes2 right) internal pure returns (bytes22 result) {
assembly ("memory-safe") {
left := and(left, shl(96, not(0)))
right := and(right, shl(240, not(0)))
result := or(left, shr(160, right))
}
}
function pack_20_4(bytes20 left, bytes4 right) internal pure returns (bytes24 result) {
assembly ("memory-safe") {
left := and(left, shl(96, not(0)))
@ -292,6 +436,30 @@ library Packing {
}
}
function pack_22_2(bytes22 left, bytes2 right) internal pure returns (bytes24 result) {
assembly ("memory-safe") {
left := and(left, shl(80, not(0)))
right := and(right, shl(240, not(0)))
result := or(left, shr(176, right))
}
}
function pack_22_6(bytes22 left, bytes6 right) internal pure returns (bytes28 result) {
assembly ("memory-safe") {
left := and(left, shl(80, not(0)))
right := and(right, shl(208, not(0)))
result := or(left, shr(176, right))
}
}
function pack_22_10(bytes22 left, bytes10 right) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
left := and(left, shl(80, not(0)))
right := and(right, shl(176, not(0)))
result := or(left, shr(176, right))
}
}
function pack_24_4(bytes24 left, bytes4 right) internal pure returns (bytes28 result) {
assembly ("memory-safe") {
left := and(left, shl(64, not(0)))
@ -466,6 +634,81 @@ library Packing {
}
}
function extract_10_1(bytes10 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 9) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(248, not(0)))
}
}
function replace_10_1(bytes10 self, bytes1 value, uint8 offset) internal pure returns (bytes10 result) {
bytes1 oldValue = extract_10_1(self, offset);
assembly ("memory-safe") {
value := and(value, shl(248, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_10_2(bytes10 self, uint8 offset) internal pure returns (bytes2 result) {
if (offset > 8) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(240, not(0)))
}
}
function replace_10_2(bytes10 self, bytes2 value, uint8 offset) internal pure returns (bytes10 result) {
bytes2 oldValue = extract_10_2(self, offset);
assembly ("memory-safe") {
value := and(value, shl(240, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_10_4(bytes10 self, uint8 offset) internal pure returns (bytes4 result) {
if (offset > 6) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(224, not(0)))
}
}
function replace_10_4(bytes10 self, bytes4 value, uint8 offset) internal pure returns (bytes10 result) {
bytes4 oldValue = extract_10_4(self, offset);
assembly ("memory-safe") {
value := and(value, shl(224, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_10_6(bytes10 self, uint8 offset) internal pure returns (bytes6 result) {
if (offset > 4) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(208, not(0)))
}
}
function replace_10_6(bytes10 self, bytes6 value, uint8 offset) internal pure returns (bytes10 result) {
bytes6 oldValue = extract_10_6(self, offset);
assembly ("memory-safe") {
value := and(value, shl(208, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_10_8(bytes10 self, uint8 offset) internal pure returns (bytes8 result) {
if (offset > 2) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(192, not(0)))
}
}
function replace_10_8(bytes10 self, bytes8 value, uint8 offset) internal pure returns (bytes10 result) {
bytes8 oldValue = extract_10_8(self, offset);
assembly ("memory-safe") {
value := and(value, shl(192, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_12_1(bytes12 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 11) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -541,6 +784,21 @@ library Packing {
}
}
function extract_12_10(bytes12 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 2) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_12_10(bytes12 self, bytes10 value, uint8 offset) internal pure returns (bytes12 result) {
bytes10 oldValue = extract_12_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_16_1(bytes16 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 15) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -616,6 +874,21 @@ library Packing {
}
}
function extract_16_10(bytes16 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 6) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_16_10(bytes16 self, bytes10 value, uint8 offset) internal pure returns (bytes16 result) {
bytes10 oldValue = extract_16_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_16_12(bytes16 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 4) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -706,6 +979,21 @@ library Packing {
}
}
function extract_20_10(bytes20 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 10) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_20_10(bytes20 self, bytes10 value, uint8 offset) internal pure returns (bytes20 result) {
bytes10 oldValue = extract_20_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_20_12(bytes20 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 8) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -736,6 +1024,141 @@ library Packing {
}
}
function extract_22_1(bytes22 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 21) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(248, not(0)))
}
}
function replace_22_1(bytes22 self, bytes1 value, uint8 offset) internal pure returns (bytes22 result) {
bytes1 oldValue = extract_22_1(self, offset);
assembly ("memory-safe") {
value := and(value, shl(248, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_2(bytes22 self, uint8 offset) internal pure returns (bytes2 result) {
if (offset > 20) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(240, not(0)))
}
}
function replace_22_2(bytes22 self, bytes2 value, uint8 offset) internal pure returns (bytes22 result) {
bytes2 oldValue = extract_22_2(self, offset);
assembly ("memory-safe") {
value := and(value, shl(240, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_4(bytes22 self, uint8 offset) internal pure returns (bytes4 result) {
if (offset > 18) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(224, not(0)))
}
}
function replace_22_4(bytes22 self, bytes4 value, uint8 offset) internal pure returns (bytes22 result) {
bytes4 oldValue = extract_22_4(self, offset);
assembly ("memory-safe") {
value := and(value, shl(224, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_6(bytes22 self, uint8 offset) internal pure returns (bytes6 result) {
if (offset > 16) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(208, not(0)))
}
}
function replace_22_6(bytes22 self, bytes6 value, uint8 offset) internal pure returns (bytes22 result) {
bytes6 oldValue = extract_22_6(self, offset);
assembly ("memory-safe") {
value := and(value, shl(208, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_8(bytes22 self, uint8 offset) internal pure returns (bytes8 result) {
if (offset > 14) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(192, not(0)))
}
}
function replace_22_8(bytes22 self, bytes8 value, uint8 offset) internal pure returns (bytes22 result) {
bytes8 oldValue = extract_22_8(self, offset);
assembly ("memory-safe") {
value := and(value, shl(192, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_10(bytes22 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 12) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_22_10(bytes22 self, bytes10 value, uint8 offset) internal pure returns (bytes22 result) {
bytes10 oldValue = extract_22_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_12(bytes22 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 10) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(160, not(0)))
}
}
function replace_22_12(bytes22 self, bytes12 value, uint8 offset) internal pure returns (bytes22 result) {
bytes12 oldValue = extract_22_12(self, offset);
assembly ("memory-safe") {
value := and(value, shl(160, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_16(bytes22 self, uint8 offset) internal pure returns (bytes16 result) {
if (offset > 6) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(128, not(0)))
}
}
function replace_22_16(bytes22 self, bytes16 value, uint8 offset) internal pure returns (bytes22 result) {
bytes16 oldValue = extract_22_16(self, offset);
assembly ("memory-safe") {
value := and(value, shl(128, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_22_20(bytes22 self, uint8 offset) internal pure returns (bytes20 result) {
if (offset > 2) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(96, not(0)))
}
}
function replace_22_20(bytes22 self, bytes20 value, uint8 offset) internal pure returns (bytes22 result) {
bytes20 oldValue = extract_22_20(self, offset);
assembly ("memory-safe") {
value := and(value, shl(96, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_24_1(bytes24 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 23) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -811,6 +1234,21 @@ library Packing {
}
}
function extract_24_10(bytes24 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 14) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_24_10(bytes24 self, bytes10 value, uint8 offset) internal pure returns (bytes24 result) {
bytes10 oldValue = extract_24_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_24_12(bytes24 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 12) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -856,6 +1294,21 @@ library Packing {
}
}
function extract_24_22(bytes24 self, uint8 offset) internal pure returns (bytes22 result) {
if (offset > 2) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(80, not(0)))
}
}
function replace_24_22(bytes24 self, bytes22 value, uint8 offset) internal pure returns (bytes24 result) {
bytes22 oldValue = extract_24_22(self, offset);
assembly ("memory-safe") {
value := and(value, shl(80, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_28_1(bytes28 self, uint8 offset) internal pure returns (bytes1 result) {
if (offset > 27) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -931,6 +1384,21 @@ library Packing {
}
}
function extract_28_10(bytes28 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 18) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_28_10(bytes28 self, bytes10 value, uint8 offset) internal pure returns (bytes28 result) {
bytes10 oldValue = extract_28_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_28_12(bytes28 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 16) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -976,6 +1444,21 @@ library Packing {
}
}
function extract_28_22(bytes28 self, uint8 offset) internal pure returns (bytes22 result) {
if (offset > 6) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(80, not(0)))
}
}
function replace_28_22(bytes28 self, bytes22 value, uint8 offset) internal pure returns (bytes28 result) {
bytes22 oldValue = extract_28_22(self, offset);
assembly ("memory-safe") {
value := and(value, shl(80, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_28_24(bytes28 self, uint8 offset) internal pure returns (bytes24 result) {
if (offset > 4) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -1066,6 +1549,21 @@ library Packing {
}
}
function extract_32_10(bytes32 self, uint8 offset) internal pure returns (bytes10 result) {
if (offset > 22) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(176, not(0)))
}
}
function replace_32_10(bytes32 self, bytes10 value, uint8 offset) internal pure returns (bytes32 result) {
bytes10 oldValue = extract_32_10(self, offset);
assembly ("memory-safe") {
value := and(value, shl(176, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_32_12(bytes32 self, uint8 offset) internal pure returns (bytes12 result) {
if (offset > 20) revert OutOfRangeAccess();
assembly ("memory-safe") {
@ -1111,6 +1609,21 @@ library Packing {
}
}
function extract_32_22(bytes32 self, uint8 offset) internal pure returns (bytes22 result) {
if (offset > 10) revert OutOfRangeAccess();
assembly ("memory-safe") {
result := and(shl(mul(8, offset), self), shl(80, not(0)))
}
}
function replace_32_22(bytes32 self, bytes22 value, uint8 offset) internal pure returns (bytes32 result) {
bytes22 oldValue = extract_32_22(self, offset);
assembly ("memory-safe") {
value := and(value, shl(80, not(0)))
result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
}
}
function extract_32_24(bytes32 self, uint8 offset) internal pure returns (bytes24 result) {
if (offset > 8) revert OutOfRangeAccess();
assembly ("memory-safe") {

View File

@ -1,3 +1,3 @@
module.exports = {
SIZES: [1, 2, 4, 6, 8, 12, 16, 20, 24, 28, 32],
SIZES: [1, 2, 4, 6, 8, 10, 12, 16, 20, 22, 24, 28, 32],
};

View File

@ -11,14 +11,14 @@ import {Packing} from "@openzeppelin/contracts/utils/Packing.sol";
`;
const testPack = (left, right) => `\
function testPack(bytes${left} left, bytes${right} right) external {
function testPack(bytes${left} left, bytes${right} right) external pure {
assertEq(left, Packing.pack_${left}_${right}(left, right).extract_${left + right}_${left}(0));
assertEq(right, Packing.pack_${left}_${right}(left, right).extract_${left + right}_${right}(${left}));
}
`;
const testReplace = (outer, inner) => `\
function testReplace(bytes${outer} container, bytes${inner} newValue, uint8 offset) external {
function testReplace(bytes${outer} container, bytes${inner} newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, ${outer - inner}));
bytes${inner} oldValue = container.extract_${outer}_${inner}(offset);

View File

@ -1,5 +1,5 @@
{
"detectors_to_run": "arbitrary-send-erc20,array-by-reference,incorrect-shift,name-reused,rtlo,suicidal,uninitialized-state,uninitialized-storage,arbitrary-send-erc20-permit,controlled-array-length,controlled-delegatecall,delegatecall-loop,msg-value-loop,reentrancy-eth,unchecked-transfer,weak-prng,domain-separator-collision,erc20-interface,erc721-interface,locked-ether,mapping-deletion,shadowing-abstract,tautology,write-after-write,boolean-cst,reentrancy-no-eth,reused-constructor,tx-origin,unchecked-lowlevel,unchecked-send,variable-scope,void-cst,events-access,events-maths,incorrect-unary,boolean-equal,cyclomatic-complexity,deprecated-standards,erc20-indexed,function-init-state,pragma,unused-state,reentrancy-unlimited-gas,constable-states,immutable-states,var-read-using-this",
"filter_paths": "contracts/mocks,contracts-exposed",
"filter_paths": "contracts/mocks,contracts/vendor,contracts-exposed",
"compile_force_framework": "hardhat"
}

View File

@ -0,0 +1,211 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { packValidationData, packPaymasterData, UserOperation } = require('../../helpers/erc4337');
const { MAX_UINT48 } = require('../../helpers/constants');
const fixture = async () => {
const [authorizer, sender, entrypoint, paymaster] = await ethers.getSigners();
const utils = await ethers.deployContract('$ERC4337Utils');
return { utils, authorizer, sender, entrypoint, paymaster };
};
describe('ERC4337Utils', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('parseValidationData', function () {
it('parses the validation data', async function () {
const authorizer = this.authorizer;
const validUntil = 0x12345678n;
const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, authorizer);
expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([
authorizer.address,
validAfter,
validUntil,
]);
});
it('returns an type(uint48).max if until is 0', async function () {
const authorizer = this.authorizer;
const validAfter = 0x12345678n;
const validationData = packValidationData(validAfter, 0, authorizer);
expect(this.utils.$parseValidationData(validationData)).to.eventually.deep.equal([
authorizer.address,
validAfter,
MAX_UINT48,
]);
});
});
describe('packValidationData', function () {
it('packs the validation data', async function () {
const authorizer = this.authorizer;
const validUntil = 0x12345678n;
const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, authorizer);
expect(
this.utils.$packValidationData(ethers.Typed.address(authorizer), validAfter, validUntil),
).to.eventually.equal(validationData);
});
it('packs the validation data (bool)', async function () {
const success = false;
const validUntil = 0x12345678n;
const validAfter = 0x9abcdef0n;
const validationData = packValidationData(validAfter, validUntil, false);
expect(this.utils.$packValidationData(ethers.Typed.bool(success), validAfter, validUntil)).to.eventually.equal(
validationData,
);
});
});
describe('combineValidationData', function () {
const validUntil1 = 0x12345678n;
const validAfter1 = 0x9abcdef0n;
const validUntil2 = 0x87654321n;
const validAfter2 = 0xabcdef90n;
it('combines the validation data', async function () {
const validationData1 = packValidationData(validAfter1, validUntil1, ethers.ZeroAddress);
const validationData2 = packValidationData(validAfter2, validUntil2, ethers.ZeroAddress);
const expected = packValidationData(validAfter2, validUntil1, true);
// check symmetry
expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected);
expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected);
});
for (const [authorizer1, authorizer2] of [
[ethers.ZeroAddress, '0xbf023313b891fd6000544b79e353323aa94a4f29'],
['0xbf023313b891fd6000544b79e353323aa94a4f29', ethers.ZeroAddress],
]) {
it('returns SIG_VALIDATION_FAILURE if one of the authorizers is not address(0)', async function () {
const validationData1 = packValidationData(validAfter1, validUntil1, authorizer1);
const validationData2 = packValidationData(validAfter2, validUntil2, authorizer2);
const expected = packValidationData(validAfter2, validUntil1, false);
// check symmetry
expect(this.utils.$combineValidationData(validationData1, validationData2)).to.eventually.equal(expected);
expect(this.utils.$combineValidationData(validationData2, validationData1)).to.eventually.equal(expected);
});
}
});
describe('getValidationData', function () {
it('returns the validation data with valid validity range', async function () {
const aggregator = this.authorizer;
const validAfter = 0;
const validUntil = MAX_UINT48;
const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, false]);
});
it('returns the validation data with invalid validity range (expired)', async function () {
const aggregator = this.authorizer;
const validAfter = 0;
const validUntil = 1;
const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]);
});
it('returns the validation data with invalid validity range (not yet valid)', async function () {
const aggregator = this.authorizer;
const validAfter = MAX_UINT48;
const validUntil = MAX_UINT48;
const validationData = packValidationData(validAfter, validUntil, aggregator);
expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([aggregator.address, true]);
});
it('returns address(0) and false for validationData = 0', function () {
expect(this.utils.$getValidationData(0n)).to.eventually.deep.equal([ethers.ZeroAddress, false]);
});
});
describe('hash', function () {
it('returns the user operation hash', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1 });
const chainId = await ethers.provider.getNetwork().then(({ chainId }) => chainId);
expect(this.utils.$hash(userOp.packed)).to.eventually.equal(userOp.hash(this.utils.target, chainId));
});
it('returns the operation hash with specified entrypoint and chainId', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1 });
const chainId = 0xdeadbeef;
expect(this.utils.$hash(userOp.packed, this.entrypoint, chainId)).to.eventually.equal(
userOp.hash(this.entrypoint, chainId),
);
});
});
describe('userOp values', function () {
it('returns verificationGasLimit', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, verificationGas: 0x12345678n });
expect(this.utils.$verificationGasLimit(userOp.packed)).to.eventually.equal(userOp.verificationGas);
});
it('returns callGasLimit', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, callGas: 0x12345678n });
expect(this.utils.$callGasLimit(userOp.packed)).to.eventually.equal(userOp.callGas);
});
it('returns maxPriorityFeePerGas', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxPriorityFee: 0x12345678n });
expect(this.utils.$maxPriorityFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee);
});
it('returns maxFeePerGas', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, maxFeePerGas: 0x12345678n });
expect(this.utils.$maxFeePerGas(userOp.packed)).to.eventually.equal(userOp.maxFeePerGas);
});
it('returns gasPrice', async function () {
const userOp = new UserOperation({
sender: this.sender,
nonce: 1,
maxPriorityFee: 0x12345678n,
maxFeePerGas: 0x87654321n,
});
expect(this.utils.$gasPrice(userOp.packed)).to.eventually.equal(userOp.maxPriorityFee);
});
describe('paymasterAndData', function () {
beforeEach(async function () {
this.verificationGasLimit = 0x12345678n;
this.postOpGasLimit = 0x87654321n;
this.paymasterAndData = packPaymasterData(this.paymaster, this.verificationGasLimit, this.postOpGasLimit);
this.userOp = new UserOperation({
sender: this.sender,
nonce: 1,
paymasterAndData: this.paymasterAndData,
});
});
it('returns paymaster', async function () {
expect(this.utils.$paymaster(this.userOp.packed)).to.eventually.equal(this.paymaster);
});
it('returns verificationGasLimit', async function () {
expect(this.utils.$paymasterVerificationGasLimit(this.userOp.packed)).to.eventually.equal(
this.verificationGasLimit,
);
});
it('returns postOpGasLimit', async function () {
expect(this.utils.$paymasterPostOpGasLimit(this.userOp.packed)).to.eventually.equal(this.postOpGasLimit);
});
});
});
});

View File

@ -0,0 +1,354 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture, setBalance } = require('@nomicfoundation/hardhat-network-helpers');
const {
EXEC_TYPE_DEFAULT,
EXEC_TYPE_TRY,
encodeSingle,
encodeBatch,
encodeDelegate,
CALL_TYPE_CALL,
CALL_TYPE_BATCH,
encodeMode,
} = require('../../helpers/erc7579');
const { selector } = require('../../helpers/methods');
const coder = ethers.AbiCoder.defaultAbiCoder();
const fixture = async () => {
const [sender] = await ethers.getSigners();
const utils = await ethers.deployContract('$ERC7579Utils');
const utilsGlobal = await ethers.deployContract('$ERC7579UtilsGlobalMock');
const target = await ethers.deployContract('CallReceiverMock');
const anotherTarget = await ethers.deployContract('CallReceiverMock');
await setBalance(utils.target, ethers.parseEther('1'));
return { utils, utilsGlobal, target, anotherTarget, sender };
};
describe('ERC7579Utils', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
describe('execSingle', function () {
it('calls the target with value', async function () {
const value = 0x012;
const data = encodeSingle(this.target, value, this.target.interface.encodeFunctionData('mockFunction'));
await expect(this.utils.$execSingle(EXEC_TYPE_DEFAULT, data)).to.emit(this.target, 'MockFunctionCalled');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value);
});
it('calls the target with value and args', async function () {
const value = 0x432;
const data = encodeSingle(
this.target,
value,
this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']),
);
await expect(this.utils.$execSingle(EXEC_TYPE_DEFAULT, data))
.to.emit(this.target, 'MockFunctionCalledWithArgs')
.withArgs(42, '0x1234');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value);
});
it('reverts when target reverts in default ExecType', async function () {
const value = 0x012;
const data = encodeSingle(
this.target,
value,
this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
);
await expect(this.utils.$execSingle(EXEC_TYPE_DEFAULT, data)).to.be.revertedWith('CallReceiverMock: reverting');
});
it('emits ERC7579TryExecuteFail event when target reverts in try ExecType', async function () {
const value = 0x012;
const data = encodeSingle(
this.target,
value,
this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
);
await expect(this.utils.$execSingle(EXEC_TYPE_TRY, data))
.to.emit(this.utils, 'ERC7579TryExecuteFail')
.withArgs(
CALL_TYPE_CALL,
ethers.solidityPacked(
['bytes4', 'bytes'],
[selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
),
);
});
it('reverts with an invalid exec type', async function () {
const value = 0x012;
const data = encodeSingle(this.target, value, this.target.interface.encodeFunctionData('mockFunction'));
await expect(this.utils.$execSingle('0x03', data))
.to.be.revertedWithCustomError(this.utils, 'ERC7579UnsupportedExecType')
.withArgs('0x03');
});
});
describe('execBatch', function () {
it('calls the targets with value', async function () {
const value1 = 0x012;
const value2 = 0x234;
const data = encodeBatch(
[this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
[this.anotherTarget, value2, this.anotherTarget.interface.encodeFunctionData('mockFunction')],
);
await expect(this.utils.$execBatch(EXEC_TYPE_DEFAULT, data))
.to.emit(this.target, 'MockFunctionCalled')
.to.emit(this.anotherTarget, 'MockFunctionCalled');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2);
});
it('calls the targets with value and args', async function () {
const value1 = 0x012;
const value2 = 0x234;
const data = encodeBatch(
[this.target, value1, this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234'])],
[
this.anotherTarget,
value2,
this.anotherTarget.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']),
],
);
await expect(this.utils.$execBatch(EXEC_TYPE_DEFAULT, data))
.to.emit(this.target, 'MockFunctionCalledWithArgs')
.to.emit(this.anotherTarget, 'MockFunctionCalledWithArgs');
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2);
});
it('reverts when any target reverts in default ExecType', async function () {
const value1 = 0x012;
const value2 = 0x234;
const data = encodeBatch(
[this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
[this.anotherTarget, value2, this.anotherTarget.interface.encodeFunctionData('mockFunctionRevertsReason')],
);
await expect(this.utils.$execBatch(EXEC_TYPE_DEFAULT, data)).to.be.revertedWith('CallReceiverMock: reverting');
});
it('emits ERC7579TryExecuteFail event when any target reverts in try ExecType', async function () {
const value1 = 0x012;
const value2 = 0x234;
const data = encodeBatch(
[this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
[this.anotherTarget, value2, this.anotherTarget.interface.encodeFunctionData('mockFunctionRevertsReason')],
);
await expect(this.utils.$execBatch(EXEC_TYPE_TRY, data))
.to.emit(this.utils, 'ERC7579TryExecuteFail')
.withArgs(
CALL_TYPE_BATCH,
ethers.solidityPacked(
['bytes4', 'bytes'],
[selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
),
);
// Check balances
expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value1);
expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(0);
});
it('reverts with an invalid exec type', async function () {
const value1 = 0x012;
const value2 = 0x234;
const data = encodeBatch(
[this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
[this.anotherTarget, value2, this.anotherTarget.interface.encodeFunctionData('mockFunction')],
);
await expect(this.utils.$execBatch('0x03', data))
.to.be.revertedWithCustomError(this.utils, 'ERC7579UnsupportedExecType')
.withArgs('0x03');
});
});
describe('execDelegateCall', function () {
it('delegate calls the target', async function () {
const slot = ethers.hexlify(ethers.randomBytes(32));
const value = ethers.hexlify(ethers.randomBytes(32));
const data = encodeDelegate(
this.target,
this.target.interface.encodeFunctionData('mockFunctionWritesStorage', [slot, value]),
);
expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(ethers.ZeroHash);
await this.utils.$execDelegateCall(EXEC_TYPE_DEFAULT, data);
expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(value);
});
it('reverts when target reverts in default ExecType', async function () {
const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunctionRevertsReason'));
await expect(this.utils.$execDelegateCall(EXEC_TYPE_DEFAULT, data)).to.be.revertedWith(
'CallReceiverMock: reverting',
);
});
it('emits ERC7579TryExecuteFail event when target reverts in try ExecType', async function () {
const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunctionRevertsReason'));
await expect(this.utils.$execDelegateCall(EXEC_TYPE_TRY, data))
.to.emit(this.utils, 'ERC7579TryExecuteFail')
.withArgs(
CALL_TYPE_CALL,
ethers.solidityPacked(
['bytes4', 'bytes'],
[selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
),
);
});
it('reverts with an invalid exec type', async function () {
const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunction'));
await expect(this.utils.$execDelegateCall('0x03', data))
.to.be.revertedWithCustomError(this.utils, 'ERC7579UnsupportedExecType')
.withArgs('0x03');
});
});
it('encodes Mode', async function () {
const callType = CALL_TYPE_BATCH;
const execType = EXEC_TYPE_TRY;
const selector = '0x12345678';
const payload = ethers.toBeHex(0, 22);
expect(this.utils.$encodeMode(callType, execType, selector, payload)).to.eventually.equal(
encodeMode({
callType,
execType,
selector,
payload,
}),
);
});
it('decodes Mode', async function () {
const callType = CALL_TYPE_BATCH;
const execType = EXEC_TYPE_TRY;
const selector = '0x12345678';
const payload = ethers.toBeHex(0, 22);
expect(
this.utils.$decodeMode(
encodeMode({
callType,
execType,
selector,
payload,
}),
),
).to.eventually.deep.equal([callType, execType, selector, payload]);
});
it('encodes single', async function () {
const target = this.target;
const value = 0x123;
const data = '0x12345678';
expect(this.utils.$encodeSingle(target, value, data)).to.eventually.equal(encodeSingle(target, value, data));
});
it('decodes single', async function () {
const target = this.target;
const value = 0x123;
const data = '0x12345678';
expect(this.utils.$decodeSingle(encodeSingle(target, value, data))).to.eventually.deep.equal([
target.target,
value,
data,
]);
});
it('encodes batch', async function () {
const entries = [
[this.target, 0x123, '0x12345678'],
[this.anotherTarget, 0x456, '0x12345678'],
];
expect(this.utils.$encodeBatch(entries)).to.eventually.equal(encodeBatch(...entries));
});
it('decodes batch', async function () {
const entries = [
[this.target.target, 0x123, '0x12345678'],
[this.anotherTarget.target, 0x456, '0x12345678'],
];
expect(this.utils.$decodeBatch(encodeBatch(...entries))).to.eventually.deep.equal(entries);
});
it('encodes delegate', async function () {
const target = this.target;
const data = '0x12345678';
expect(this.utils.$encodeDelegate(target, data)).to.eventually.equal(encodeDelegate(target, data));
});
it('decodes delegate', async function () {
const target = this.target;
const data = '0x12345678';
expect(this.utils.$decodeDelegate(encodeDelegate(target, data))).to.eventually.deep.equal([target.target, data]);
});
describe('global', function () {
describe('eqCallTypeGlobal', function () {
it('returns true if both call types are equal', async function () {
expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_BATCH, CALL_TYPE_BATCH)).to.eventually.be.true;
});
it('returns false if both call types are different', async function () {
expect(this.utilsGlobal.$eqCallTypeGlobal(CALL_TYPE_CALL, CALL_TYPE_BATCH)).to.eventually.be.false;
});
});
describe('eqExecTypeGlobal', function () {
it('returns true if both exec types are equal', async function () {
expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_TRY, EXEC_TYPE_TRY)).to.eventually.be.true;
});
it('returns false if both exec types are different', async function () {
expect(this.utilsGlobal.$eqExecTypeGlobal(EXEC_TYPE_DEFAULT, EXEC_TYPE_TRY)).to.eventually.be.false;
});
});
describe('eqModeSelectorGlobal', function () {
it('returns true if both selectors are equal', async function () {
expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x12345678')).to.eventually.be.true;
});
it('returns false if both selectors are different', async function () {
expect(this.utilsGlobal.$eqModeSelectorGlobal('0x12345678', '0x87654321')).to.eventually.be.false;
});
});
describe('eqModePayloadGlobal', function () {
it('returns true if both payloads are equal', async function () {
expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(0, 22))).to.eventually.be
.true;
});
it('returns false if both payloads are different', async function () {
expect(this.utilsGlobal.$eqModePayloadGlobal(ethers.toBeHex(0, 22), ethers.toBeHex(1, 22))).to.eventually.be
.false;
});
});
});
});

95
test/helpers/erc4337.js Normal file
View File

@ -0,0 +1,95 @@
const { ethers } = require('hardhat');
const SIG_VALIDATION_SUCCESS = '0x0000000000000000000000000000000000000000';
const SIG_VALIDATION_FAILURE = '0x0000000000000000000000000000000000000001';
function getAddress(account) {
return account.target ?? account.address ?? account;
}
function pack(left, right) {
return ethers.solidityPacked(['uint128', 'uint128'], [left, right]);
}
function packValidationData(validAfter, validUntil, authorizer) {
return ethers.solidityPacked(
['uint48', 'uint48', 'address'],
[
validAfter,
validUntil,
typeof authorizer == 'boolean'
? authorizer
? SIG_VALIDATION_SUCCESS
: SIG_VALIDATION_FAILURE
: getAddress(authorizer),
],
);
}
function packPaymasterData(paymaster, verificationGasLimit, postOpGasLimit) {
return ethers.solidityPacked(
['address', 'uint128', 'uint128'],
[getAddress(paymaster), verificationGasLimit, postOpGasLimit],
);
}
/// Represent one user operation
class UserOperation {
constructor(params) {
this.sender = getAddress(params.sender);
this.nonce = params.nonce;
this.initCode = params.initCode ?? '0x';
this.callData = params.callData ?? '0x';
this.verificationGas = params.verificationGas ?? 10_000_000n;
this.callGas = params.callGas ?? 100_000n;
this.preVerificationGas = params.preVerificationGas ?? 100_000n;
this.maxPriorityFee = params.maxPriorityFee ?? 100_000n;
this.maxFeePerGas = params.maxFeePerGas ?? 100_000n;
this.paymasterAndData = params.paymasterAndData ?? '0x';
this.signature = params.signature ?? '0x';
}
get packed() {
return {
sender: this.sender,
nonce: this.nonce,
initCode: this.initCode,
callData: this.callData,
accountGasLimits: pack(this.verificationGas, this.callGas),
preVerificationGas: this.preVerificationGas,
gasFees: pack(this.maxPriorityFee, this.maxFeePerGas),
paymasterAndData: this.paymasterAndData,
signature: this.signature,
};
}
hash(entrypoint, chainId) {
const p = this.packed;
const h = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
['address', 'uint256', 'bytes32', 'bytes32', 'uint256', 'uint256', 'uint256', 'uint256'],
[
p.sender,
p.nonce,
ethers.keccak256(p.initCode),
ethers.keccak256(p.callData),
p.accountGasLimits,
p.preVerificationGas,
p.gasFees,
ethers.keccak256(p.paymasterAndData),
],
),
);
return ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'address', 'uint256'], [h, getAddress(entrypoint), chainId]),
);
}
}
module.exports = {
SIG_VALIDATION_SUCCESS,
SIG_VALIDATION_FAILURE,
packValidationData,
packPaymasterData,
UserOperation,
};

58
test/helpers/erc7579.js Normal file
View File

@ -0,0 +1,58 @@
const { ethers } = require('hardhat');
const MODULE_TYPE_VALIDATOR = 1;
const MODULE_TYPE_EXECUTOR = 2;
const MODULE_TYPE_FALLBACK = 3;
const MODULE_TYPE_HOOK = 4;
const EXEC_TYPE_DEFAULT = '0x00';
const EXEC_TYPE_TRY = '0x01';
const CALL_TYPE_CALL = '0x00';
const CALL_TYPE_BATCH = '0x01';
const CALL_TYPE_DELEGATE = '0xff';
const encodeMode = ({
callType = '0x00',
execType = '0x00',
selector = '0x00000000',
payload = '0x00000000000000000000000000000000000000000000',
} = {}) =>
ethers.solidityPacked(
['bytes1', 'bytes1', 'bytes4', 'bytes4', 'bytes22'],
[callType, execType, '0x00000000', selector, payload],
);
const encodeSingle = (target, value = 0n, data = '0x') =>
ethers.solidityPacked(['address', 'uint256', 'bytes'], [target.target ?? target.address ?? target, value, data]);
const encodeBatch = (...entries) =>
ethers.AbiCoder.defaultAbiCoder().encode(
['(address,uint256,bytes)[]'],
[
entries.map(entry =>
Array.isArray(entry)
? [entry[0].target ?? entry[0].address ?? entry[0], entry[1] ?? 0n, entry[2] ?? '0x']
: [entry.target.target ?? entry.target.address ?? entry.target, entry.value ?? 0n, entry.data ?? '0x'],
),
],
);
const encodeDelegate = (target, data = '0x') =>
ethers.solidityPacked(['address', 'bytes'], [target.target ?? target.address ?? target, data]);
module.exports = {
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK,
MODULE_TYPE_HOOK,
EXEC_TYPE_DEFAULT,
EXEC_TYPE_TRY,
CALL_TYPE_CALL,
CALL_TYPE_BATCH,
CALL_TYPE_DELEGATE,
encodeMode,
encodeSingle,
encodeBatch,
encodeDelegate,
};

View File

@ -9,182 +9,287 @@ import {Packing} from "@openzeppelin/contracts/utils/Packing.sol";
contract PackingTest is Test {
using Packing for *;
function testPack(bytes1 left, bytes1 right) external {
function testPack(bytes1 left, bytes1 right) external pure {
assertEq(left, Packing.pack_1_1(left, right).extract_2_1(0));
assertEq(right, Packing.pack_1_1(left, right).extract_2_1(1));
}
function testPack(bytes2 left, bytes2 right) external {
function testPack(bytes2 left, bytes2 right) external pure {
assertEq(left, Packing.pack_2_2(left, right).extract_4_2(0));
assertEq(right, Packing.pack_2_2(left, right).extract_4_2(2));
}
function testPack(bytes2 left, bytes4 right) external {
function testPack(bytes2 left, bytes4 right) external pure {
assertEq(left, Packing.pack_2_4(left, right).extract_6_2(0));
assertEq(right, Packing.pack_2_4(left, right).extract_6_4(2));
}
function testPack(bytes2 left, bytes6 right) external {
function testPack(bytes2 left, bytes6 right) external pure {
assertEq(left, Packing.pack_2_6(left, right).extract_8_2(0));
assertEq(right, Packing.pack_2_6(left, right).extract_8_6(2));
}
function testPack(bytes4 left, bytes2 right) external {
function testPack(bytes2 left, bytes8 right) external pure {
assertEq(left, Packing.pack_2_8(left, right).extract_10_2(0));
assertEq(right, Packing.pack_2_8(left, right).extract_10_8(2));
}
function testPack(bytes2 left, bytes10 right) external pure {
assertEq(left, Packing.pack_2_10(left, right).extract_12_2(0));
assertEq(right, Packing.pack_2_10(left, right).extract_12_10(2));
}
function testPack(bytes2 left, bytes20 right) external pure {
assertEq(left, Packing.pack_2_20(left, right).extract_22_2(0));
assertEq(right, Packing.pack_2_20(left, right).extract_22_20(2));
}
function testPack(bytes2 left, bytes22 right) external pure {
assertEq(left, Packing.pack_2_22(left, right).extract_24_2(0));
assertEq(right, Packing.pack_2_22(left, right).extract_24_22(2));
}
function testPack(bytes4 left, bytes2 right) external pure {
assertEq(left, Packing.pack_4_2(left, right).extract_6_4(0));
assertEq(right, Packing.pack_4_2(left, right).extract_6_2(4));
}
function testPack(bytes4 left, bytes4 right) external {
function testPack(bytes4 left, bytes4 right) external pure {
assertEq(left, Packing.pack_4_4(left, right).extract_8_4(0));
assertEq(right, Packing.pack_4_4(left, right).extract_8_4(4));
}
function testPack(bytes4 left, bytes8 right) external {
function testPack(bytes4 left, bytes6 right) external pure {
assertEq(left, Packing.pack_4_6(left, right).extract_10_4(0));
assertEq(right, Packing.pack_4_6(left, right).extract_10_6(4));
}
function testPack(bytes4 left, bytes8 right) external pure {
assertEq(left, Packing.pack_4_8(left, right).extract_12_4(0));
assertEq(right, Packing.pack_4_8(left, right).extract_12_8(4));
}
function testPack(bytes4 left, bytes12 right) external {
function testPack(bytes4 left, bytes12 right) external pure {
assertEq(left, Packing.pack_4_12(left, right).extract_16_4(0));
assertEq(right, Packing.pack_4_12(left, right).extract_16_12(4));
}
function testPack(bytes4 left, bytes16 right) external {
function testPack(bytes4 left, bytes16 right) external pure {
assertEq(left, Packing.pack_4_16(left, right).extract_20_4(0));
assertEq(right, Packing.pack_4_16(left, right).extract_20_16(4));
}
function testPack(bytes4 left, bytes20 right) external {
function testPack(bytes4 left, bytes20 right) external pure {
assertEq(left, Packing.pack_4_20(left, right).extract_24_4(0));
assertEq(right, Packing.pack_4_20(left, right).extract_24_20(4));
}
function testPack(bytes4 left, bytes24 right) external {
function testPack(bytes4 left, bytes24 right) external pure {
assertEq(left, Packing.pack_4_24(left, right).extract_28_4(0));
assertEq(right, Packing.pack_4_24(left, right).extract_28_24(4));
}
function testPack(bytes4 left, bytes28 right) external {
function testPack(bytes4 left, bytes28 right) external pure {
assertEq(left, Packing.pack_4_28(left, right).extract_32_4(0));
assertEq(right, Packing.pack_4_28(left, right).extract_32_28(4));
}
function testPack(bytes6 left, bytes2 right) external {
function testPack(bytes6 left, bytes2 right) external pure {
assertEq(left, Packing.pack_6_2(left, right).extract_8_6(0));
assertEq(right, Packing.pack_6_2(left, right).extract_8_2(6));
}
function testPack(bytes6 left, bytes6 right) external {
function testPack(bytes6 left, bytes4 right) external pure {
assertEq(left, Packing.pack_6_4(left, right).extract_10_6(0));
assertEq(right, Packing.pack_6_4(left, right).extract_10_4(6));
}
function testPack(bytes6 left, bytes6 right) external pure {
assertEq(left, Packing.pack_6_6(left, right).extract_12_6(0));
assertEq(right, Packing.pack_6_6(left, right).extract_12_6(6));
}
function testPack(bytes8 left, bytes4 right) external {
function testPack(bytes6 left, bytes10 right) external pure {
assertEq(left, Packing.pack_6_10(left, right).extract_16_6(0));
assertEq(right, Packing.pack_6_10(left, right).extract_16_10(6));
}
function testPack(bytes6 left, bytes16 right) external pure {
assertEq(left, Packing.pack_6_16(left, right).extract_22_6(0));
assertEq(right, Packing.pack_6_16(left, right).extract_22_16(6));
}
function testPack(bytes6 left, bytes22 right) external pure {
assertEq(left, Packing.pack_6_22(left, right).extract_28_6(0));
assertEq(right, Packing.pack_6_22(left, right).extract_28_22(6));
}
function testPack(bytes8 left, bytes2 right) external pure {
assertEq(left, Packing.pack_8_2(left, right).extract_10_8(0));
assertEq(right, Packing.pack_8_2(left, right).extract_10_2(8));
}
function testPack(bytes8 left, bytes4 right) external pure {
assertEq(left, Packing.pack_8_4(left, right).extract_12_8(0));
assertEq(right, Packing.pack_8_4(left, right).extract_12_4(8));
}
function testPack(bytes8 left, bytes8 right) external {
function testPack(bytes8 left, bytes8 right) external pure {
assertEq(left, Packing.pack_8_8(left, right).extract_16_8(0));
assertEq(right, Packing.pack_8_8(left, right).extract_16_8(8));
}
function testPack(bytes8 left, bytes12 right) external {
function testPack(bytes8 left, bytes12 right) external pure {
assertEq(left, Packing.pack_8_12(left, right).extract_20_8(0));
assertEq(right, Packing.pack_8_12(left, right).extract_20_12(8));
}
function testPack(bytes8 left, bytes16 right) external {
function testPack(bytes8 left, bytes16 right) external pure {
assertEq(left, Packing.pack_8_16(left, right).extract_24_8(0));
assertEq(right, Packing.pack_8_16(left, right).extract_24_16(8));
}
function testPack(bytes8 left, bytes20 right) external {
function testPack(bytes8 left, bytes20 right) external pure {
assertEq(left, Packing.pack_8_20(left, right).extract_28_8(0));
assertEq(right, Packing.pack_8_20(left, right).extract_28_20(8));
}
function testPack(bytes8 left, bytes24 right) external {
function testPack(bytes8 left, bytes24 right) external pure {
assertEq(left, Packing.pack_8_24(left, right).extract_32_8(0));
assertEq(right, Packing.pack_8_24(left, right).extract_32_24(8));
}
function testPack(bytes12 left, bytes4 right) external {
function testPack(bytes10 left, bytes2 right) external pure {
assertEq(left, Packing.pack_10_2(left, right).extract_12_10(0));
assertEq(right, Packing.pack_10_2(left, right).extract_12_2(10));
}
function testPack(bytes10 left, bytes6 right) external pure {
assertEq(left, Packing.pack_10_6(left, right).extract_16_10(0));
assertEq(right, Packing.pack_10_6(left, right).extract_16_6(10));
}
function testPack(bytes10 left, bytes10 right) external pure {
assertEq(left, Packing.pack_10_10(left, right).extract_20_10(0));
assertEq(right, Packing.pack_10_10(left, right).extract_20_10(10));
}
function testPack(bytes10 left, bytes12 right) external pure {
assertEq(left, Packing.pack_10_12(left, right).extract_22_10(0));
assertEq(right, Packing.pack_10_12(left, right).extract_22_12(10));
}
function testPack(bytes10 left, bytes22 right) external pure {
assertEq(left, Packing.pack_10_22(left, right).extract_32_10(0));
assertEq(right, Packing.pack_10_22(left, right).extract_32_22(10));
}
function testPack(bytes12 left, bytes4 right) external pure {
assertEq(left, Packing.pack_12_4(left, right).extract_16_12(0));
assertEq(right, Packing.pack_12_4(left, right).extract_16_4(12));
}
function testPack(bytes12 left, bytes8 right) external {
function testPack(bytes12 left, bytes8 right) external pure {
assertEq(left, Packing.pack_12_8(left, right).extract_20_12(0));
assertEq(right, Packing.pack_12_8(left, right).extract_20_8(12));
}
function testPack(bytes12 left, bytes12 right) external {
function testPack(bytes12 left, bytes10 right) external pure {
assertEq(left, Packing.pack_12_10(left, right).extract_22_12(0));
assertEq(right, Packing.pack_12_10(left, right).extract_22_10(12));
}
function testPack(bytes12 left, bytes12 right) external pure {
assertEq(left, Packing.pack_12_12(left, right).extract_24_12(0));
assertEq(right, Packing.pack_12_12(left, right).extract_24_12(12));
}
function testPack(bytes12 left, bytes16 right) external {
function testPack(bytes12 left, bytes16 right) external pure {
assertEq(left, Packing.pack_12_16(left, right).extract_28_12(0));
assertEq(right, Packing.pack_12_16(left, right).extract_28_16(12));
}
function testPack(bytes12 left, bytes20 right) external {
function testPack(bytes12 left, bytes20 right) external pure {
assertEq(left, Packing.pack_12_20(left, right).extract_32_12(0));
assertEq(right, Packing.pack_12_20(left, right).extract_32_20(12));
}
function testPack(bytes16 left, bytes4 right) external {
function testPack(bytes16 left, bytes4 right) external pure {
assertEq(left, Packing.pack_16_4(left, right).extract_20_16(0));
assertEq(right, Packing.pack_16_4(left, right).extract_20_4(16));
}
function testPack(bytes16 left, bytes8 right) external {
function testPack(bytes16 left, bytes6 right) external pure {
assertEq(left, Packing.pack_16_6(left, right).extract_22_16(0));
assertEq(right, Packing.pack_16_6(left, right).extract_22_6(16));
}
function testPack(bytes16 left, bytes8 right) external pure {
assertEq(left, Packing.pack_16_8(left, right).extract_24_16(0));
assertEq(right, Packing.pack_16_8(left, right).extract_24_8(16));
}
function testPack(bytes16 left, bytes12 right) external {
function testPack(bytes16 left, bytes12 right) external pure {
assertEq(left, Packing.pack_16_12(left, right).extract_28_16(0));
assertEq(right, Packing.pack_16_12(left, right).extract_28_12(16));
}
function testPack(bytes16 left, bytes16 right) external {
function testPack(bytes16 left, bytes16 right) external pure {
assertEq(left, Packing.pack_16_16(left, right).extract_32_16(0));
assertEq(right, Packing.pack_16_16(left, right).extract_32_16(16));
}
function testPack(bytes20 left, bytes4 right) external {
function testPack(bytes20 left, bytes2 right) external pure {
assertEq(left, Packing.pack_20_2(left, right).extract_22_20(0));
assertEq(right, Packing.pack_20_2(left, right).extract_22_2(20));
}
function testPack(bytes20 left, bytes4 right) external pure {
assertEq(left, Packing.pack_20_4(left, right).extract_24_20(0));
assertEq(right, Packing.pack_20_4(left, right).extract_24_4(20));
}
function testPack(bytes20 left, bytes8 right) external {
function testPack(bytes20 left, bytes8 right) external pure {
assertEq(left, Packing.pack_20_8(left, right).extract_28_20(0));
assertEq(right, Packing.pack_20_8(left, right).extract_28_8(20));
}
function testPack(bytes20 left, bytes12 right) external {
function testPack(bytes20 left, bytes12 right) external pure {
assertEq(left, Packing.pack_20_12(left, right).extract_32_20(0));
assertEq(right, Packing.pack_20_12(left, right).extract_32_12(20));
}
function testPack(bytes24 left, bytes4 right) external {
function testPack(bytes22 left, bytes2 right) external pure {
assertEq(left, Packing.pack_22_2(left, right).extract_24_22(0));
assertEq(right, Packing.pack_22_2(left, right).extract_24_2(22));
}
function testPack(bytes22 left, bytes6 right) external pure {
assertEq(left, Packing.pack_22_6(left, right).extract_28_22(0));
assertEq(right, Packing.pack_22_6(left, right).extract_28_6(22));
}
function testPack(bytes22 left, bytes10 right) external pure {
assertEq(left, Packing.pack_22_10(left, right).extract_32_22(0));
assertEq(right, Packing.pack_22_10(left, right).extract_32_10(22));
}
function testPack(bytes24 left, bytes4 right) external pure {
assertEq(left, Packing.pack_24_4(left, right).extract_28_24(0));
assertEq(right, Packing.pack_24_4(left, right).extract_28_4(24));
}
function testPack(bytes24 left, bytes8 right) external {
function testPack(bytes24 left, bytes8 right) external pure {
assertEq(left, Packing.pack_24_8(left, right).extract_32_24(0));
assertEq(right, Packing.pack_24_8(left, right).extract_32_8(24));
}
function testPack(bytes28 left, bytes4 right) external {
function testPack(bytes28 left, bytes4 right) external pure {
assertEq(left, Packing.pack_28_4(left, right).extract_32_28(0));
assertEq(right, Packing.pack_28_4(left, right).extract_32_4(28));
}
function testReplace(bytes2 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes2 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 1));
bytes1 oldValue = container.extract_2_1(offset);
@ -193,7 +298,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_2_1(newValue, offset).replace_2_1(oldValue, offset));
}
function testReplace(bytes4 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes4 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 3));
bytes1 oldValue = container.extract_4_1(offset);
@ -202,7 +307,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_4_1(newValue, offset).replace_4_1(oldValue, offset));
}
function testReplace(bytes4 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes4 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes2 oldValue = container.extract_4_2(offset);
@ -211,7 +316,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_4_2(newValue, offset).replace_4_2(oldValue, offset));
}
function testReplace(bytes6 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes6 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 5));
bytes1 oldValue = container.extract_6_1(offset);
@ -220,7 +325,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_6_1(newValue, offset).replace_6_1(oldValue, offset));
}
function testReplace(bytes6 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes6 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes2 oldValue = container.extract_6_2(offset);
@ -229,7 +334,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_6_2(newValue, offset).replace_6_2(oldValue, offset));
}
function testReplace(bytes6 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes6 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes4 oldValue = container.extract_6_4(offset);
@ -238,7 +343,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_6_4(newValue, offset).replace_6_4(oldValue, offset));
}
function testReplace(bytes8 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes8 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 7));
bytes1 oldValue = container.extract_8_1(offset);
@ -247,7 +352,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_8_1(newValue, offset).replace_8_1(oldValue, offset));
}
function testReplace(bytes8 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes8 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes2 oldValue = container.extract_8_2(offset);
@ -256,7 +361,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_8_2(newValue, offset).replace_8_2(oldValue, offset));
}
function testReplace(bytes8 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes8 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes4 oldValue = container.extract_8_4(offset);
@ -265,7 +370,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_8_4(newValue, offset).replace_8_4(oldValue, offset));
}
function testReplace(bytes8 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes8 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes6 oldValue = container.extract_8_6(offset);
@ -274,7 +379,52 @@ contract PackingTest is Test {
assertEq(container, container.replace_8_6(newValue, offset).replace_8_6(oldValue, offset));
}
function testReplace(bytes12 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes10 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 9));
bytes1 oldValue = container.extract_10_1(offset);
assertEq(newValue, container.replace_10_1(newValue, offset).extract_10_1(offset));
assertEq(container, container.replace_10_1(newValue, offset).replace_10_1(oldValue, offset));
}
function testReplace(bytes10 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes2 oldValue = container.extract_10_2(offset);
assertEq(newValue, container.replace_10_2(newValue, offset).extract_10_2(offset));
assertEq(container, container.replace_10_2(newValue, offset).replace_10_2(oldValue, offset));
}
function testReplace(bytes10 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes4 oldValue = container.extract_10_4(offset);
assertEq(newValue, container.replace_10_4(newValue, offset).extract_10_4(offset));
assertEq(container, container.replace_10_4(newValue, offset).replace_10_4(oldValue, offset));
}
function testReplace(bytes10 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes6 oldValue = container.extract_10_6(offset);
assertEq(newValue, container.replace_10_6(newValue, offset).extract_10_6(offset));
assertEq(container, container.replace_10_6(newValue, offset).replace_10_6(oldValue, offset));
}
function testReplace(bytes10 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes8 oldValue = container.extract_10_8(offset);
assertEq(newValue, container.replace_10_8(newValue, offset).extract_10_8(offset));
assertEq(container, container.replace_10_8(newValue, offset).replace_10_8(oldValue, offset));
}
function testReplace(bytes12 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 11));
bytes1 oldValue = container.extract_12_1(offset);
@ -283,7 +433,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_12_1(newValue, offset).replace_12_1(oldValue, offset));
}
function testReplace(bytes12 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes12 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 10));
bytes2 oldValue = container.extract_12_2(offset);
@ -292,7 +442,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_12_2(newValue, offset).replace_12_2(oldValue, offset));
}
function testReplace(bytes12 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes12 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes4 oldValue = container.extract_12_4(offset);
@ -301,7 +451,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_12_4(newValue, offset).replace_12_4(oldValue, offset));
}
function testReplace(bytes12 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes12 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes6 oldValue = container.extract_12_6(offset);
@ -310,7 +460,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_12_6(newValue, offset).replace_12_6(oldValue, offset));
}
function testReplace(bytes12 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes12 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes8 oldValue = container.extract_12_8(offset);
@ -319,7 +469,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_12_8(newValue, offset).replace_12_8(oldValue, offset));
}
function testReplace(bytes16 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes12 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes10 oldValue = container.extract_12_10(offset);
assertEq(newValue, container.replace_12_10(newValue, offset).extract_12_10(offset));
assertEq(container, container.replace_12_10(newValue, offset).replace_12_10(oldValue, offset));
}
function testReplace(bytes16 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 15));
bytes1 oldValue = container.extract_16_1(offset);
@ -328,7 +487,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_1(newValue, offset).replace_16_1(oldValue, offset));
}
function testReplace(bytes16 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes16 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 14));
bytes2 oldValue = container.extract_16_2(offset);
@ -337,7 +496,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_2(newValue, offset).replace_16_2(oldValue, offset));
}
function testReplace(bytes16 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes16 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes4 oldValue = container.extract_16_4(offset);
@ -346,7 +505,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_4(newValue, offset).replace_16_4(oldValue, offset));
}
function testReplace(bytes16 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes16 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 10));
bytes6 oldValue = container.extract_16_6(offset);
@ -355,7 +514,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_6(newValue, offset).replace_16_6(oldValue, offset));
}
function testReplace(bytes16 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes16 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes8 oldValue = container.extract_16_8(offset);
@ -364,7 +523,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_8(newValue, offset).replace_16_8(oldValue, offset));
}
function testReplace(bytes16 container, bytes12 newValue, uint8 offset) external {
function testReplace(bytes16 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes10 oldValue = container.extract_16_10(offset);
assertEq(newValue, container.replace_16_10(newValue, offset).extract_16_10(offset));
assertEq(container, container.replace_16_10(newValue, offset).replace_16_10(oldValue, offset));
}
function testReplace(bytes16 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes12 oldValue = container.extract_16_12(offset);
@ -373,7 +541,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_16_12(newValue, offset).replace_16_12(oldValue, offset));
}
function testReplace(bytes20 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 19));
bytes1 oldValue = container.extract_20_1(offset);
@ -382,7 +550,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_1(newValue, offset).replace_20_1(oldValue, offset));
}
function testReplace(bytes20 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 18));
bytes2 oldValue = container.extract_20_2(offset);
@ -391,7 +559,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_2(newValue, offset).replace_20_2(oldValue, offset));
}
function testReplace(bytes20 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 16));
bytes4 oldValue = container.extract_20_4(offset);
@ -400,7 +568,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_4(newValue, offset).replace_20_4(oldValue, offset));
}
function testReplace(bytes20 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 14));
bytes6 oldValue = container.extract_20_6(offset);
@ -409,7 +577,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_6(newValue, offset).replace_20_6(oldValue, offset));
}
function testReplace(bytes20 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes8 oldValue = container.extract_20_8(offset);
@ -418,7 +586,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_8(newValue, offset).replace_20_8(oldValue, offset));
}
function testReplace(bytes20 container, bytes12 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 10));
bytes10 oldValue = container.extract_20_10(offset);
assertEq(newValue, container.replace_20_10(newValue, offset).extract_20_10(offset));
assertEq(container, container.replace_20_10(newValue, offset).replace_20_10(oldValue, offset));
}
function testReplace(bytes20 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes12 oldValue = container.extract_20_12(offset);
@ -427,7 +604,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_12(newValue, offset).replace_20_12(oldValue, offset));
}
function testReplace(bytes20 container, bytes16 newValue, uint8 offset) external {
function testReplace(bytes20 container, bytes16 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes16 oldValue = container.extract_20_16(offset);
@ -436,7 +613,88 @@ contract PackingTest is Test {
assertEq(container, container.replace_20_16(newValue, offset).replace_20_16(oldValue, offset));
}
function testReplace(bytes24 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes22 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 21));
bytes1 oldValue = container.extract_22_1(offset);
assertEq(newValue, container.replace_22_1(newValue, offset).extract_22_1(offset));
assertEq(container, container.replace_22_1(newValue, offset).replace_22_1(oldValue, offset));
}
function testReplace(bytes22 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 20));
bytes2 oldValue = container.extract_22_2(offset);
assertEq(newValue, container.replace_22_2(newValue, offset).extract_22_2(offset));
assertEq(container, container.replace_22_2(newValue, offset).replace_22_2(oldValue, offset));
}
function testReplace(bytes22 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 18));
bytes4 oldValue = container.extract_22_4(offset);
assertEq(newValue, container.replace_22_4(newValue, offset).extract_22_4(offset));
assertEq(container, container.replace_22_4(newValue, offset).replace_22_4(oldValue, offset));
}
function testReplace(bytes22 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 16));
bytes6 oldValue = container.extract_22_6(offset);
assertEq(newValue, container.replace_22_6(newValue, offset).extract_22_6(offset));
assertEq(container, container.replace_22_6(newValue, offset).replace_22_6(oldValue, offset));
}
function testReplace(bytes22 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 14));
bytes8 oldValue = container.extract_22_8(offset);
assertEq(newValue, container.replace_22_8(newValue, offset).extract_22_8(offset));
assertEq(container, container.replace_22_8(newValue, offset).replace_22_8(oldValue, offset));
}
function testReplace(bytes22 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes10 oldValue = container.extract_22_10(offset);
assertEq(newValue, container.replace_22_10(newValue, offset).extract_22_10(offset));
assertEq(container, container.replace_22_10(newValue, offset).replace_22_10(oldValue, offset));
}
function testReplace(bytes22 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 10));
bytes12 oldValue = container.extract_22_12(offset);
assertEq(newValue, container.replace_22_12(newValue, offset).extract_22_12(offset));
assertEq(container, container.replace_22_12(newValue, offset).replace_22_12(oldValue, offset));
}
function testReplace(bytes22 container, bytes16 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes16 oldValue = container.extract_22_16(offset);
assertEq(newValue, container.replace_22_16(newValue, offset).extract_22_16(offset));
assertEq(container, container.replace_22_16(newValue, offset).replace_22_16(oldValue, offset));
}
function testReplace(bytes22 container, bytes20 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes20 oldValue = container.extract_22_20(offset);
assertEq(newValue, container.replace_22_20(newValue, offset).extract_22_20(offset));
assertEq(container, container.replace_22_20(newValue, offset).replace_22_20(oldValue, offset));
}
function testReplace(bytes24 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 23));
bytes1 oldValue = container.extract_24_1(offset);
@ -445,7 +703,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_1(newValue, offset).replace_24_1(oldValue, offset));
}
function testReplace(bytes24 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 22));
bytes2 oldValue = container.extract_24_2(offset);
@ -454,7 +712,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_2(newValue, offset).replace_24_2(oldValue, offset));
}
function testReplace(bytes24 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 20));
bytes4 oldValue = container.extract_24_4(offset);
@ -463,7 +721,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_4(newValue, offset).replace_24_4(oldValue, offset));
}
function testReplace(bytes24 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 18));
bytes6 oldValue = container.extract_24_6(offset);
@ -472,7 +730,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_6(newValue, offset).replace_24_6(oldValue, offset));
}
function testReplace(bytes24 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 16));
bytes8 oldValue = container.extract_24_8(offset);
@ -481,7 +739,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_8(newValue, offset).replace_24_8(oldValue, offset));
}
function testReplace(bytes24 container, bytes12 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 14));
bytes10 oldValue = container.extract_24_10(offset);
assertEq(newValue, container.replace_24_10(newValue, offset).extract_24_10(offset));
assertEq(container, container.replace_24_10(newValue, offset).replace_24_10(oldValue, offset));
}
function testReplace(bytes24 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes12 oldValue = container.extract_24_12(offset);
@ -490,7 +757,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_12(newValue, offset).replace_24_12(oldValue, offset));
}
function testReplace(bytes24 container, bytes16 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes16 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes16 oldValue = container.extract_24_16(offset);
@ -499,7 +766,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_16(newValue, offset).replace_24_16(oldValue, offset));
}
function testReplace(bytes24 container, bytes20 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes20 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes20 oldValue = container.extract_24_20(offset);
@ -508,7 +775,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_24_20(newValue, offset).replace_24_20(oldValue, offset));
}
function testReplace(bytes28 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes24 container, bytes22 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 2));
bytes22 oldValue = container.extract_24_22(offset);
assertEq(newValue, container.replace_24_22(newValue, offset).extract_24_22(offset));
assertEq(container, container.replace_24_22(newValue, offset).replace_24_22(oldValue, offset));
}
function testReplace(bytes28 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 27));
bytes1 oldValue = container.extract_28_1(offset);
@ -517,7 +793,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_1(newValue, offset).replace_28_1(oldValue, offset));
}
function testReplace(bytes28 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 26));
bytes2 oldValue = container.extract_28_2(offset);
@ -526,7 +802,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_2(newValue, offset).replace_28_2(oldValue, offset));
}
function testReplace(bytes28 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 24));
bytes4 oldValue = container.extract_28_4(offset);
@ -535,7 +811,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_4(newValue, offset).replace_28_4(oldValue, offset));
}
function testReplace(bytes28 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 22));
bytes6 oldValue = container.extract_28_6(offset);
@ -544,7 +820,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_6(newValue, offset).replace_28_6(oldValue, offset));
}
function testReplace(bytes28 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 20));
bytes8 oldValue = container.extract_28_8(offset);
@ -553,7 +829,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_8(newValue, offset).replace_28_8(oldValue, offset));
}
function testReplace(bytes28 container, bytes12 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 18));
bytes10 oldValue = container.extract_28_10(offset);
assertEq(newValue, container.replace_28_10(newValue, offset).extract_28_10(offset));
assertEq(container, container.replace_28_10(newValue, offset).replace_28_10(oldValue, offset));
}
function testReplace(bytes28 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 16));
bytes12 oldValue = container.extract_28_12(offset);
@ -562,7 +847,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_12(newValue, offset).replace_28_12(oldValue, offset));
}
function testReplace(bytes28 container, bytes16 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes16 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes16 oldValue = container.extract_28_16(offset);
@ -571,7 +856,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_16(newValue, offset).replace_28_16(oldValue, offset));
}
function testReplace(bytes28 container, bytes20 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes20 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes20 oldValue = container.extract_28_20(offset);
@ -580,7 +865,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_20(newValue, offset).replace_28_20(oldValue, offset));
}
function testReplace(bytes28 container, bytes24 newValue, uint8 offset) external {
function testReplace(bytes28 container, bytes22 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 6));
bytes22 oldValue = container.extract_28_22(offset);
assertEq(newValue, container.replace_28_22(newValue, offset).extract_28_22(offset));
assertEq(container, container.replace_28_22(newValue, offset).replace_28_22(oldValue, offset));
}
function testReplace(bytes28 container, bytes24 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes24 oldValue = container.extract_28_24(offset);
@ -589,7 +883,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_28_24(newValue, offset).replace_28_24(oldValue, offset));
}
function testReplace(bytes32 container, bytes1 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes1 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 31));
bytes1 oldValue = container.extract_32_1(offset);
@ -598,7 +892,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_1(newValue, offset).replace_32_1(oldValue, offset));
}
function testReplace(bytes32 container, bytes2 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes2 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 30));
bytes2 oldValue = container.extract_32_2(offset);
@ -607,7 +901,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_2(newValue, offset).replace_32_2(oldValue, offset));
}
function testReplace(bytes32 container, bytes4 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes4 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 28));
bytes4 oldValue = container.extract_32_4(offset);
@ -616,7 +910,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_4(newValue, offset).replace_32_4(oldValue, offset));
}
function testReplace(bytes32 container, bytes6 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes6 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 26));
bytes6 oldValue = container.extract_32_6(offset);
@ -625,7 +919,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_6(newValue, offset).replace_32_6(oldValue, offset));
}
function testReplace(bytes32 container, bytes8 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes8 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 24));
bytes8 oldValue = container.extract_32_8(offset);
@ -634,7 +928,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_8(newValue, offset).replace_32_8(oldValue, offset));
}
function testReplace(bytes32 container, bytes12 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes10 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 22));
bytes10 oldValue = container.extract_32_10(offset);
assertEq(newValue, container.replace_32_10(newValue, offset).extract_32_10(offset));
assertEq(container, container.replace_32_10(newValue, offset).replace_32_10(oldValue, offset));
}
function testReplace(bytes32 container, bytes12 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 20));
bytes12 oldValue = container.extract_32_12(offset);
@ -643,7 +946,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_12(newValue, offset).replace_32_12(oldValue, offset));
}
function testReplace(bytes32 container, bytes16 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes16 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 16));
bytes16 oldValue = container.extract_32_16(offset);
@ -652,7 +955,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_16(newValue, offset).replace_32_16(oldValue, offset));
}
function testReplace(bytes32 container, bytes20 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes20 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 12));
bytes20 oldValue = container.extract_32_20(offset);
@ -661,7 +964,16 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_20(newValue, offset).replace_32_20(oldValue, offset));
}
function testReplace(bytes32 container, bytes24 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes22 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 10));
bytes22 oldValue = container.extract_32_22(offset);
assertEq(newValue, container.replace_32_22(newValue, offset).extract_32_22(offset));
assertEq(container, container.replace_32_22(newValue, offset).replace_32_22(oldValue, offset));
}
function testReplace(bytes32 container, bytes24 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 8));
bytes24 oldValue = container.extract_32_24(offset);
@ -670,7 +982,7 @@ contract PackingTest is Test {
assertEq(container, container.replace_32_24(newValue, offset).replace_32_24(oldValue, offset));
}
function testReplace(bytes32 container, bytes28 newValue, uint8 offset) external {
function testReplace(bytes32 container, bytes28 newValue, uint8 offset) external pure {
offset = uint8(bound(offset, 0, 4));
bytes28 oldValue = container.extract_32_28(offset);