Alchemy ERC security record

Ethereumalchemy.com

As of 30 Oct 2026, Alchemy ERC has 6 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 20 May 2025. Those reports list no open critical findings.

Critical findings
No open critical findings
Across 6 reports.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits6

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Alchemy ERC-4337 PaymastersSecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com
Alchemy - Modular Account V2Security firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com
Alchemy - LightAccount v2Security firm: QuantstampDate: Scope: Repository at commit 93f46a2No open critical findingsOpen · certificate.quantstamp.com
Alchemy - Multisig PluginSecurity firm: QuantstampDate: Scope: Repository at commit 70089a2No open critical findingsOpen · certificate.quantstamp.com
Alchemy Modular AccountSecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com
Alchemy Light AccountSecurity firm: QuantstampDate: Scope: Repository at commit aa8196bNo open critical findingsOpen · certificate.quantstamp.com

Findings85

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical000
High004
Medium124
Low33536
MediumMalicious Plugin Could Prevent Uninstall and Lead to Denial-Of-ServiceAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

Plugins can register hooks to offer custom functionality during various stages of a user operation or runtime transaction. However, a malicious plugin can abuse this by introducing hooks that consistently revert, preventing the user from uninstalling the plugin or upgrading the account. As arbitrary selectors can be specified, this could completely brick the account. This issue outlines the most significant impact of the dangers of the plugin trust model. Other cases are covered in MSCA-10.

Open the report
MediumState-Changing Hooks May Affect Validation Logic in Other HooksAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

Plugins can define pre- or post-execution hooks to a specific execution function, which allows them to perform additional validations or modify states before or after the execution function call. However, when a plugin receives a call to its pre-execution hook, it should not assume that the state at the time of the pre-hook call will remain unchanged when the execution function is called. This is because the MSCA implementation allows overlapping hooks, so multiple pre-hooks can be executed after the current pre-hook but before the execution function call. Furthermore, The execution order of the hooks cannot be enforced by the plugins and depends on the account's internal state. Therefore, validation in a pre-hook based on the current state may no longer be valid if the state being checked is changed by another pre-hook before the execution function call. For example, the `SessionKeyPermissionsPlugin` defines a pre-execution hook, which invokes the `_updateLimitsPreExec()` function to update the ERC-20 token spend limits of a session key based on the `transfer()` or `approve()` value. If the call is `approve()`, this function relies on the current allowance to the spender to calculate the increase in the amount spent. However, such a calculation can be erroneous as it assumes no allowance change will occur after the current pre-hook call. A similar issue may exist in post-hooks and validation hooks as well. For example, the state at the time of a post-hook call may not be the exact state after the execution function is executed since there may be other post-hooks in between and change the state.

Open the report
Low`paymasterData` Field Length Should Be EnforcedAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

For `mode 1, 2 and 3` the `paymasterData` field is structured in the following way: ``` For v0.6 // ERC-20 format: // - [0:20] 20 bytes for paymaster // - [20:52] 32 bytes for sigTimeRange // - [52] 1 byte for mode (0: pre-approval, 1: post-op, 2: permit) // - [53:85] 32 bytes for conversion factor (scaled token price, calculated off-chain) // PRICE_SCALE * ethPriceUsd * 10^tokenDecimals / (tokenPriceUsd * 10^(ethDecimals)) // - [85:105] 20 bytes for token address // - [105:125] 20 bytes for receiver address // - [125:190] 65 bytes for signature // - [190:] variable bytes for permit (if mode == 2) For v0.7 // ERC-20 format for paymaster data: // - [0] 1 byte for mode (0: pre-approval, 1: post-op, 2: permit) // - [1:33] 32 bytes for conversion factor (scaled token price, calculated off-chain) // PRICE_SCALE * ethPriceUsd * 10^tokenDecimals / (tokenPriceUsd * 10^(ethDecimals)) // - [33:53] 20 bytes for token address // - [53:73] 20 bytes for receiver address // - [73:138] 65 bytes for signature // - [138:] variable bytes for permit (if mode == 2) ``` For example, in the context of paymaster v0.7, the bits 138 onwards are only used in `mode = 2`, but there is no enforcement that the field is not used during `modes = 0` and `1`, which could lead to wasted gas due to byte stuffing. The only check currently enforces the `paymasterData ` to be at least 138 bits. There are no direct exploits here, as this byte stuffing would cause a loss for the user because both the paymaster and the user have to sign this data. There is only one case where the paymaster loses some funds, and that is in `mode 1`, where the user does not pay in `postOp`. However, it would be a good practice to avoid this griefing vector.

Open the report
LowInaccurate `postOp()`-Gas Cost Charges Due to Single ConstantAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

The paymaster implementation for EntryPointv0.6 contains a `POST_OP_GAS ` constant that is a snapshot measure of the gas-costs of the `postOp()` call with USDC as the ERC-20-payment token. This is used as this version of the EntryPoint lacks a granular parameter for limiting the `postOp` gas that is needed for the cost calculations. The single value is slightly simplifying the mechanics of the different modes of operation. In `mode = 0`, there is for example not just one, but two `safeTransfers()` compared to `mode = 1`. Some tokens might have differing gas costs for their transfers, especially if e.g. ERC-777 or ERC-1155 tokens with callbacks are used. Different chains might also have different gas costs for certain opcodes that are used in this call. Furthermore, in the version supporting EntryPointv0.6, there is also the possibility of reverts, leading to a second invocation of the `postOp()` call that incurs additional gas costs not captured by this constant. We also want to mention that there are very select few chains that adjust the gas costs of certain opcodes most notably [ZKSync](https://docs.zksync.io/zksync-protocol/rollup/fee-model#different-opcode-pricing), that could lead to inaccuracies with the same value between different chain deployments.

Open the report
LowIncorrect Gas Penalty Calculation in `postOp()` Overcharges UserAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

The `VerifyingPaymaster` contract applies a 10% gas penalty in the `postOp()` function based on the **total gas limit**, rather than strictly the **unused execution gas**, contrary to the ERC-4337 specification. The penalty is currently calculated as: ```solidity uint256 gasPenalty = (totalGasLimit * actualUserOpFeePerGas - actualGasCostWithPostOp) / 10; ``` This calculation incorrectly penalizes users for unused pre-operation gas, inflating their final charge. According to the [ERC-4337 specification – Required EntryPoint Contract Functionality](https://eips.ethereum.org/EIPS/eip-4337#required-entrypoint-contract-functionality), the `UNUSED_GAS_PENALTY_PERCENT` (10%) should be applied only to unused gas from `callGasLimit` and `paymasterPostOpGasLimit`.

Open the report
LowNo Association Between On-Chain Gas Costs and the User’s ERC-20-PaymentAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

In the flow of using this paymaster, the `UserOperation` sender proposes a `UserOperation` to the paymaster signer API, which calculates the appropriate user token payment and encodes these values into the `paymasterAndData` field of the `UserOperation` struct. The user then confirms this data again by providing a passing validation for the full `UserOperation`. Apart from these off-chain calculations by the Paymaster, the actual gas costs of the `UserOperation` and the token amount are not associated with each other, so the users essentially opt in to some token payment, no matter the actual execution costs. The gas costs could be significantly differ from the underlying ERC-20-payment if the `conversionFactor`, the value indicating the conversion between ETH and the token the user is paying in, is wrongly calculated. In volatile token price conditions, it could also be the case that the signer API signs off a significantly lower `conversionFactor`, resulting in a monetary loss of the paymaster. A compromised signer API could also withdraw the full approval from the `UserOp.sender` in a requested `UserOperation`. If the end user does not detect that when providing the validation for the `UserOperation`, they might be left at a significant financial loss.

Open the report
LowPotential ERC-7562 Violation in `permit()`-FlowAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

The standard specification of ERC-7597 uses a `block.timestamp` check to check for freshness of the forwarded `deadline` parameter. However, as defined by ERC-7562, this is a forbidden opcode for the validation phase of `UserOperations` according to OP-011, which would result in default bundlers dropping such `UserOperations`, ultimately not including them in bundles on-chain. Some token implementations support ERC-7597, but skip the `block.timestamp` check if a `uint256.max` value is encoded as part of the deadline of the permit. An example of such a token is USDC. Such implementations would not lead to ERC-7562 complications.

Open the report
LowPotential Validation Inconsistencies Leading to Paymaster BanAlchemy ERC-4337 Paymasters · Quantstamp · 20 May 2025Acknowledged

The `_validatePaymasterUserOp()` function includes state-dependent reverts that might change in outcome between after second validation and final simulation/execution on the chain, such as paymaster being paused, signer being updated, or allowances/permits being consumed. If validation produces different results for the same `UserOperation` after the second validation simulation, bundlers may ban the paymaster, stopping UserOperations including this paymaster from being propagated. This aligns with [GREP-40](https://eips.ethereum.org/EIPS/eip-7562#general-reputation-rules) from ERC-7562, which emphasizes deterministic validation to prevent bans. Here is a non-exhaustive list of where this might occur: 1. Both Paymasters could be paused via `pause()`. Depending on when the function is called, there can be a case where `UserOperation` is invalidated just before on-chain execution. 2. Signer can be updated via `updateVerifyingSigner()`, which could lead to signature validation failing and there can be a case where `UserOperation` is invalidated just before on-chain execution. 3. Permit or approval can be withdrawn just before the `UserOperation` is executed on-chain.

Open the report
Low`executeBatch()` Can Be Used to Bypass Other Selector's Validation Hook ExecutionAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

With ERC-6900 v0.8, self-calls to the account are strictly reserved to indicate authorization to invoke some selector. Therefore, the ways in which the account can call itself are very important. The `executeBatch()` function is such a case. In case it performs a self-call, that call's selector is analyzed and has to also be able to be validated by the current validation. However, the actual validation, including the validation associated execution hooks, is then not performed, if invoked via a self-call, only the hooks for the outer `executeBatch()` call is performed. This is known and documented, but we wanted to emphasize it in the report, as well. If some entity has access to `executeBatch()`, but also to some by hooks heavily protected execution function, the hooks of that other selector can be bypassed by performing an `executeBatch()` self-call encoding that other execution function.

Open the report
LowCode-Less (Pre-)Signature Validation Modules May Result in Always-Passing ValidationsAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

Within the `ExecutionLib` library, various low-level calls such as `delegatecall()`, `staticcall()`, and `call()` are made without sufficient safeguards, specifically a lack of: 1. `returnsize()` checks 2. `extcodesize()` checks <br> This oversight is concerning, particularly in the context of EIP-7702, where an Externally Owned Account (EOA) could temporarily function as a module when invoking `installExecution()` or `installValidation()`. This scenario could allow subsequent hook calls to succeed improperly without verification. A similar risk exists with standard module installations where no `installData` is used for callbacks or interface checks. Additionally, if a module self-destructs after installation, these unchecked calls may continue to pass, leading to potential security vulnerabilities.

Open the report
Show 10 more
LowConsider adding a `forceUninstallation` Flag that Requires `onInstall()`Callback SuccessAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

In our opinion, it is not optimal that based on user input issues, the `onUninstall()` callback to the module might not succeed, but cannot be re-attempted due to the modules any longer being registered on the account-side. While it is very reasonable to have a `try-catch` flow around the `onUninstall()` callback in case the module is blocking uninstallation, this should perhaps be specifically intended by a user, e.g. with a `forceUninstallation` flag indicating that a reverting module-callback is acceptable.

Open the report
LowDeactivated Fallback Signer in `SemiModularAccountBase` Returned as Global ValidationAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

For semi-modular accounts, In case the fallback signer has been disabled through calls to `updateFallbackSignerData()`, the default validation is still returned as global validation in the `_isValidationGlobal()` function. The impact is minimal and does not enable a passing validation with the disabled fallback signer, because the `_getFallbackSigner()` call ultimately attempting to fetch the signer leads to a revert. However, as in another issue we recommend `getExecutionData()` to contain a global validation check, it might make sense to adjust the function to return false for disabled fallback signers.

Open the report
LowDeferred Actions Allow Usage of Validation Functions in UserOp Context Without `isUserOpValidation` FlagAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

Deferred actions enable actions on behalf of the account to be delayed right up until their execution is needed. This means that there are 2 calls that are bounded together during the `_validateUserOp()` function. As these 2 calls can have different validation functions, the outer user op validation function is verfiried to be correct with the correct `isUserOpValidation` validation flag. Whilst for the inner deferred action call, the validation function is a signature verification one, hence `isSignatureValidation` flag is validated. This is however not fully correct as the deferred action call is also done in the user op context, hence the signature validation function should also have the `isUserOpValidation` set correctly and verified.

Open the report
LowForwarding the `entityId` to the (Un-)Installation CallbacksAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

In the current module setup, most validation and validation-hook modules expect the `entityId` to be encoded within the `installData` passed to `onInstall()` and `uninstallData` passed to `onUninstall()`. This `entityId` links specific configurations and data within the module, allowing for stateful validation based on that `entityId`. This parameter is especially crucial for validation-associated hooks, while for regular execution hooks, it is typically less significant. Execution hooks are generally more stateless, and they currently do not involve an `onInstall()` callback for their installation, reducing the need for entityId encoding in these cases. Encoding the `entityId` in this way introduces potential inconsistencies, as the value in the `validationConfig` may not necessarily match the `entityId` provided in `installData` during `installValidation()` calls. If a module is installed multiple times with different `entityId` values, there is a risk of unintended interference. For example, if module A with `entityId` A is uninstalled but the `uninstallData` includes `entityId` B—an identifier for another installation of the same module—this could cause unintended deregistration on the module or account side, effectively "bricking" both modules by removing important data in both instances. While this risk is minor and mitigated by the fact that data fields are user-signed and validated, reducing the likelihood of intentional misuse, it still introduces a notable risk surface that could lead to user errors affecting account integrity.

Open the report
LowImprovements to the Execution Installation FlowAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

The `installExecution()` function in the `ExecutionInstallDelegate` contract is responsible for setting up both execution functions and execution hooks in a modular account. In case `installData` is provided, the modules to be installed should be invoking `ModuleInstallCommonsLib.onInstall()` on all installed entities, however, for the execution hooks, there is currently no mechanism to do that. The absence of this call is compliant with the standard. However, developers who intend to have stateful execution hooks may not expect this. Hence, it may be beneficial to add an option to add install data to execution hooks. In case `installData` is provided `ModuleInstallCommonsLib.onInstall()` would then perform the interface check and the `onInstall()` call. Furthermore, the implementation only checks for the `IModule` interface when installing execution functions, which is insufficient for confirming that a module adheres to the necessary `IExecutionModule` functionality. This oversight can result in the installation of modules that do not support the required interfaces, even if install data is provided.

Open the report
LowIncomplete Module Data Cleanup on UninstallAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

The `onUninstall()` functions in various modules may leave behind residual state data linked to `msg.sender` after uninstallation, especially since the external call is only performed if `onInstallData` is specified. While this incomplete cleanup is acknowledged in the documentation, it could pose security risks or lead to unexpected behavior in the case where the module is reinstalled with the same entityId. The danger is that previously set permissions or data may be unintentionally reused. Below is an overview of potential residual data for each module: - **`AllowlistModule`** - `allowlist`entries - `selector` mappings - **`NativeTokenLimitModule`** - `limits` - `specialPaymasters` - **`PaymasterGuardModule`** - `paymasters` - **`TimeRangeModule`** - `timeRanges` - **`SingleSignerValidationModule`** - `signers` - **`WebAuthnValidationModule`** - `signers`

Open the report
LowIndividual Validation Hooks Cannot Be Removed From ConfigurationsAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

While it is possible to remove installed execution hooks from existing execution configurations individually, the same is not possible for validation hooks. Validation configurations are always removed in their entirety, with all associated hooks. This is in contrast with the possible hook extension of existing validation or execution configs. As additional validation hooks can be added, perhaps by another party that is authorized to do so, but not removed, this possibly creates a minor UX inconvenience, as an validation installation has to be fully removed and re-installed without one hook to mimic a single hook deinstallation.

Open the report
LowInstalled Execution Functions Can Collide with Native FunctionsAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

The ERC-6900 standard currently clearly states that installations of execution selectors that are clashing with ERC-4337 and ERC-6900 selectors that are native to the account should revert: > An execution function selector MUST not conflict with native ERC-4337 and ERC-6900 functions. While it is checked for 4337 selectors, the native check is not performed. The assumption is that these selectors will not be invoked, as the native selectors will always be invoked instead. There is an extreme technical edge case, where a collision-clashed selector would suddenly become available in case of an account upgrade, where the native function was removed. Mainly, we want to point out the slight deviation from the standard.

Open the report
LowReduce Possibilities of Execution Reverts for `NativeTokenLimitModule `Alchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

The `NativeTokenLimitModule` provides a pre-execution hook that can use up the allowance for native token transfers under certain execution functions. As native transfers are rolled back in a reverting execution phase, it is a check that is correctly performed not as part of the validation phase, but in the execution phase, so that the spending limit is not changed in case a user operation, containing a native token spend, reverts in execution. However, this enables the possibility of predictable reverts in the execution phase, as the details of the native token spent remain fully unchecked in the validation phase. The possibility of execution reverts due to exceeded limits could be reduced, at least if only a single UserOp of that account is being bundled, with this slightly more restrictive approach: * Add a pre-validation hook to the module that reverts in case the checks currently performed in the `preExecutionHook()`are failing. Remove the current `preExecutionHook()` module and replace it with a validation associated post-only execution hook that performs the state change on the `limit` value. * Place the `preUserOpValidationHook()` that is doing gas limit checks on the account into a separate `GasLimitModule`. While this requires the configuration of two separate native token limit values (gas limit and token transfer limit), we deem this to be an improvement to the design.

Open the report
LowSender-Parameter in Certain Cases Will Always Be Entrypoint Rather than `UserOp.sender`Alchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

All execution hooks in UserOperation settings encode the EntryPoint as the `sender`, rather than the `sender` specified in the UserOperation struct. Regular and validation-associated execution hooks are systematically invoked such that `msg.sender` resolves to the EntryPoint, thereby standardizing the context from which these hooks are triggered. This approach ensures uniform handling of sender identity across different execution paths, reinforcing the design intent to clearly signal the operational context to the module. This is consistently applied in both direct and deferred validation processes, where the EntryPoint remains the `sender` even during runtime validations and deferred actions like signature validation. Such consistency supports the integrity of operational flows and minimizes confusion regarding the origin of execution hooks. It is recommended to document this behavior explicitly to help developers understand that this pattern is a feature of system design, aimed at providing clarity and uniform context for execution hooks. Acknowledging this in the official documentation will aid in setting correct expectations and guide proper module implementation.

Open the report
Show 10 more
LowUnenforced Off-Chain Checks for Modules and Modular AccountAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

While technically not vulnerabilities, multiple risks in global validation and hook management create risks of unauthorized access, incomplete uninstallation, and potential account bricking: 1. Global validation modules inherently have access to sensitive native functions (e.g., `executeBatch()`, `upgradeToAndCall()`). As these functions have high privileges, end users must exercise caution when assigning validation modules globally. 1. Pre-runtime validation hooks do not necessarily need to be addresses with code and would pass for all parameters, as a low level `call()` is made without any `codesize` checks or return data checks 1. Execution hooks may not be fully removed during uninstallation, leaving residual hooks on fully removed selectors. 1. The last validation function, given sufficient privileges, can be uninstalled, which could effectively brick the account.

Open the report
LowUnrelated Deferred Actions Could Be Dropped by Bundler to Apply Unused Gas Penalty to AccountAlchemy - Modular Account V2 · Quantstamp · 6 Dec 2024Acknowledged

If some UserOperation were to be expected to include some independent deferred action that is not necessary for a passing validation phase, a bundler could drop that deferred action and making the account pay the 10% penalty for the unused gas that was intended to be consumed by the deferred action. However, as there is limited upside here and as deferred actions are most likely always needed to be included for such a UserOperation, this is only informational.

Open the report
LowContract-Owners Can Reuse Signatures From Other Contract-Owners of Same `UserOp`Alchemy - Multisig Plugin · Quantstamp · 29 Mar 2024Acknowledged

In the case of a contract owner signature, the `s` value points to the offset of the start of the specific signature of the contract owner. However, unlike the underlying addresses, the offsets are not enforced to be in ascending order. This means that two contract owners can technically share the same signature. It should of course be noted that the underlying contract addresses that those two separate signature validation calls are performed upon still have to differ, but digest and signature can be shared.

Open the report
LowPotential Size Limitation on `UserOp.signature`Alchemy - Multisig Plugin · Quantstamp · 29 Mar 2024Acknowledged

Currently, all the signatures are encoded into the `userOp.signature` field, including both EOA and 1271 contract-based signatures. We could not find any current limitations on the signature field size at this iteration of bundlers, currently the only limitation is the overall block size limit. It could be possible that bundlers will add a limit on that field, especially given the 1271 contract signatures can be of arbitrary size. The current [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) specification states the following: > When a client receives a `UserOp`, it must first run some basic sanity checks, namely that: The `verificationGasLimit` is sufficiently low (<= `MAX_VERIFICATION_GAS`) ... The statement is saying there can be a limit on `verificationGasLimit` (to protect bundlers from being DoS'ed). And because the size of `userOp.signature` contributes to the verification gas, we can assume the bundler may drop a `UserOp` with a signature that is too large.

Open the report
LowThe Signature Has No Expiry TimeAlchemy - Multisig Plugin · Quantstamp · 29 Mar 2024Acknowledged

The plugin adds the ability to have multiple owners sign a `UserOp` to be executed. This is done via an ECDSA signature for an EOA or a 1271 signature from a smart contract. The signatures are then assembled together and submitted along with the `UserOp`. In the case of an EOA signature, there currently is no expiry time on the validity of the signature, so if the nonce has not been used yet, this signature is valid for the specific `UserOp` for an indefinite amount of time.

Open the report
LowDanger Of Faulty Plugin ConfigurationsAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

The MSCA's security fully relies on a proper configuration of trusted plugins. Therefore, end users should exercise extreme caution around installing and even uninstalling plugins. Below is a non-exhaustive list of areas of concern that are based on plugin trust assumptions. 1. The MSCA does not provide any native protection of function selectors (though it natively also can not properly spend native assets and tokens). So uninstalling plugins could incorrectly loosen access around certain functionality, for example, if the `MultiOwnerPlugin` were to be uninstalled without any replacement protecting the same selectors to the same degree. 2. A malicious plugin can block access to arbitrary selectors, as outlined in MSCA-5. 3. Furthermore, plugins should always be assured to not be upgradeable, or else unexpected functionality could be introduced via registered validation and hooks. 4. An installation of a malicious or even faulty plugin could cause arbitrary consequences, including loss of funds. 5. Installing plugins that add execution hooks to selectors increases the chances of a validation passing, but reverting user operation execution. While this is a desired design aspect of execution hooks, the Entry Point contract will assure that a Bundler is reimbursed, either by a paymaster or the paying account, for validated user operation, regardless of the success of the execution. Therefore, plugins adding execution hooks to selectors add a level of risk to the account. 6. Uninstalling a plugin providing a certain selector and installing another one using the same selector can cause unforeseen behavior, as other plugins from the prior installation might still have hooks and validation associated with the replaced selector 7. To upgrade the MSCA, one would need to call `upgradeToAndCall()`, and it is up to the user to either uninstall or not the plugins, depending on the severity of the upgrade. So, it is possible not to uninstall plugins and have such an upgrade that either adds functions or changes the function selectors on existing functions. This would lead to an uncertain state, as a malicious plugin can pre-register a hook for a non-existing selector that would be added at an upgrade and hence hijack execution.

Open the report
LowExtra Care Needs to Be Taken if the Project Is to Be Deployed on ZK-Based ChainsAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

ZK-based EVM, such as zkSync or Scroll, implement their own version of Ethereum Virtual Machine and are EVM compatible but not to the full extent. There are some opcodes that are not supported or some opcodes that behave differently from what happens on the mainnet. If you plan to deploy on a ZK-based chain, it is important to take these into account, particularly the following opcodes in the case of **[zkSync](https://era.zksync.io/docs/reference/architecture/differences-with-ethereum.html)**: 1. `CREATE2` 1. > To guarantee that `create`/`create2` functions operate correctly, the compiler must be aware of the bytecode of the deployed contract in advance 2. `MSTORE, MLOAD` 1. > Unlike EVM, where the memory growth is in words, on zkEVM, the memory growth is counted in bytes. For example, if you write `mstore(100, 0)`, the `msize` on zkEVM will be `132`, but on the EVM, it will be `160`.

Open the report
LowMalicious Owner Could Front-Run `updateOwners()` CallsAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

In the `MultiOwnerPlugin`, an MCSA can have multiple owners that each have equal weighting in terms of execution power. This brings advantages of multiple users or entities sharing the account but also complexities such as the one described below. Due to the nature of equal weighting, in the case where one owner's account is compromised or turns malicious, another benign user would attempt to remove the malicious one from the owner list via `updateOwners()`. Theoretically, that would work, but due to the blockchain nature, the malicious entity can monitor the memory pool and front-run the `updateOwners()` with `updateOwners()` of their own, removing the benign owner, hence causing their removal to revert due to lack of permissions. This leads to a race condition style issue in the plugin.

Open the report
LowMemory Allocated by `_allocateTempKeyBuffer` Is Fragile and Could Be OverwrittenAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

The `AssociatedLinkedListSetLib` hashes a composite key to store and retrieve items linked to an account. Given the frequency of this operation, the library employs the `_allocateTempKeyBuffer()` method to create a memory buffer once and only update it when necessary, all in assembly. However, the `_allocateTempKeyBuffer()` function does not update the free-memory pointer, meaning Solidity remains unaware of any memory changes. This could result in the buffer being mistakenly overwritten. Although the current code seems to avoid erroneous overwriting, the design is quite brittle. Even minor modifications could disrupt memory, potentially breaking the code.

Open the report
LowPlugin Metadata Can Be Misleading to End UsersAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

According to the code documentation, the `PluginMetadata` struct contains view information intended for front-end clients. However, it is critical to note that this data is not validated. Plugin authors can input any `name`, `version`, `author`, and `permissionDescriptors` they choose. Depending on the usage of this information, malicious developers could potentially mislead MSCA users into installing harmful plugins that resemble legitimate ones.

Open the report
Show 7 more
LowPotential Risks of Dependency MismatchAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

During installation, the dependencies of the being installed plugin are checked against the `dependencyInterfaceIds` in the plugin's manifest. The MSCA ensures that each dependency contract supports the given interface via ERC-165. This approach provides a basic validation of the contracts. However, there are several existing limitations: 1. If the interface of a dependency is provided as `IPlugin` (as in the session key plugins), we can only ensure the dependency contract is a plugin without knowing what type of plugin it is (e.g., `IMultiOwnerPlugin`). 1. The MSCA does not validate that the provided `functionID` is valid for the given dependency. 1. Two plugins can implement the same plugin interface while being different on the function ID order or implementation.

Open the report
LowPotential Violation of ERC-4337 Storage Access RulesAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

The plugins rely heavily on the `AssociatedLinkedListSet` or `PluginStorageLib` libraries to calculate the storage slots associated with an MSCA. A slot is calculated as `keccak256(address || batchIndex || input1 || input2)`, while `input2` is optional. Therefore, the data appended to the `address` is 64 or 96 bytes long. However, according to the latest ERC-4337 specification at the time of writing (commit `8dd085d`), the document does not explicitly specify the length of the appended data can or should be. It is mentioned that slots of type `keccak256(A || X) + n` are to cover mappings with the wallet address as the key, thereby implying that the acceptable range of `X` should at least include 32. ([reference](https://eips.ethereum.org/EIPS/eip-4337#storage-associated-with-an-address)) The [draft EIP-7562](https://github.com/ethereum/ERCs/pull/105) specifies that the appended data should be 32 bytes long (see point 5). On the other hand, the Infinitism bundler allows the data lengths between `[0, 480]` bytes. ([reference](https://github.com/eth-infinitism/bundler/blob/ba78f2067e33df399bb7d34a9d1f972834d23c80/packages/validation-manager/src/BundlerCollectorTracer.ts#L324-L327))

Open the report
LowSession Keys Require Explicitly Specifying a Spend Limit for Callable ERC-20 ContractAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

If a session key's access control is configured to interact with an ERC-20 token contract, it could consume the MSCA's entire balance and allowances if a spending limit is not also specified along with the access. This is because contract calls are only checked for token transfers and allowances if `tokenContractData.isERC20WithSpendLimit` has specifically been set to `true`, which is only set if the spending limit is explicitly set to a value smaller than `type(uint256).max`. Not performing this explicit update, but enabling the session key to interact with the contract, will therefore act as an unlimited spend limit for that token.

Open the report
LowUninstalling the Owner Plugin May Potentially Brick the AccountAlchemy Modular Account · Quantstamp · 30 Jan 2024Acknowledged

The MSCA specification mentions that uninstalling the owner plugin would allow anyone to take over the account. However, this is not the case according to the current implementation, as the `_doRuntimeValidation()` function only allows self-calls when the runtime validation function of a selector does not exist. The `_doUserOpValidation()` function reverts if the corresponding user operation validation function does not exist. As a result, an MSCA without the owner plugin will be uncontrollable, and funds will be locked in the account. Technically, an installed plugin with the self-call capability can self-call the MSCA to install or upgrade the implementation, which, however, even performing self-calls requires an initially provided and passing runtime validation in the base-call.

Open the report
LowMultiple on-Chain Validations Can Fail Due to Self-Destructed External ContractAlchemy Light Account · Quantstamp · 27 Sep 2023Acknowledged

Before a bundler submits the set of bundled user operations as a transaction to the entry point, they (again) simulate the validation to reduce the likelihood of performing on-chain, failing operation validations. The integration with `ERC-1271` signature-validating smart contracts creates an (optional) dependency on an external smart contract that can theoretically be shared between many accounts. During the simulation, i.e. during the `validateOps()` function calls, certain opcodes are banned to make validation almost stateless, with the only exception being storage that is directly linked to the validated account's address. However, during experiments with a local setup including ethinfinitism's [bundler implementation](https://github.com/eth-infinitism/bundler), we found that the validation would not revert if these verifying smart contracts make use of the `SELFDESTRUCT` opcode outside of the `validateOp()` function flow. This is important as a self-destruct on such a shared verifying `ERC-1271` smart contract would invalidate the validations of potentially many accounts at once, without any form of on-chain storage manipulation directly related to the addresses of these accounts. So if an attacker would front-run the block inclusion of a bundle with a self destruct of a contract that is used in many of the bundled user operations as the verifying contract, the bundler could have many failing on-chain validations, causing a financial loss to the bundler. Following the possibility of the `SELFDESTRUCT` opcode, the issues could be made worse in the following scenario. If the contract registered as the `owner` in many accounts was deployed via `CREATE` by an intermediary contract that was instantiated via `CREATE2`, a metamorphic contract attack would be possible, similar to the recent Tornado Cash exploit, where the verifying smart contract is replaced with a contract with arbitrary code. A possible attack vector here would be to replace the `validateSignature()` function with a gas-consuming function that performs a series of costly memory operations, causing all functions to consume the maximum designated `verificationGasLimit` without enabling validation to pass, causing a financial loss in un-reimbursed gas costs to the bundler. Furthermore, such a metamorphic verifier could also call/re-enter all `onlyOwner()` protected functions of the contract. If `_authorizeUpgrade()` would ever be overwritten to enable upgrades, this would become a concern. Note that this is a general issue that can occur when interacting with metamorphic contracts, and the associated risk is not limited to the use of `ERC-4337`. This attack also requires the bundler to include a user operation in a bundle despite the presence of the `SELFDESTRUCT` opcode outside of the validation trace in the simulation. This is a point that was also raised in a [blog post by Vitalik Buterin](https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a) and is more of an issue in the bundler simulation than in these contracts. We, however, still want to raise awareness that the set of contracts enables a possible function flow that can cause the bundler some losses.

Open the report
LowPotential Security Implication of Indefinite Validity for EOA Owner-Signed User OperationsAlchemy Light Account · Quantstamp · 27 Sep 2023Acknowledged

During the validation process of a user operation, the `LightAccount` wallet initially verifies whether the wallet's `owner` has signed the provided signature. If this verification succeeds, the `_validateSignature()` function returns a result of 0, indicating the signature is correct and valid, without imposing any time-based restrictions. However, this approach results in the user operation's signature remaining valid indefinitely once the EOA owner has signed it. This indefinite validity could expose users to specific attack vectors. For example, if a bundler behaves maliciously, they could withhold a user operation after receiving it from the user and submit the bundling transaction when there is a favorable profit opportunity. Delays in the bundling transaction, possibly caused by congestion in the bundler's alternate mempool, could create similar issues for users. It's worth noting that there exists a method for the EOA owner to manually invalidate a signature - by signing another user operation with the same nonce and executing it before the previous one.

Open the report
LowUnlocked PragmaAlchemy Light Account · Quantstamp · 27 Sep 2023Acknowledged

Every Solidity file specifies in the header a version number of the format `pragma solidity (^)0.8.*`. The caret (`^`) before the version number implies an unlocked pragma, meaning that the compiler will use the specified version _and above_, hence the term "unlocked".

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="alchemy-erc"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us