Skip to main content

Road to 4337: Understanding Entrypoint and UserOp Calldata

· 7 min read
Aditya Agarwal

Pre-reeds (<5 min)


If you're new to Account Abstraction or 4337, I recommend reading these first:

I assume you know what smart contracts are and how an EOA triggers a smart contract function.

Introduction


Since Eth Denver in Feb'23, crypto Twitter has gone gaga over Account Abstraction and EIP-4337's impact on the EVM ecosystem.

After talking with multiple web3 devs and mentoring teams at ETH Tokyo Hackathon, we realized it's taking a good chunk of time and effort to grasp the basic concepts of 4337, causing delays in people getting their hands dirty.
We're here to create easy-to-grasp material, with no fluff or annoying tech jargon, at least not without clarifying it in an easy-to-understand manner.

So let's get started.

Overview


Anyone wishing to build on 4337, first needs to understand the actual data submitted for a 4337 transaction. This post is dedicated to achieving the same.

The tutorial is basically split into two sections:

  1. Understanding the data fields representing an individual User Operations (UserOp)
  2. Understanding the metadata (bundle) representing the actual on-chain transaction

Sample Reference Transaction

We will refer to the transaction (aka bundle) 0x7ed2...204 submitted to the Polygon chain on 15th May. We chose this bundle as it has more than 1 userOp and paymasters are used.
Explore it on PolyScan or On JiffyScan.

The two UserOp Hashes in the bundle are:

  1. 0xfa44f8d690560a97b5dac43487ce98773a11c26dce6fd5fb3891ba6251d553e5
  2. 0xd44c9f908422e79552878f25121a8801dcbdf0819a761d269ee337954d1debc9

Now let me show you how to understand a 4337 transaction.

Breaking down Calldata


The calldata, in simple terms, is the input parameters of a blockchain transaction. UserOp is an abstract transaction, i.e., it is not natively understood by the EVM chain. Rather certain sub-fields passed in the actual blockchain transaction are recognized as UserOps by the entrypoint smart contract and smart contract wallets (SCWs).

A single blockchain transaction to the entrypoint contract can contain 1 or more UserOps. Each UserOp is independent of the others in a transaction and its success or failure doesn't depend on the ones preceding it or the ones after it.

Calldata for UserOp Hash 0xfa4...3e5

Thus, it would be fair to say that a UserOp is the atomic unit of 4337. Now, let's dissect it further.

UserOp Fields


UserOp is the individual operation a smart contract wallet executes. We'll first understand its fields and their purpose and then see the value from actual UserOperations.

In the latest release of 4337 (v0.6), the entrypoint requires a UserOperation with the following fields:

  struct UserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
uint256 callGasLimit;
uint256 verificationGasLimit;
uint256 preVerificationGas;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
bytes paymasterAndData;
bytes signature;
}

The fields are officially defined in EIP-4337. Though we'll like to represent them again here in slightly more friendly terms:

FieldTypeDescription
senderaddressThe Smart Contract Wallet Address creating the UserOp.
nonceuint256A number to prevent replaying the same UserOp. Usually it's a number expected to be greater than the last recorded value by the smart contract wallet.
initCodebytesIf the sender is yet to be created, initCode bytes are used to first create a smart contract wallet for the given sender and then process rest of the UserOp as a normal 4337 transaction. The first 20 bytes represents the factory contract address and rest of the bytes are calldata for the factory to deploy the SCW.
callDatabytesThis is the actual calldata for a function present in the sender's smart contract wallet. The structure of the calldata will depend on the specific smart contract wallet implementation by its developer. This calldata represents the actual change desired by the user, for eg: transfer tokens out of the smart contract wallet to interact with other dApp contracts.
callGasLimituint256The maximum amount gas units the smart contract wallet can consume for the actual execution of the calldata (the above parameter).
verificationGasLimituint256There are certain operational checks carried out by the entrypoint before & after the actual execution of the calldata. The maximum gas for all these operations are specified by verificationGasLimit. Some of these operations can be creating the SCW when initcode is present, confirming validness of the UserOp and paymaster's support for the UserOp and triggering postOp on paymaster to validate successful completion of the userOp and fee sponsor.
preVerificationGasuint256A UserOp is usually validated off-chain by the bundler before including it in a bundle (a transaction) and submitting it to a node for inclusion to the blockchain. This gas represents compensation to the bundler for the same.
maxFeePerGasuint256Gas is the unit measuring amount the of computation effort. The fee Per Gas is the amount of wei 1 unit of gas is costing. This fee changes as the demand to include new transactions increases/decreases. maxFeePerGas is the maximum amount of wei per gas unit the sender is willing to spend. This is similar to max_fee_per_gas field in EIP-1559)
maxPriorityFeePerGasuint256This is similar to max_priority_fee_per_gas in EIP-1559 and is used to incentivize a node to include a transaction in the next block over other transactions as a limited number can be included per block.
paymasterAndDatabytesThere's a provision for a paymaster to sponsor the gas or the value in a UserOp. When that is the case, the first 20 bytes of this field represents the paymaster contract address and rest of the bytes are calldata for the paymaster contract to validate and sponsor the UserOp.
signaturebytesThis field is used for verification of the UserOp by the SCW and is usually the signature of the UserOpHash signed by the owner of SCW. (Each SCW basically has an EOA owner which the wallet creator has abstracted from the flow).

You can see the calldata values for each UserOp on the JiffyScan Explorer by going down to the developer details and expanding the CallData row:

Calldata for UserOp Hash 0xfa4...3e5

Calldata for UserOp Hash 0xfa4...3e5

Understanding Bundle

The Bundle represents a single on-chain transaction, containing one or more UserOps. It is a typical on-chain EVM transaction invoking the handleOps() function of the entrypoint smart contract, and is usually triggered by EOA. A bundler generally does this, in order to first validate the correctness of a UserOp, and to package multiple UserOps from different users into one to save gas costs. Though it could be as simple as a EOA invoking the function directly with the required calldata.

As the 4337 matures & picks up adoption, the bundler is expected to sponsor gas on behalf of multiple UserOps in a single bundle. During the execution of a bundle, the entrypoint contract collects the promised gas fee from SCW or sponsoring paymaster and holds it during execution of the bundle. At the end of the bundle, all the gas fee collected from the UserOps is transferred to an address the bundler specifies.

FieldTypeDescription
(sender, initCode, ...., signature)[]Array<UserOperation>A list of Fields shown in the UserOperation Struct above representing all the UserOps to execute in the current bundle
beneficiaryaddressThe gas fee collected from the sender SCW is transferred to this address. Currently it's the same address as that of the EOA used by the bundler

Let's see what happened in our reference bundle above:

4337 Bundle Internal Txns

4337 Bundle Internal Txns as shown on polyscan

Here, there are two UserOps, both successful shown by the first two transfer calls of 0.2 MATIC and 0.18 MATIC respectively. Then there's a third internal transaction transferring 0.17699 MATIC to the beneficiary, which, in this case, is the Stackup's bundler itself.

What's Next

Try submitting a UserOp yourself. Resources listed at awesome-account-abstraction can help you explore further.

Remember you can use the JiffyScan interface to share your UserOps with your friends and community.

Also, if you need real-time data on 4337, do check out our leading API.

We will be releasing more deep dives, walkthroughs, and quickstart tutorials for 4337 regularly in the coming weeks. Follow me on Twitter or JiffyScan to stay updated.