Patch Thursday — Account Abstraction Security Guide

ChainLight
ChainLight Blog & Research
16 min readNov 16, 2023

Summary

In the previous article, we discussed the components and operational structure of account abstraction and its associated security considerations. In this article, we will analyze and categorize vulnerabilities discovered during security audits of projects implemented using account abstraction. Based on these findings, we will introduce considerations that projects based on account abstraction should prioritize.

Security Considerations of AA Projects

ChainLight has analyzed security audit reports for projects that are either built on or planning to implement ERC4337, and has compiled a list of essential considerations for projects using account abstraction based on these reports. In this article, we referenced OpenZeppelin’s audit report on ERC4337 reference implementation, and other reports of projects like Biconomy, Zerodev Kernel, and Ambire, which have undergone detailed security audits.

1. Gas Fee Calculation Logic

One of the key features that an ERC4337-based project can offer is paying gas fees on behalf of users through Bundlers and letting Paymaster reimburse the consumed fees to Bundler when the transaction succeeds. Therefore, a vulnerability that makes the malicious Bundler to manipulate the gas fee reward or an innocent Bundler unable to receive a proper payment would lead to the financial loss of participating entities.

Incorrect Calculation of Gas Repayment

Since gas fee manipulation vulnerabilities can lead to fund thefts, handling gas fee reimbursement logic should be approached with caution. In Biconomy, accounts provide compensation for the Relayer (similar to ERC4337’s Bundler) who submits transactions on behalf of users, which was designed to increase with the size of calldata. However, a vulnerability allowed attackers or malicious Relayers to arbitrarily increase compensation by adding zero bytes to calldata.

In this vulnerability, when the Relayer submits a transaction, accounts pay 8 gas per msg.data.length (complete calldata). However, EVM consumes only 4 gas for zero bytes in calldata, while spending 16 gas for non-zero bytes. Thus, whenever Relayer adds zero bytes to calldata, it gains 4 additional gas (8 gas as compensation — 4 gas of actual consumption cost). This allows attackers to receive additional compensation by adding a bunch of zero bytes to calldata, draining the account’s asset. This was implemented before the release of Infinitism’s ERC4337 standard implementation. In the reference implementation, the compensation to the Bundler is determined by maxFeePerGas, maxPriorityFeePerGas, and preVerificationGas parameters of UserOperation, so such manipulation is now infeasible. However, it is essential to be cautious when setting these parameters too high, as this could result in unnecessary high gas compensation paid to the Bundler by user accounts.

Gas Estimation Error of Bundler

If the gas estimation performed by Bundler during simulation is lower than the actual gas cost required for on-chain execution, the transaction will fail with an out-of-gas error when executed with the estimated gas amount. In the previous implementation of ERC4337 of Infinitism, a vulnerability existed where gas estimation in the simulation phase was lower than the actual execution in an on-chain environment due to the cold storage access during the simulation phase. In this vulnerability, Bundler calls _validateAccountPrepayment() with a value of 1 as an argument for the address aggregator. During this process, Bundler retrieves the address of the sender account’s aggregator from the storage of the sender account. Since the storage of the sender account is accessed for the first time(cold access) in the simulation phase, fewer gas costs are incurred(EIP-2929) when calling validateUserOp() for the sender account’s UserOperation validation as this is warm access. As a result, gas usage between the simulation and the actual execution environment differs, and the transaction can fail due to out-of-gas errors in on-chain execution. To prevent this, Bundlers must ensure that there are no discrepancies in gas measurement between the simulation environment and on-chain execution, especially when designing the EntryPoint directly.

2. Signature Generation & Usage

Correctly generating and verifying signatures is indeed crucial. If signature verification is not performed correctly, attackers may impersonate an account and execute arbitrary transactions, which can lead to severe issues such as the theft of user funds.

Insufficient verification of generated signatures

In Biconomy’s SmartAccount contract, a vulnerability existed where attackers could create arbitrary transactions bypassing the signature verification. The SmartAccount contract supported EIP1271-based signature transactions through the execTransaction() function. Still, it lacked the validation process to check if the input signature in the checkSignatures() function belonged to the contract owner.

// https://github.com/code-423n4/2023-01-biconomy/blob/8a7b05ac58a65727e7e1fb17b91e418bc372be2b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L302-L353
function checkSignatures (…) {

_signer = address(uint160(uint256(r)));
require(ISignatureValidator(_signer).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "BSA024");

}

Since the signer does not verify if the signer is the owner of the account, attackers could specify an attack contract address for the signer and make isValidSignature() always return EIP1271_MAGIC_VALUE. Therefore, the attacker can create arbitrary transactions and have them executed by the account. This could lead to attacks such as stealing funds from the account, destroying proxy contracts through selfdestruct(), and usurping control over wallets through updates to implementation contracts. The attacker could deploy a new implementation to steal all funds from the account or even unstake funds from the protocol. Since signature verification errors in ERC4337-based accounts can lead to fund theft, special attention is required to ensure that there are no errors in signature verification.

Infinitism’s implementation had issues due to a flaw in the signature verification mechanism. BLSSignatureAggregator contract provides a mechanism where the Bundler verifies each signature of UserOperation before composing a bundle. Then Bundler generates a combined signature of successful UserOperation off-chain, which can be verified all at once, within the on-chain environment by the EntryPoint. However, there was a vulnerability that made individual signature’s verification to pass but combined signature’s verification to fail.

validateUserOpSignature() in the code below calls getUserOpPublicKey() to get publicKey of UserOperation. GetUserOpPublicKey() retrieves publicKey depending on the existence of initCode. If two publicKeys are not the same, the verification of combined UserOp may fail even though individual UserOp’s verification succeeds. In this case, Bundler cannot retrieve the gas fee as the submitted transaction reverts with the verification failure. Moreover, as it is assumed that the aggregator has reverted Bundler’s successful transaction, this can harm the aggregator’s reputation and lead to the rejection of the other UserOp that utilizes certain aggregators.

// https://github.com/eth-infinitism/account-abstraction/blob/6dea6d8752f64914dd95d932f673ba0f9ff8e144/contracts/bls/BLSSignatureAggregator.sol#L123-L131
function validateUserOpSignature(UserOperation calldata userOp)
… (bytes memory sigForUserOp) {
uint256[2] memory signature = abi.decode(userOp.signature, (uint256[2]));
uint256[4] memory pubkey = getUserOpPublicKey(userOp);
uint256[2] memory message = userOpToMessage(userOp);
require(BLSOpen.verifySingle(signature, pubkey, message), "BLS: wrong sig");
return "";
}

// https://github.com/eth-infinitism/account-abstraction/blob/6dea6d8752f64914dd95d932f673ba0f9ff8e144/contracts/bls/BLSSignatureAggregator.sol#L20-L27
function getUserOpPublicKey(UserOperation memory userOp) … (uint256[4] memory publicKey) {
bytes memory initCode = userOp.initCode;
if (initCode.length > 0) {
publicKey = getTrailingPublicKey(initCode);
} else {
return IBLSAccount(userOp.sender).getBlsPublicKey();
}
}

Use of Unsigned Variables

Biconomy’s SmartAccount contract has a handlePayment() function that makes the account pay for the gas fee to the Relayer who submitted the transaction. This function uses variables related to gas, such as baseGas, gasPrice, and gasToken, which are closely related to the gas amount the account pays. However, there was a vulnerability that allowed one of the arguments, tokenGasPriceFactor, to be arbitrarily set by the Relayer. The final payment, which accounts must refund to the Bundler, is determined by tokenGasPriceFactor, as seen in the handlePayment() code. Attackers could manipulate the value of tokenGasPriceFactor to charge accounts more than the actual gas cost, potentially leading to the theft of funds.

// https://github.com/code-423n4/2023-01-biconomy/blob/8a7b05ac58a65727e7e1fb17b91e418bc372be2b/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L247-L269
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
uint256 tokenGasPriceFactor,
address gasToken,
address payable refundReceiver
) private nonReentrant returns (uint256 payment) {
// uint256 startGas = gasleft();
// solhint-disable-next-line avoid-tx-origin
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
// For ETH we will only adjust the gas price to not be higher than the actual used gas price
payment = (gasUsed + baseGas) * (gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
(bool success,) = receiver.call{value: payment}("");
require(success, "BSA011");
} else {
payment = (gasUsed + baseGas) * (gasPrice) / (tokenGasPriceFactor);
require(transferToken(gasToken, receiver, payment), "BSA012");
}
// uint256 requiredGas = startGas - gasleft();
// console.log("hp %s", requiredGas);
}

3. Reuse of Signatures

The reuse of signatures that allow sending the same UserOperation repeatedly within a short period can lead to depleting Paymaster’s funds and significantly reduce the usability of the participating components. Therefore, ERC4337-based projects must carefully implement verification on the reuse of signatures on UserOperations and transactions.

Setting Arbitrary Validator via Reuse of UserOperation Signatures

In the Zerodev Kernel’s KillSwitchValidator contract, the validateUserOp() function verifies the validity of UserOperation and plays a role in specifying the validator. A process that sets a new validator takes place when the length of signatures in UserOperation is 71, which is shown in the code below:

// https://github.com/zerodevapp/kernel/blob/199ae7d838f21b37f069267c86e8eeb6f0175a69/src/validator/KillSwitchValidator.sol#L43-L80
function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256) external payable override returns (uint256) {
KillSwitchValidatorStorage storage validatorStorage = killSwitchValidatorStorage[_userOp.sender];
uint48 pausedUntil = validatorStorage.pausedUntil;
uint256 validationResult = 0;
if (address(validatorStorage.validator) != address(0)) {
// check for validator at first
try validatorStorage.validator.validateUserOp(_userOp, _userOpHash, pausedUntil) returns (uint256 res) { // ECDSAValidator
validationResult = res;
} catch {
validationResult = SIG_VALIDATION_FAILED;
}
ValidationData memory validationData = _parseValidationData(validationResult);
if (validationData.aggregator != address(1)) {
// if signature verification has not been failed, return with the result
uint256 delayedData = _packValidationData(false, 0, pausedUntil);
return _packValidationData(_intersectTimeRange(validationResult, delayedData));
}
}
if (_userOp.signature.length == 71) {
// save data to this storage
validatorStorage.pausedUntil = uint48(bytes6(_userOp.signature[0:6]));
validatorStorage.validator = KernelStorage(msg.sender).getDefaultValidator();
validatorStorage.disableMode = KernelStorage(msg.sender).getDisabledMode();
bytes32 hash = ECDSA.toEthSignedMessageHash(keccak256(bytes.concat(_userOp.signature[0:6], _userOpHash)));
address recovered = ECDSA.recover(hash, _userOp.signature[6:]);
if (validatorStorage.guardian != recovered) {
return SIG_VALIDATION_FAILED;
}
return _packValidationData(false, 0, pausedUntil);
} else {
return SIG_VALIDATION_FAILED;
}
}

The vulnerability arises as the data for the validatorStorage, which stores information about validators for UserOperation, can be updated by msg.sender. In other words, even if userOp.sender and msg.sender are different, msg.sender (the attacker) can specify killSwitchValidatorStorage[_userOp.sender].validator to any address of their choice.

Let us assume that the attacker uses a previously used UserOperation to call validateUserOp(). When the validator is not specified, the first condition, if (address(validatorStorage.validator) != address(0)), will pass as is. Even if the validator already exists, it does not trigger a revert for the already used UserOperation but assigns SIG_VALIDATION_FAILED to the validationResult. The important point here is that after this, in the second if condition, the attacker can set the address specified by the attacker as validatorStorage.validator, after checking the signature length of UserOperation. Since the previous UserOperation is reused, the attacker can pass through the condition about validatorStorage.guardian. In this way, the attacker can use the newly set validator by the attacker to arbitrarily pass validation for any UserOperation. As a result, this can lead to ownership or fund theft of the wallet.

Reuse of Signatures for the Paymaster

A vulnerability has been discovered in Biconomy’s Upgrader contract, where an attacker could upgrade the implementation of an account to a malicious one using delegatecall. The attacker could upgrade the account to a contract that performs the same functions as the original SmartAccount contract without nonce verification. With this, the attacker can use the same Paymaster signature they previously used on an account to repeatedly send the same transaction, depleting the Paymaster’s funds in the process. In response to this, Biconomy has added checks for replay attacks at the EntryPoint and has modified the contract by adding a NonceManager to handle nonce verification, which was previously handled by the account itself.

Projects using account abstraction should ensure that proper nonce verification is implemented when trusting certain EntryPoints. Additionally, it is advisable to minimize Paymaster’s trust dependencies on the accounts.

In the ERC4337 standard implementation of Infinitism, a vulnerability allowed the reuse of signatures for the Paymaster. The VerifyingPaymaster contract, which validates the UserOperation for the Paymaster, computed the hash of the UserOperation as shown in the code below and performed signature verification on it. However, the signature data did not include the address of a specific Paymaster or a chain ID, making it possible to reuse this on a different chain or with a different Paymaster having the same verifyingSigner.

// https://github.com/eth-infinitism/account-abstraction/blob/6dea6d8752f64914dd95d932f673ba0f9ff8e144/contracts/samples/VerifyingPaymaster.sol#L36-L50
function getHash(UserOperation calldata userOp) public pure returns (bytes32) {
return keccak256(abi.encode(
userOp.getSender(),
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas
));
}

// https://github.com/eth-infinitism/account-abstraction/blob/6dea6d8752f64914dd95d932f673ba0f9ff8e144/contracts/samples/VerifyingPaymaster.sol#L56-L75
function validatePaymasterUserOp(…) … {
bytes32 hash = getHash(userOp);

if (verifyingSigner != hash.toEthSignedMessageHash().recover(paymasterAndData[20 :]) {
return ("",1);
}
}

Infinitism addressed this problem by revising getHash() function to include block.chainid, the address of Paymaster contract, and the expiration of the signature.

Reuse of Signed Transactions

The execTransaction() function within Biconomy’s SmartAccount contract, responsible for executing transactions, calculates the hash of a signed transaction when it is sent, including nonces[batchId]. Still, it does not verify the batchId itself, allowing the setting of arbitrary batchId. Exploiting this, an attacker could reuse the same transaction multiple times for different batchIds that share the same nonce value (nonces[batchId]).

// https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L192-L245
function execTransaction(
Transaction memory _tx,
uint256 batchId,
bytes memory signatures
) … {

bytes32 txHash;
{
bytes memory txHashData =
encodeTransactionData(
_tx,
refundInfo,
nonces[batchId]
);
nonces[batchId]++;
txHash = keccak256(txHashData);
checkSignatures(txHash, txHashData, signatures);
}

}

As a result, for a specific nonce value, there can be up to ²²⁵⁶ possible batchId values, allowing the same transaction to be practically executed an infinite number of times. In this case, it is important to prevent the generation of duplicate transactions with the same batchId included during the transaction hash calculation process.

4. Front-running

In the ERC4337 standard, the handleOps() function processes multiple UserOperations bundled together in an array. During this process, if one UserOperation fails, the entire transaction gets reverted. Let us assume a Bundler submits a transaction and an attacker uses front-running to execute the last UserOperation in the UserOperation array first. The attacked Bundler verifies UserOperations up to the last UserOperation, consumes gas for them, and then fails at the last UserOperation, causing the entire set of UserOperations to be reverted. Consequently, the victim spends most of the gas fees for the UserOperation without compensation, and cannot receive payment due to the transaction failure.

To prevent this, Bundlers should take measures against front-running. Therefore, Bundlers should either be block miners who can position their UserOperation bundle as the first transaction in a block or utilize infrastructures like MEV-boost. In addition, Bundlers should check the validity of their UserOperations in each block. Layer 2 blockchains, where sequencers are the only entities that can create blocks, should support RPC endpoints for Bundlers, as mentioned in the previous article.

In Ambire, a problem has been discovered where a Relayer can forcibly revert a transaction that is executed through a specific function via front-running. The AmbireAccount contract contains a function called tryCatch(), which invokes UserOperation with a low-level call. As can be seen in the code below, the tryCatch() function is implemented in a way that it does not revert even if the call fails. Thus, Relayer could be rewarded for the transaction submission even if the UserOperation fails to execute.

// https://github.com/AmbireTech/wallet/blob/1ef5b7208e906be4287673746f15418984e78bc0/contracts/AmbireAccount.sol#L81-L87
function tryCatch(address to, uint256 value, bytes calldata data) external payable {
require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
(bool success, bytes memory returnData) = to.call{ value: value, gas: gasleft() }(data);
if (!success) emit LogErr(to, value, data, returnData);
}

The problem is that Relayer could intentionally fail the UserOperation and get the reward by manipulating the gas limit of the transaction. Ethereum introduced changes to its gas fee policy through the EIP150 hard fork, which introduced the “1/64 rule”. This means 1/64 of the gas is reserved for the call context itself, and the rest (gasLeft — gasLeft * 1/64) is passed to the called function. In other words, even if the called function(child) runs out of gas due to internal operations, there is some gas left for the caller(parent) to complete its execution, consequently not reverting the transaction.

Malicious Relayers or entities who can front-run transactions can exploit this behavior to disrupt the operation of accounts. When a user account intends to submit a transaction through the tryCatch() function, the calls will occur in the following sequence:

  1. Call of the execute() function of the user account.
  2. Call of the tryCatch() function.
  3. Call of the function in the destination contract.

An attacker can precisely set gas limits when submitting a transaction that makes the call to the destination contract’s function fail due to gas depletion, while allowing the operation after tryCatch() to succeed. In other words, transaction submission takes place, but the account’s intended behavior will not be executed.

To address this issue, Ambire has modified the contract to check the remaining gas before and after the low-level call in the tryCatch() function and revert the transaction if the remaining gas after the call is insufficient. In contrast to the Ambire’s implementation, Infinitism’s ERC4337 reference implementation reverts the transaction if the call for UserOperation fails due to an out-of-gas error.

5. ERC4337 Compliance

Within the Biconomy, several parts existed that did not comply with the ERC4337 standard. There were cases where the logic specified in ERC4337 was not implemented in the correct order (createSenderIfNeeded()), was incorrectly implemented (validateUserOp()), or was not implemented at all (Aggregator validation).

Furthermore, a case implemented modifier functions that could prevent the standard’s intended behavior. For example, the execute() and executeBatch() functions in Biconomy’s SmartAccount contract had an onlyOwner modifier, limiting interactions with the EntryPoint.

In this case, the EntryPoint contract cannot call these functions, making user accounts unable to interact with the EntryPoint contract. Account abstraction based on ERC4337 should allow transactions to execute based on the UserOperations specified by the user accounts. These kinds of features should be removed, as they contradict the original design intent and limit the scope of transaction behavior.

Zerodev Kernel also had vulnerabilities due to non-compliance with the ERC4337 standard. According to the standard, the Zerodev Kernel’s ECDSAKernelFactory contract should stake a certain amount of ETH at the EntryPoint to access storage. If no funds are staked in the EntryPoint, Bundlers would not execute transactions with UserOperations utilizing the Factory. In the case of Zerodev Kernel, the issue was found where Bundlers did not stake at the EntryPoint they should access.

Failure to comply with the standard may lead to unexpected bugs or incompatibility with other protocols during future interactions. Since different entities can operate the components of account abstraction, such as Bundler, Paymaster, EntryPoint, and Wallet Factory, projects must carefully verify compliance with the ERC4337 standard during implementation.

Furthermore, an issue in the Zerodev Kernel could lead to unintended behaviors during interactions with external libraries and standards. In the SessionKeyValidator contract within Zerodev Kernel, the validateUserOp() function is responsible for validating UserOperations using a Merkle Proof mechanism, which is implemented to verify using an external verification library. This function returns the result of MerkleProofLib.verify(), and the library is implemented to return 1 for true and 0 for false. In other words, it returns 1 for a valid UserOperation and 0 for an invalid one.

However, at the EntryPoint, the value 1 is interpreted as SIG_VALIDATION_FAILED, which indicates a signature verification failure. Consequently, a failure in UserOperation verification using Merkle Proof was recognized as a successful signature verification of UserOperation at the EntryPoint, potentially leading to user fund theft. When using a different verification library for UserOperation verification during the project implementation process, it is essential to thoroughly understand potential cases rather than directly using the result value.

Vulnerabilities in Variant Logics

ERC4337 supports a feature called “counterfactual address”, which lets users know the address of the account in advance of the creation and allows transferring funds to the address. Biconomy also supports the counterfactual address feature. However, an issue existed in Biconomy’s, which allowed the caller to point an arbitrary EntryPoint in the creation of the counterfactual address (with the same address to the counterfactual wallet). In this process, EntryPoint’s address was not included in the creation of the account, and only owner and index were used as the salt. Therefore, the attacker could discover the counterfactual wallet address before the user and set a malicious EntryPoint prior to the user. If users pre-funded the account in advance to the creation, the attacker could set the attack contract as EntryPoint and drain the fund inside.

In the ERC4337 standard, users do not need to specify an EntryPoint separately. Instead, the account implementation (SimpleAccount.accountImplementation) leverages only the pre-registered EntryPoint addresses. Therefore, even if an attacker deploys an account, the EntryPoint specified by the attacker will not be used; instead, the pre-registered EntryPoint address will be employed.

6. Implementation Protection

In Biconomy, an issue occurred that allowed attackers to compromise the ownership of the implementation of accounts specified in SmartAccount Factory. If the implementation is not initialized with the deployment of accounts, the attacker could destroy the implementation through selfdestruct() and freeze all the operations of entire accounts pointing to a certain implementation contract. Similar to this case, Ambire’s AmbireAccount contract also lacked the protection of its implementation, which allowed destruction of the implementation by the authorized entity by constructor() or the service itself. Those two cases lead to the fund freeze of the accounts pointing to the implementation.

In the ERC4337 reference implementation of Infinitism, there existed a vulnerability anyone can configure the manager contract with new modules.) where the implementation could be destroyed due to a delegatecall that invokes selfdestruct() within the EIP4337Manager contract. This contract inherits functionality from the GnosisSafe contract, and the issue arose as anyone could add arbitrary modules as shown in the code below. An attacker could add a module that calls a function that performs selfdestruct() and thereby destroy the EIP4337Manager contract.

// https://github.com/eth-infinitism/account-abstraction/blob/6dea6d8752f64914dd95d932f673ba0f9ff8e144/contracts/gnosis/EIP4337Manager.sol#L66-L72
function setup4337Modules(
EIP4337Manager manager //the manager (this contract)
) external {
GnosisSafe safe = GnosisSafe(payable(this));
safe.enableModule(manager.entryPoint());
safe.enableModule(manager.eip4337Fallback());
}

7. Unintended Transaction Failure

Biconomy provides various ways to execute transactions from user accounts, including calling the execFromEntryPoint() or execute() functions through an EntryPoint and executing the execTransaction() function. Biconomy’s SmartAccount contract maintains a fixed batchId 0 for use by EntryPoints. When the Entrypoint executes the account’s validateUserOp(), nonces[0] increases by 1.

batchId 0 should be called only by the EntryPoints. However, there was no restriction in the execTransaction() function that bans other Relayers from usingbatchId 0. If execTransaction() is called with batchId, which is set to 0, the nonce for batchId 0 will increase, and subsequent transactions from the EntryPoint will be reverted by the updated nonce. This unintended transaction failure can lead to the financial loss of Relayers and unincentivize their activities.

As mentioned in the previous article, the implementation of Infinitism also had a vulnerability that could enforce the transaction failure. In the standard implementation of Infinitism, the EntryPoint is designed to receive the reason for the reverts in the form of memory bytes when the transaction reverts in the execution phase of UserOperations. However, this process consumes a significant amount of gas to copy the revert reason with extreme length in the EntryPoint, which could lead to a forced transaction failure. This type of attack could occur during the execution phase of a UserOperation, making it challenging to predict transaction failures during the simulation phase. Likewise, unexpected transaction failures in the EntryPoint can result in economic losses for Bundlers. Therefore, when designing a separate EntryPoint contract, it is essential to be aware of this issue and take it into consideration.

Conclusion

Throughout the article, we discussed the security considerations that account abstraction projects based on ERC4337 must recognize. Currently, ERC4337-based implementations are in its very early stages, and besides the projects mentioned in this article, many promising projects like Gnosis Safe are actively working on ERC4337-related projects. Since many of these projects have not completed security audits, there is still a possibility of new vulnerabilities emerging due to features developed independently by those projects.

Given the various issues that have arisen during the initial implementations of projects developed so far, it is crucial for forthcoming projects to carefully consider the points mentioned in the article during their development.

Reference

About ChainLight

Patch Thursday is ChainLight’s weekly report introducing and analyzing vulnerabilities discovered and reported by our award-winning security researchers. With the mission to assist security researchers and developers in collectively learning, growing, and contributing to making Web3 a safer place, we release our report every week, free of charge.

To receive the latest research and reports conducted by award-winning experts:

👉 Subscribe to our newsletter

👉 Follow @ChainLight_io

Established in 2016, ChainLight’s award-winning experts provide tailored security solutions to fortify your smart contract and help you thrive on the blockchain.

--

--

ChainLight
ChainLight Blog & Research

Established in 2016, ChainLight's award-winning experts provide tailored security solutions to fortify your smart contract and help you thrive on the blockchain