Smashing ERC4337 Wallets For Fun and Profit – Intro

Smashing ERC4337 Wallets For Fun and Profit – Intro

The ERC4337 standard is something I’ve been coming across more in auditing projects. And it seems to be the direction we are moving towards to allow for mainstream adoption of web3.

This is because externally owned accounts (or wallets) as we know them, are frustrating for the average user to maintain. It is hard to continually adhere to security standards, and this makes the user experience full of friction.

I can’t begin to tell you the number of Metamask wallets that got compromised because the owner had the seed phrase in a text document.

And when we consider that a user losing access to their system, while not having the seed phrase backed up, means the loss of ALL the funds in the wallet, it gets really scary!

The ERC4337 standard attempts to mitigate this issue by providing an abstraction layer that makes interacting with web3 much smoother than it is now.

In this ongoing series, I will give a technical explanation of how this is supposed to work and share my own checklist of what to look for while auditing these projects.

I will be referencing this EIP document as we go.

Let’s assume that Alice wants to interact with the blockchain. Instead of using Metamask, Alice decides to use a service that implements ERC4337. Let’s call that service smart-wallet

Alice use’s smart-wallet interface to create an account. Now, to do that Alice needs to provide some sort of a seed. This can be anything from a Twitter handle to a random salt.

The salt is then used by the smartWallet factory’s contract to deploy a new instance of the wallet contract implementation to be used by Alice. The contract usually sets behind a proxy to allow for upgradability.

Under the hood, the factory uses create2 in order to know the address of the deployed contract beforehand. This also gives us the benefit of checking if a wallet exists at that address by providing a seed parameter.

For instance, if we provide the same Twitter handle that Alice provided, we will get the contract deployed for Alice.

This also prevents us from creating a wallet for a seed that already exists. Here is an example code:

We fetch the address using the salt parameter the user provides, and then we check if there is a contract at that location; by checking the codeSize the user provides.

Here is the part where we use create2:

We use the salt, the creation code of the ERC1967 proxy, and the address of our smart wallet implementation.

If there is code at that address, then this must mean the salt is already used, and there is no need to redeploy a proxy at that address again.

Of course, this opens the door for a front-running attack, where an attacker can target a victim and just keep using the same seed to deny the honest user’s account creation. More on that later.

But how is this function called in the first place? Good question!

This function is called by EntryPoint contract, the code of the ERC4337 standard.

In the coming part, we will dive deep into that contract and outline its main flows. Stay tuned!