> For the complete documentation index, see [llms.txt](https://paymasterdocs.gitbook.io/paymasterdocs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://paymasterdocs.gitbook.io/paymasterdocs/basics-of-paymaster/breaking-down-the-paymaster.md).

# Breaking down the paymaster

In this page, we will deep-dive the paymaster by code block-to-block and then pieces-by-pieces. If you already know the basic operations of the paymaster, skip to the [several types of the paymaster.](/paymasterdocs/several-types-of-the-paymaster/several-types-of-the-paymaster.md)

## The interfaces described by ERC-4337

In ERC-4337, there's an interface that defines the interaction functions and an enum used by the paymaster when working with the `EntryPoint` contract. These components are essential for the paymaster's proper operation. The paymaster interacts with these interfaces in two key steps: the validation step and the execution step, represented by the `validatePaymasterUserOp()` and `postOp()` functions, respectively.&#x20;

Let’s dive into how these functions and the enum `PostOpMode` interact with the `EntryPoint` and the `PackedUserOperation` struct, which is passed as calldata from `EntryPoint`.

> <mark style="background-color:red;">Well...it sounds pretty complicated, but don't worry. we will explore it step-by-step with the diagram and the descriptions!</mark>

## Before we start: intertaction with the EntryPoint

Before we deep-dive how the function actually works, we will see the top-level of how these functions interact with the `Entrypoint` contract. Let's see how it works with the flow graph below.

<figure><img src="https://964345221-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fei169WYgpcSKzLw6xzlv%2Fuploads%2FnrGioJEb5wWIcBctx77U%2Fimage.png?alt=media&amp;token=4599eeab-1367-41eb-ab5b-e9103877ca78" alt=""><figcaption><p>The paymaster's operation overview. SourceCode: eth-infinitism</p></figcaption></figure>

In the flow graph, you can see how the paymaster interacts with `EntryPoint` through the two functions we previously discussed. `EntryPoint` processes the `userOp` in two main phases: the validation phase and the execution phase.

During the validation phase, `EntryPoint` ensures that all involved entities—such as the paymaster, SCW, SCW factory (which can deploy the wallet contract), and the aggregator—are valid and meet the required conditions. In the execution phase, `EntryPoint` allows the paymaster to check whether the user has sufficient gas, if necessary. This is the key feature that allows users to perform transactions without holding ETH in their wallets.

For example, if a user uses paymaster that converts their tokens into ETH, they can cover the gas fees using tokens like ERC-20 Token, which are repaid to the bundler. Additionally, if the paymaster is running a promotion, the user may benefit from reduced gas fees, as the paymaster can cover a portion of the total gas costs. (For more details, check out the [Several types of the paymaster](/paymasterdocs/several-types-of-the-paymaster/several-types-of-the-paymaster.md) section.) The flexibility of the paymaster interface also opens up many possibilities for customizing operations further. &#x20;

## Paymaster-validation phase

Now that we’ve covered the flow and use of the paymaster, let's dive into the actual parameters, return values, and examples of how the paymaster operates in practice.

#### Try-Catch mechanism of EntryPoint in validation phase

```solidity
try
    IPaymaster(paymaster).validatePaymasterUserOp{gas: pmVerificationGasLimit}(
        op,
        opInfo.userOpHash,
        requiredPreFund
    )
returns (bytes memory _context, uint256 _validationData) {
    context = _context;
    validationData = _validationData;
} catch {
    revert FailedOpWithRevert(opIndex, "AA33 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN));
}
```

`validatePaymasterUserOp()` function is called by [`EntryPoint` in validation phase](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L538). All external contract calls are handled within try-catch blocks to manage potential revert messages issued by the `EntryPoint`. These revert messages follow a specific format (e.g. AA33) and are used by the bundler to impose penalties within its system when a malicious operation triggers a revert.

#### validatePaymasterUserOp: params

* **\[gas: pmVerificationGasLimit]**\
  This parameter originates from the `userOp` struct. Its value is taken from `mUserOp`, which is used by the `Entrypoint` to duplicate the `userOp` calldata into memory, helping to save gas fees. Typically, this value is packed within the `paymasterAndData` field in the `userOp` calldata. However, if the user isn't utilizing a paymaster, the value in `mUserOp` will be set to zero. The value is then [unpacked and stored in `mUserOp.paymasterVerificationGasLimit`](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L390) before being [passed to `pmVerificationGasLimit`](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L536). This process ensures that the value resides in the stack area since `mUserOp` is located in memory, and `pmVerificationGasLimit` (`uint256` type) will be pushed onto the stack.
* **\[param0: op -> userOp]**\
  The `PackedUserOperation` struct that is parsed from the bundler's calldata, which should be verified in the paymaster's context. Since there is no standardized verification method for this struct, the paymaster has the freedom to validate it however it sees fit. For example, the paymaster might perform signature validation to confirm permission or simply check whether the user has the ability to cover the payment.
* **\[param1: opInfo.userOpHash -> userOpHash]**\
  The hash of the `userOp` allows the paymaster to verify whether the `userOp` is valid. For instance, the paymaster can use this hash to check the signature and confirm if the `paymasterAndData` field in the struct is accurate.
* **\[param2: requiredPreFund -> maxCost]**\
  This parameter represents the maximum predicted cost of the transaction, estimated by the bundler (or the user). The paymaster uses this value to determine the compensation amount it may need to collect from the user, if necessary. Additionally, during the validation phase, the `EntryPoint` temporarily deducts this amount in ether from the paymaster's deposited balance.

#### validatePaymasterUserOp: returns

* **\[returns0: bytes memory context]**\
  The value to send to the postOp. If the paymaster needs to cache something after this validation and toss it to the postOp, it can be passed by this bytes.\
  This value is sent to `postOp` function. If the paymaster needs to temporarily store data after validation and pass it to `postOp` functionon, it can do so using this `bytes` field.\
  \
  Q: Why don't we just cache it to the storage slot in the paymaster?\
  A: Due to gas costs. Using the `SSTORE` opcode (writing to storage) is significantly more expensive than the `MSTORE` opcode (storing in memory). Thus, returning the value directly through memory is much cheaper.
* **\[returns1: uint256 validationData]**

  This field consists of:

  * sigAuthorizer (20 bytes)
  * validUntil (6 bytes)
  * validAfter (6 bytes)

  \
  The `validatePaymasterUserOp` function doesn't revert if the signature validation fails. Instead, it returns a flag value of `1`, encoded within the 20-byte `sigAuthorizer` section. This design avoids immediate reversion since the `EntryPoint` has its own reversion format, and it allows the bundler to handle potential penalties. If the signature validation succeeds, the function returns `0`.\
  The `validUntil` and `validAfter` fields represent timestamps. The operation will only be executed if the current timestamp is greater than `validAfter` and less than `validUntil`. This check is performed immediately after receiving the return value in the [`_validateAccountAndPaymasterValidationData`](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L251) function.

## Paymaster-execution phase

The execution phase is significantly more complex than the validation phase. This is due to the need to handle the operation's result and the potential inability of the user to pay the required amount after the execution of the `userOp`. Multiple calls to paymaster contract may occur during this phase.

To provide a clearer overview, we organized the process in a time table and present it as a flow graph, accompanied by additional explanations.

#### Before we start: PostOpMode

The enum introduced in the paymaster interface is crucial for the operation state when calling the paymaster's `postOp` function. There are three values in the enum.

&#x20;     \[value0: opSucceeded     ]\
&#x20;     \[value1: opReverted          ]\
&#x20;     \[value2: postOpReverted]

`opSucceeded` is the state when the userOperation is executed by the SCW successfully. If it dosen't succeed, the state would be `opReverted`. `postOpReverted` is the state when the postOp has been reverted for unexpected operation. In this case, the postOp is not called again.

<figure><img src="https://964345221-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fei169WYgpcSKzLw6xzlv%2Fuploads%2FGRX7wzHDsyl2LeSW06bo%2Fimage.png?alt=media&amp;token=229e1f33-c12f-4a30-8714-a69e3913877f" alt=""><figcaption><p>The paymaster's interaction on the execution phase</p></figcaption></figure>

Let's begin with the `innerHandleOp()` function. This function is invoked by `_executeUserOp()`, which is responsible for handling all operations during the execution phase. Within this process, `innerHandleOp()` is called to execute the actual operation and to interact with the paymaster through the `postExecution()` function. When calling `postExecution()`, `innerHandleOp()` passes the following parameters:

#### \_postExecution: params

* **\[param0: IPaymaster.PostOpMode mode]**\
  The value which represents whether the user's TX succeed or failed.
* **\[param1: UserOpInfo memory opInfo]**

  ```solidity
  struct UserOpInfo {
      MemoryUserOp mUserOp;
      bytes32 userOpHash;
      uint256 prefund;
      uint256 contextOffset;
      uint256 preOpGas;
  }
  ```

  \
  \- **mUserOp**: A copy of the original transaction’s calldata sent by the bundler.\
  \- **userOpHash**: The hash of the `userOp`, used for verifying its correctness through ECDSA. However, it is not utilized during the validation phase.\
  \- **prefund**: The estimated total gas required for executing the `userOp`. The `innerHandleOp()` function uses this value when calculating the expected gas cost to reimburse the bundler.\
  \- **contextOffset**: The memory offset used to load the operation’s context. Since the context is cached in the `EntryPoint` contract’s memory, it needs to be retrieved before passing it to `postOp` function in the external paymaster contract.\
  \- **preOpGas**: The amount of gas consumed before executing the `userOp` call to the SCW. This value must be passed to `innerHandleOp()`  function to correctly calculate the total gas usage for the operation.
* **\[param2: bytes memory context]**\
  This field holds the cached values from the validation phase. As discussed, the paymaster passes this data to the `EntryPoint` through the `validatePaymasterUserOp()` function. The contents of the `context` are determined by the paymaster, based on its internal logic and requirements.
* **\[param3: uint256 actualGas]**\
  The exact total gas used right before `postOp` function. It is passed to `postOp` function to verify whether the user or paymaster can cover the gas costs. This ensures that the transaction can be settled without issues.

In the `postexecution()` function, there will be a call to the paymaster with several parameters.

#### postOp: params

* **\[param0: PostOpMode mode]**\
  This value indicates whether the user’s TX succeeded or failed. The paymaster can apply different logic based on the mode. For instance, if a user’s TX reverts frequently, the paymaster might decide to block the user from accessing its services.
* **\[param1: bytes calldata context]**\
  This parameter holds the data cached by the paymaster during the validation phase.
* **\[param2: uint256 actualGasCost]**\
  The exact total amount of gas used right before the `postOp` function. It is provided to `postOp` function to ensure that the gas cost can be covered, either by the user or the paymaster.
* **\[param3: uint256 actualUserOpFeePerGas]**\
  This parameter represents the gas price used for the TX. In the context of SCWs, the gas fee must be calculated based on the initial price provided by the bundler to ensure consistency between off-chain and on-chain calculations. \
  Since gas prices can fluctuate due to network volatility, mismatches between the predicted gas fee (off-chain) and the actual gas fee (on-chain) could lead to unexpected issues like denial of service (DoS). To mitigate this, all operations must use a consistent gas price to ensure stability and avoid such risks in account abstraction.

#### What operation would be in the postOp?

Typically, operation in `postOp` function is used to calculate the gas consumed and claim it from another entity. However, this is just an example—the specific gas claiming logic varies depending on the paymaster. Some paymasters cover all the gas fees and claim them off-chain, while others claim the gas fees during the transaction itself. If you're reviewing a specific paymaster, refer to this documentation. For examples of how different paymasters operate, check the `postOp` section in the documentation for  [Several types of the paymaster](/paymasterdocs/several-types-of-the-paymaster/several-types-of-the-paymaster.md).

#### Caution: What if the initial postOp reverts?

If the call to the `postOp` function fails, the `innerHandleOp()` function will also revert, triggering the catch block.

```solidity
            } else {
                emit PostOpRevertReason(
                    opInfo.userOpHash,
                    opInfo.mUserOp.sender,
                    opInfo.mUserOp.nonce,
                    Exec.getReturnData(REVERT_REASON_MAX_LEN)
                );

                uint256 actualGas = preGas - gasleft() + opInfo.preOpGas;
                collected = _postExecution(
                    IPaymaster.PostOpMode.postOpReverted,
                    opInfo,
                    context,
                    actualGas
                );
            }
```

There are several reversion catch codes, but the one we need to focus on is the [else statement](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L134). In this scenario, `_executeUserOp()` emits the `PostOpRevertReason` and then calls another function, `_postExecution()`, with the mode set to `postOpReverted`. However, it does not invoke `postOp` again because the state changes made by SCW are lost due to the revert. As a result, the responsibility for handling the revert in `postOp` now falls on the paymaster.
