---
eip: 7677
title: Paymaster Web Service Capability
description: A way for apps to communicate with smart wallets about paymaster web services
author: Lukas Rosario (@lukasrosario), Dror Tirosh (@drortirosh), Wilson Cusack (@wilsoncusack), Kristof Gazso (@kristofgazso), Hazim Jumali (@hazim-j)
discussions-to: https://ethereum-magicians.org/t/erc-7677-paymaster-web-service-capability/19530
status: Review
type: Standards Track
category: ERC
created: 2024-04-03
requires: 4337, 5792
---

## Abstract

With [EIP-5792](./eip-5792.md), apps can communicate with wallets about advanced features via capabilities. This proposal defines a capability that allows apps to request that [ERC-4337](./eip-4337.md) wallets communicate with a specified paymaster web service. To support this, we also define a standardized API for paymaster web services.

## Motivation

App developers want to start sponsoring their users' transactions using paymasters. Paymasters are commonly used via web services. However, there is currently no way for apps to tell wallets to communicate with a specific paymaster web service. Similarly, there is no standard for how wallets should communicate with these services. We need both a way for apps to tell wallets to communicate with a specific paymaster web service and a communication standard for wallets to do so.

## Specification

One new [EIP-5792](./eip-5792.md) wallet capability is defined. We also define a standard interface for paymaster web services as a prerequisite.

### Paymaster Web Service Interface

We define two JSON-RPC methods to be implemented by paymaster web services.

#### `pm_getPaymasterStubData`

Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object.

This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return `paymasterVerificationGasLimit`. Furthermore, for EntryPoint v0.7, this method MUST return a value for `paymasterPostOpGasLimit`. This is because it is the paymaster that pays the postOpGasLimit, so it cannot trust the wallet to estimate this amount.

The wallet SHOULD use these provided gas values when submitting the `UserOperation` to a bundler for gas estimation.

This method MAY also return a `sponsor` object with a `name` field and an optional `icon` field. The `name` field is the name of the party sponsoring the transaction, and the `icon` field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The `icon` string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the `<img>` tag to ensure no untrusted Javascript execution can occur.

There are cases where a call to just `pm_getPaymasterStubData` is sufficient to provide valid paymaster-related user operation fields, e.g. when the `paymasterData` does not contain a signature. In such cases, the second RPC call to `pm_getPaymasterData` (defined below) MAY be skipped, by returning `isFinal: true` by this RPC call.

Paymaster web services SHOULD do validations on incoming user operations during `pm_getPaymasterStubData` execution and reject the request at this stage if it would not sponsor the operation.

##### `pm_getPaymasterStubData` RPC Specification

Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated.

```typescript
// [userOp, entryPoint, chainId, context]
type GetPaymasterStubDataParams = [
  // Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too
  {
    sender: `0x${string}`;
    nonce: `0x${string}`;
    initCode: `0x${string}`;
    callData: `0x${string}`;
    callGasLimit: `0x${string}`;
    verificationGasLimit: `0x${string}`;
    preVerificationGas: `0x${string}`;
    maxFeePerGas: `0x${string}`;
    maxPriorityFeePerGas: `0x${string}`;
  }, // userOp
  `0x${string}`, // EntryPoint
  `0x${string}`, // Chain ID
  Record<string, any> // Context
];

type GetPaymasterStubDataResult = {
  sponsor?: { name: string; icon?: string }; // Sponsor info
  paymaster?: string; // Paymaster address (entrypoint v0.7)
  paymasterData?: string; // Paymaster data (entrypoint v0.7)
  paymasterVerificationGasLimit?: `0x${string}`; // Paymaster validation gas (entrypoint v0.7)
  paymasterPostOpGasLimit?: `0x${string}`; // Paymaster post-op gas (entrypoint v0.7)
  paymasterAndData?: string; // Paymaster and data (entrypoint v0.6)
  isFinal?: boolean; // Indicates that the caller does not need to call pm_getPaymasterData
};
```

###### `pm_getPaymasterStubData` Example Parameters

```json
[
  {
    "sender": "0x...",
    "nonce": "0x...",
    "initCode": "0x",
    "callData": "0x...",
    "callGasLimit": "0x...",
    "verificationGasLimit": "0x...",
    "preVerificationGas": "0x...",
    "maxFeePerGas": "0x...",
    "maxPriorityFeePerGas": "0x..."
  },
  "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
  "0x2105",
  {
    // Illustrative context field. These should be defined by service providers.
    "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
  }
]
```

###### `pm_getPaymasterStubData` Example Return Value

Paymaster services MUST detect which entrypoint version the account is using and return the correct fields.

For example, if using entrypoint v0.6:

```json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymasterAndData": "0x..."
}
```

If using entrypoint v0.7:

```json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x..."
}
```

If using entrypoint v0.7, with paymaster gas estimates:

```json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x...",
  "paymasterVerificationGasLimit": "0x...",
  "paymasterPostOpGasLimit": "0x..."
}
```

Indicating that the caller does not need to make a `pm_getPaymasterData` RPC call:

```json
{
  "sponsor": {
    "name": "My App",
    "icon": "https://..."
  },
  "paymaster": "0x...",
  "paymasterData": "0x...",
  "isFinal": true
}
```

#### `pm_getPaymasterData`

Returns values to be used in paymaster-related fields of a signed user operation. These are not stub values and will be used during user operation submission to a bundler. Similar to `pm_getPaymasterStubData`, accepts an unsigned user operation, entrypoint address, chain id, and a context object.

##### `pm_getPaymasterData` RPC Specification

Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated.

```typescript
// [userOp, entryPoint, chainId, context]
type GetPaymasterDataParams = [
  // Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too
  {
    sender: `0x${string}`;
    nonce: `0x${string}`;
    initCode: `0x${string}`;
    callData: `0x${string}`;
    callGasLimit: `0x${string}`;
    verificationGasLimit: `0x${string}`;
    preVerificationGas: `0x${string}`;
    maxFeePerGas: `0x${string}`;
    maxPriorityFeePerGas: `0x${string}`;
  }, // userOp
  `0x${string}`, // Entrypoint
  `0x${string}`, // Chain ID
  Record<string, any> // Context
];

type GetPaymasterDataResult = {
  paymaster?: string; // Paymaster address (entrypoint v0.7)
  paymasterData?: string; // Paymaster data (entrypoint v0.7)
  paymasterAndData?: string; // Paymaster and data (entrypoint v0.6)
};
```

###### `pm_getPaymasterData` Example Parameters

```json
[
  {
    "sender": "0x...",
    "nonce": "0x...",
    "initCode": "0x",
    "callData": "0x...",
    "callGasLimit": "0x...",
    "verificationGasLimit": "0x...",
    "preVerificationGas": "0x...",
    "maxFeePerGas": "0x...",
    "maxPriorityFeePerGas": "0x..."
  },
  "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
  "0x2105",
  {
    // Illustrative context field. These should be defined by service providers.
    "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
  }
]
```

###### `pm_getPaymasterData` Example Return Value

Paymaster services MUST detect which entrypoint version the account is using and return the correct fields.

For example, if using entrypoint v0.6:

```json
{
  "paymasterAndData": "0x..."
}
```

If using entrypoint v0.7:

```json
{
  "paymaster": "0x...",
  "paymasterData": "0x..."
}
```

### `paymasterService` Capability

The `paymasterService` capability is implemented by both apps and wallets.

#### App Implementation

Apps need to give wallets a paymaster service URL they can make the above RPC calls to. They can do this using the `paymasterService` capability as part of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call.

##### `wallet_sendCalls` Paymaster Capability Specification

```typescript
type PaymasterCapabilityParams = {
  url: string;
  context: Record<string, any>;
}
```

###### `wallet_sendCalls` Example Parameters

```json
[
  {
    "version": "1.0",
    "chainId": "0x01",
    "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
    "calls": [
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x9184e72a",
        "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
      },
      {
        "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        "value": "0x182183",
        "data": "0xfbadbaf01"
      }
    ],
    "capabilities": {
      "paymasterService": {
        "url": "https://...",
        "context": {
          "policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
        }
      }
    }
  }
]
```

The wallet will then make the above paymaster RPC calls to the URL specified in the `paymasterService` capability field.

#### Wallet Implementation

To conform to this specification, smart wallets that wish to leverage app-sponsored transactions:

1. MUST indicate to apps that they can communicate with paymaster web services via their response to an [EIP-5792](./eip-5792.md) `wallet_getCapabilities` call.
2. SHOULD make calls to and use the values returned by the paymaster service specified in the capabilities field of an [EIP-5792](./eip-5792.md) `wallet_sendCalls` call. An example of an exception is a wallet that allows users to select a paymaster provided by the wallet. Since there might be cases in which the provided paymaster is ultimately not used—either due to service failure or due to a user selecting a different, wallet-provided paymaster—applications MUST NOT assume that the paymaster it provides to a wallet is the entity that pays for transaction fees.

##### `wallet_getCapabilities` Response Specification

```typescript
type PaymasterServiceCapability = {
  supported: boolean;
};
```

###### `wallet_getCapabilities` Example Response

```json
{
  "0x2105": {
    "paymasterService": {
      "supported": true
    }
  },
  "0x14A34": {
    "paymasterService": {
      "supported": true
    }
  }
}
```

Below is a diagram illustrating the full `wallet_sendCalls` flow, including how a wallet might implement the interaction.

![flow](../assets/eip-7677/0.svg)

## Rationale

### Gas Estimation

The current loose standard for paymaster services is to implement `pm_sponsorUserOperation`. This method returns values for paymaster-related user operation fields and updated gas values. The problem with this method is that paymaster service providers have different ways of estimating gas, which results in different estimated gas values. Sometimes these estimates can be insufficient. As a result we believe it’s better to leave gas estimation up to the wallet, as the wallet has more context on how user operations will get submitted (e.g. which bundler they will get submitted to). Then wallets can ask paymaster services to sponsor given the estimates defined by the wallet.

The above reason is also why we specify that the `pm_getPaymasterStubData` method MAY also return paymaster-specific gas estimates. I.e., bundlers are prone to insufficiently estimating the paymaster-specific gas values, and the paymaster servies themselves are ultimately better equipped to provide them.

### Chain ID Parameter

Currently, paymaster service providers typically provide developers with a URL per chain. That is, paymaster service URLs are not typically multichain. So why do we need a chain ID parameter? We recognize that we must specify some constraint so that wallets can communicate with paymaster services about which chain their requests are for. As we see it, there are two options:

1. Formalize the current loose standard and require that paymaster service URLs are 1:1 with chains.
2. Require a chain ID parameter as part of paymaster service requests.

We feel that option (2) is the better abstraction here. This allows service providers to offer multichain URLs if they wish at essentially no downside to providers who offer a URL per chain. Providers who offer a URL per chain would just need to accept an additional parameter that they can ignore. When an app developer who uses a URL-per-chain provider wants to submit a request to a different chain, they can just swap out the URL accordingly.

### Challenges With Stub Data

Enabling a workflow with greater flexibility in gas estimations will nonetheless come with some known challenges that paymaster services must be aware of in order to ensure reliable gas estimates are generated during the process.

#### `preVerificationGas`

The `preVerificationGas` value is largely influenced by the size of the user operation and it's ratio of zero to non-zero bytes. This can cause a scenario where `pm_getPaymasterStubData` returns values that results in upstream gas estimations to derive a lower `preVerificationGas` compared to what `pm_getPaymasterData` would require. If this occurs then bundlers will return an insufficient `preVerificationGas` error during `eth_sendUserOperation`.

To avoid this scenario, a paymaster service MUST return stub data that:

1. Is of the same length as the final data.
2. Has an amount of zero bytes (`0x00`) that is less than or equal to the final data.

#### Consistent Code Paths

In the naive case, a stub value of repeating non-zero bytes (e.g. `0x01`) that is of the same length as the final value will generate a usable `preVerificationGas`. Although this would immediately result in a gas estimation error given that the simulation will likely revert due to an invalid paymaster data.

In a more realistic case, a valid stub can result in a successful simulation but still return insufficient gas limits. This can occur if the stub data causes `validatePaymasterUserOp` or `postOp` functions to simulate a different code path compared to the final value. For example, if the simulated code was to return early, the estimated gas limits would be less than expected which would cause upstream `out of gas` errors once a user operation is submitted to the bundler.

Therefore, a paymaster service MUST also return a stub that can result in a simulation executing the same code path compared to what is expected of the final user operation.

## Security Considerations

The URLs paymaster service providers give to app developers commonly have API keys in them. App developers might not want to pass these API keys along to wallets. To remedy this, we recommend that app developers provide a URL to their app's backend, which can then proxy calls to paymaster services. Below is a modified diagram of what this flow might look like.

![flowWithAPI](../assets/eip-7677/1.svg)

This flow would allow developers to keep their paymaster service API keys secret. Developers might also want to do additional simulation / validation in their backends to ensure they are sponsoring a transaction they want to sponsor.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
