UserOperation InitCode

The initCode field on a UserOperation encodes instructions on how to deploy the sender's smart account on-chain.

In the last section on UserOperation Sender we discussed the rationale of using factories to deploy a smart account to the sender address. In this part, we discuss how this is done within the ERC-4337 standard through the User Operation initCode.

Creating the initCode

The User Operation initCode is simply a hex concatenation of two values:

  1. The factory address.
  2. The encoded call data to send to the factory.

In practice, using SimpleAccount.sol as an example:

const initCode = ethers.utils.hexConcat([
  factory.address,
  factory.interface.encodeFunctionData("createAccount", [
    await instance.signer.getAddress(),
    ethers.BigNumber.from(0),
  ]),
]);

From initCode to deployed smart account

If this is the user's first transaction and there is no code located at the sender address, the EntryPoint will use the User Operation initCode to send the encoded call data to the factory contract.

To continue the SimpleAccount example, the above initCode will execute the function createAccount on the SimpleAccountFactory.

function createAccount(address owner,uint256 salt) public returns (SimpleAccount ret) {
  address addr = getAddress(owner, salt);
  uint codeSize = addr.code.length;
  if (codeSize > 0) {
    return SimpleAccount(payable(addr));
  }
  ret = SimpleAccount(payable(new ERC1967Proxy{salt : bytes32(salt)}(
    address(accountImplementation),
    abi.encodeCall(SimpleAccount.initialize, (owner))
  )));
}

Assuming no errors, the function returns a newly deployed SimpleAccount with the exact same address as the User Operation sender.