> ## Documentation Index
> Fetch the complete documentation index at: https://docs.startale.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Paymaster wire format

> The ERC-7677 methods a paymaster must implement to sponsor gas for an application built on @startale/app-sdk, and the constraints the Startale App applies when forwarding to it.

The App SDK is provider-neutral. It does not host a paymaster and it does not prefer one. When you set [`paymasterOptions`](/app-sdk/gasless#configure-a-paymaster), the Startale App forwards standard [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677) requests to the URL you name, and any service that answers them correctly will work.

This page is the contract that service has to satisfy.

## How the request reaches your paymaster

```mermaid theme={null}
sequenceDiagram
  participant App as Your application
  participant Host as Startale App
  participant PM as Your paymaster
  participant B as Bundler

  App->>Host: wallet_sendCalls (paymasterService.url)
  Host->>PM: pm_getPaymasterStubData
  PM-->>Host: stub paymasterData
  Host->>PM: pm_getPaymasterData
  PM-->>Host: signed paymasterData
  Host->>B: eth_sendUserOperation
```

The Startale App calls your paymaster from its own server rather than from the browser. Your endpoint therefore has to be reachable from the public internet over HTTPS, so it cannot be a localhost service or sit behind a private network address. It also never sees the browser's headers: nothing you set on the client reaches it, and no `Authorization` header is forwarded.

## Two required methods

Both methods are JSON-RPC calls against the same endpoint.

### `pm_getPaymasterStubData`

Called before gas estimation. Return a stub that is representative in size so the bundler can estimate gas, without committing a real signature.

The stub request does not carry the user's operation. The Startale App requests it with a placeholder operation, with a zero-address `sender`, empty `callData`, and nonce and gas fields set to zero, then caches the result and reuses it across transactions. Do not validate stub requests or apply your sponsorship policy to them: a rule on `sender` or on the target contract rejects every stub, and sponsorship fails.

**Request**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "pm_getPaymasterStubData",
  "params": [
    {
      "sender": "0x0000000000000000000000000000000000000000",
      "nonce": "0x0",
      "callData": "0x",
      "callGasLimit": "0x0",
      "verificationGasLimit": "0x0",
      "preVerificationGas": "0x0",
      "maxFeePerGas": "0x0",
      "maxPriorityFeePerGas": "0x0"
    },
    "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
    "0x74c",
    { "calculateGasLimits": false }
  ]
}
```

`params[1]` is the EntryPoint address, `params[2]` is the hex chain id, and `params[3]` is the context object described [below](#the-context-object). Startale accounts use EntryPoint v0.7 at the address shown.

**Response (EntryPoint v0.7)**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "paymaster": "0x...PaymasterAddress",
    "paymasterData": "0x...stubBytes",
    "paymasterVerificationGasLimit": "0x...",
    "paymasterPostOpGasLimit": "0x..."
  }
}
```

### `pm_getPaymasterData`

Called after gas estimation, with the user's UserOperation. Return the real signature. The request shape is the same and the response is the same, with real `paymasterData` in place of the stub.

<Warning>
  Do not return `isFinal: true` from `pm_getPaymasterStubData`. ERC-7677 lets a stub declare itself final. The Startale App then skips `pm_getPaymasterData` and submits paymaster data that was produced for the placeholder operation, not for the user's.
</Warning>

This is the call that commits you to paying. Every sponsorship decision your service makes belongs here, evaluated against the `sender` and `callData` of the operation in front of it.

### The context object

ERC-7677 allows a fourth parameter carrying a service-defined context object. The Startale App sends one, and its contents belong to the App rather than to your configuration:

```json theme={null}
{ "calculateGasLimits": false }
```

The `id` field of `paymasterOptions` is **not** part of it and does not reach you. Treat the context as opaque, and ignore any field you do not recognise. If your service needs to distinguish a policy, a tenant, or an environment, put that distinction in the URL. A separate path or query parameter per policy works, and it is the only mechanism available through this integration.

This also means a hosted provider that requires its own context, such as Alchemy's `policyId`, cannot be used directly. Put it behind a proxy that adds the context before forwarding. See [Secure your paymaster](/app-sdk/paymaster-security#keep-the-provider-credential-server-side).

`calculateGasLimits` tells you which of two attempts a request belongs to. The Startale App always makes the `false` attempt first, and makes the `true` attempt only if the first one fails:

| Value | When | What the Startale App does |
| - | - | - |
| `false` | Every transaction, first attempt | Uses the stub from `pm_getPaymasterStubData`, lets the bundler estimate gas, then calls `pm_getPaymasterData` to sign. |
| `true` | Retry, only after the first attempt fails | Calls `pm_getPaymasterData` before the bundler estimates gas, with the gas limits set to zero, and expects the paymaster gas limits back with the signature. After estimation it calls `pm_getPaymasterData` again to sign the final operation. |

Return `paymasterVerificationGasLimit` and `paymasterPostOpGasLimit` from both methods and you satisfy either strategy without having to branch on the flag.

`pm_getPaymasterData` can therefore be called more than once for a single transaction, for example when the Startale App retries, so it has to be safe to call repeatedly. Do not decrement a quota, consume a nonce, or write a billing record on the assumption of one call per transaction. Count spend from onchain settlement instead.

## Deciding what to run

| Option | When it fits |
| - | - |
| A hosted gas manager from an infrastructure provider | You want sponsorship in production quickly and your rules fit what the product offers. |
| A service you write yourself | You need rules a hosted product cannot express, such as sponsoring only specific contract methods, resolving policy from your own user records, or settling against your own accounting. |
| A thin proxy in front of a hosted gas manager | The common middle ground. The provider signs, and your proxy holds the credential and applies the rules the provider cannot. See [Secure your paymaster](/app-sdk/paymaster-security). |

Whichever you choose, EntryPoint v0.7 is the version in use on Soneium, so return the v0.7 response fields shown above.

## Constraints applied when forwarding

| Constraint | Value |
| - | - |
| Scheme | HTTPS only |
| Host | Must resolve to a publicly routable address |
| Redirects | Not followed. A `3xx` is treated as a failure. |
| Request and response size | 256 KB each |
| Upstream timeout | 10 seconds |
| Headers forwarded | None |
| Method | `POST`, with the JSON-RPC body forwarded unchanged |

Keep responses small and fast. A paymaster that takes longer than 10 seconds to sign will fail the user's transaction, and the failure will look to your user like the application is broken.

## Before you go live

Serve both methods from one HTTPS endpoint on a public host, returning EntryPoint v0.7 fields from each. Answer stub requests without validating them, and never mark a stub final. Make the sponsorship decision in `pm_getPaymasterData` from `sender` and `callData`, decline with a JSON-RPC error rather than a malformed result, and ignore any context field you do not recognise instead of rejecting the request outright.

Log every signature against the resulting UserOperation hash so spend can be reconciled later, and keep responses well inside the 10 second timeout.

## Related

<CardGroup cols={2}>
  <Card title="Configure paymasterOptions" icon="bolt" href="/app-sdk/gasless">
    The call site, and the batched-call requirement that makes it take effect.
  </Card>

  <Card title="Secure your paymaster" icon="shield" href="/app-sdk/paymaster-security">
    Your URL is public. Scope what you sponsor before you fund it.
  </Card>

  <Card title="Who pays for gas" icon="gas-pump" href="/concepts/gasless">
    Mini App and standalone contexts, and the division of responsibility.
  </Card>

  <Card title="Batched calls" icon="layer-group" href="/app-sdk/batched-calls">
    How `wallet_sendCalls` builds the UserOperation your paymaster signs.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.