> ## 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.

# Gas in a standalone application

> Configure paymasterOptions so your paymaster settles gas for your users, or design for the user paying gas from their smart account.

Applications that use `@startale/app-sdk` from their own origin are not sponsored by Startale. This page covers the two supported outcomes at the call site: your paymaster pays, or the user pays.

<Note>
  Building a Mini App instead? Gas is sponsored by the Startale App and there is nothing to configure. See [Gas in a Mini App](/miniapps/wallet-integration#gas).
</Note>

## Configure a paymaster

Pass `paymasterOptions` to either `createStartaleAccountSDK` or `startaleConnector`. The map is keyed by chain id.

<Tabs>
  <Tab title="Raw SDK">
    ```ts theme={null}
    import { createStartaleAccountSDK } from '@startale/app-sdk'

    const sdk = createStartaleAccountSDK({
      appName: 'My App',
      appChainIds: [1868],
      paymasterOptions: {
        1868: {
          url: 'https://paymaster.my-app.com/rpc',
          id: 'my-sponsorship-policy',
        },
      },
    })
    ```
  </Tab>

  <Tab title="wagmi connector">
    ```ts theme={null}
    import { startaleConnector } from '@startale/app-sdk'

    startaleConnector({
      appName: 'My App',
      paymasterOptions: {
        1868: {
          url: 'https://paymaster.my-app.com/rpc',
          id: 'my-sponsorship-policy',
        },
      },
    })
    ```

    The connector forwards every option to `createStartaleAccountSDK` internally. Configuration is identical.
  </Tab>
</Tabs>

| Field | Type | Behaviour |
| - | - | - |
| `url` | `string` | The ERC-7677 paymaster endpoint that will be asked to sponsor the operation. Required. Must be HTTPS and must resolve to a publicly routable address. |
| `id` | `string` | Reserved. Accepted by the SDK today but **not transmitted to your paymaster**. Do not build sponsorship logic that depends on receiving it. |

<Warning>
  `id` does not reach your paymaster. Only `url` is forwarded. The Startale App does send an ERC-7677 context object, but it carries the App's own gas-estimation flag rather than anything you configured. If your service needs to distinguish policies, tenants, or environments, encode that in the URL itself, for example as a distinct path or as a query parameter. See [the context object](/app-sdk/custom-paymaster#the-context-object).
</Warning>

## Only batched calls carry your paymaster

`paymasterOptions` is attached as an [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) `paymasterService` capability, and that capability exists only on `wallet_sendCalls`. A plain `eth_sendTransaction` carries no capabilities, so it cannot carry your paymaster either.

| Call | Carries `paymasterOptions` | Result in a standalone application |
| - | - | - |
| `wallet_sendCalls` | Yes | Your paymaster is asked to sponsor. |
| `eth_sendTransaction` | No | The user pays, even with `paymasterOptions` configured. |

This is the single most common integration mistake. A viem `walletClient.sendTransaction` call, or wagmi's `useSendTransaction` and `useWriteContract`, all issue `eth_sendTransaction`. Configuring `paymasterOptions` and then sending through those paths produces a working transaction that the **user** pays for, with no error to tell you the paymaster was skipped.

To route through your paymaster, send the transaction as a batch of one:

```ts theme={null}
const { transactionHash, userOpHash } = await provider.request({
  method: 'wallet_sendCalls',
  params: [
    {
      version: '1',
      from: smartAccountAddress,
      chainId: '0x74c', // 1868, Soneium Mainnet
      atomicRequired: true,
      calls: [{ to: contractAddress, data: encodedCall }],
    },
  ],
})
```

The SDK injects the `paymasterService` capability for you, so there is no capability to write by hand. On wagmi, use the EIP-5792 `useSendCalls` hook rather than the single-transaction hooks. See [Batched calls](/app-sdk/batched-calls).

## Or let the user pay

With no `paymasterOptions`, UserOperations are still built and submitted and gas is debited from the user's smart account. The user needs:

* ETH at their **smart account** address, not at the EOA that signs for it.
* A route to put ETH there, such as a bridge or a deposit flow inside your product.

Make the funding requirement explicit in your interface before the first transaction. Without it, a new user reaches a failed transaction with no way to interpret or resolve it, which is a worse experience than not offering the flow at all.

## Configure per chain

`paymasterOptions` is a map, so sponsorship is a per-chain decision. A chain with no entry falls back to the user paying.

```ts theme={null}
paymasterOptions: {
  // Soneium Mainnet: your paymaster pays.
  1868: { url: 'https://paymaster.my-app.com/rpc', id: 'soneium' },
  // Any chain without an entry: the user pays.
}
```

## Constraints applied by the Startale App

Paymaster requests are forwarded server-side by the Startale App rather than called from the browser, and that forwarding applies fixed limits. Check these first when a sponsorship fails for no obvious reason.

| Constraint | Value |
| - | - |
| Scheme | HTTPS only |
| Host | Must resolve to a public address. Private and internal addresses are rejected. |
| Redirects | Not followed |
| Request and response size | 256 KB each |
| Upstream timeout | 10 seconds |
| Headers forwarded | None. No `Authorization` header reaches your paymaster. |

Because no `Authorization` header is forwarded, any credential your paymaster requires must be carried in the URL or terminated by a gateway you control. See [Secure your paymaster](/app-sdk/paymaster-security).

## Failure modes

Your application does not see your paymaster's error text or the forwarding layer's. The Startale App reports a failed send to your `wallet_sendCalls` request as a JSON-RPC error with a short reason category.

| What your application receives | Cause | Fix |
| - | - | - |
| `-32602`, message naming `capabilities.paymasterService.url` | The paymaster `url` is not HTTPS. The request is rejected before the user can confirm. | Serve the paymaster over HTTPS. |
| `-32603` `Transaction sending failed (<reason>).` | The transaction could not be sent. `<reason>` is a category such as `paymaster_error`, `network_error`, or `insufficient_funds`. This covers your paymaster declining, failing, or breaking one of the constraints above. | Check your paymaster's logs first, then the constraints above. Startale has no visibility into your sponsorship decisions. |
| `-32603` | The user pays, and their smart account does not hold enough ETH for gas. | Fund the smart account, or configure `paymasterOptions`. |
| The transaction succeeds but your paymaster is never called | The call went through `eth_sendTransaction`. | Send through `wallet_sendCalls`. |

## Related

<CardGroup cols={2}>
  <Card title="Paymaster wire format" icon="plug" href="/app-sdk/custom-paymaster">
    The two ERC-7677 methods your service must implement, with request and response shapes.
  </Card>

  <Card title="Secure your paymaster" icon="shield" href="/app-sdk/paymaster-security">
    Your URL is public. Proxy your credentials and scope what you sponsor.
  </Card>

  <Card title="Who pays for gas" icon="bolt" href="/concepts/gasless">
    The model across both contexts, and where Startale's responsibility ends.
  </Card>

  <Card title="Batched calls" icon="layer-group" href="/app-sdk/batched-calls">
    The `wallet_sendCalls` path that carries your paymaster.
  </Card>
</CardGroup>


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