> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Pay Solana gas in stablecoins (USDC, USDT, USDG)

On Solana, you can let users pay transaction fees directly in stablecoins such as **USDC, USDT, or USDG** instead of SOL. Each is an SPL token, so the same flow applies to any SPL mint your project accepts. The Openfort paymaster (Kora) signs the transaction as fee payer, and the user pays the fee on-chain in one of the SPL mints your policy accepts — your project balance is never debited.

This is the strategy shown in the dashboard as **Charge SPL tokens (Solana)** when creating a gas sponsorship.

## How it works

1. You create a gas sponsorship with `splTokens` set to the SPL mints your project accepts.
2. The end-user's transaction must include an SPL `TransferChecked` instruction paying Kora in one of those mints (your wallet/SDK constructs this).
3. The client calls [`signTransaction`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#signtransaction) or [`signAndSendTransaction`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#signandsendtransaction).
4. Openfort enforces the policy — the transaction must contain a `TransferChecked` to an accepted mint, otherwise the request is rejected.
5. Kora signs as fee payer and (optionally) broadcasts the transaction. The user pays the SPL fee on-chain; the project is not billed.

## Discover the accepted SPL tokens

Kora is the source of truth for which mints can be used for fee payment. Query it for the active list — only mints from this list can be added to a policy:

```bash
# devnet — use `mainnet-beta` for live mode
curl -X POST https://api.openfort.io/rpc/solana/devnet \
  -H "Authorization: Bearer $YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "getSupportedTokens",
    "params": [],
    "id": 1
  }'
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tokens": [
      "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    ]
  }
}
```

See the [`getSupportedTokens`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#getsupportedtokens) endpoint reference for details.

### Common stablecoin SPL mints

These are the mainnet mint addresses for the stablecoins most projects accept as fee tokens. A mint is only usable for fees once it appears in `getSupportedTokens` for your environment — always confirm the active list before creating a policy.

| Network | Token | Mint address | Token program |
|---------|-------|--------------|---------------|
| **Solana mainnet-beta** | USDC | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | SPL Token |
| **Solana mainnet-beta** | USDT | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` | SPL Token |
| **Solana mainnet-beta** | USDG | `2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH` | Token-2022 |
| **Solana devnet** | USDC | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | SPL Token |

:::info
USDG uses the [Token-2022](https://solana.com/docs/tokens) program rather than the classic SPL Token program. Confirm it is returned by `getSupportedTokens` before adding it to a policy.
:::

## Create the gas sponsorship

::::steps
### Create a policy for the sponsorship rules

Create a [policy](https://www.openfort.io/docs/configuration/policies) with the `sponsorSolTransaction` operation. Add Solana criteria (`solAddress`, `programId`, `mintAddress`, …) to restrict which transactions are eligible, or leave `criteria` empty to accept every Solana transaction:

```bash
curl -X POST https://api.openfort.io/v2/policies \
  -H "Authorization: Bearer $YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "project",
    "description": "Sponsor Solana transactions paid in SPL",
    "rules": [
      {
        "action": "accept",
        "operation": "sponsorSolTransaction",
        "criteria": []
      }
    ]
  }'
```

### Create the gas sponsorship with SPL tokens

Link the policy to a gas sponsorship and pass the accepted SPL mints in `strategy.splTokens`:

```bash
curl -X POST https://api.openfort.io/v2/fee-sponsorship \
  -H "Authorization: Bearer $YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pay gas in USDC on Solana",
    "strategy": {
      "sponsorSchema": "charge_custom_tokens",
      "splTokens": [
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      ]
    },
    "policyId": "ply_..."
  }'
```

The response echoes the accepted mints in `strategy.splTokens`:

```json
{
  "id": "pol_...",
  "object": "feeSponsorship",
  "enabled": true,
  "strategy": {
    "sponsorSchema": "charge_custom_tokens",
    "splTokens": [
      "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    ]
  },
  "policyId": "ply_..."
}
```

:::tip
You can list multiple mints in `splTokens` to give end-users a choice of fee tokens. The user's transaction picks the actual fee mint by including an SPL `TransferChecked` to Kora in one of them.
:::
::::

## Construct the user transaction

Your client builds the user's transaction with two things:

* The business instructions (transfer, swap, …) with the user as signer.
* An SPL [`TransferChecked`](https://spl.solana.com/token#transferring-tokens) paying the Kora fee payer in one of the accepted mints. The amount should cover Kora's fee — use [`estimateTransactionFee`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#estimatetransactionfee) with `fee_token` set to the mint to get a quote.

Only `TransferChecked` (token program instruction `12`) is recognised — the legacy `Transfer` instruction is not, because `TransferChecked` carries the mint inline.

Then call [`signTransaction`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#signtransaction) or [`signAndSendTransaction`](https://www.openfort.io/docs/products/infrastructure/paymaster/solana/endpoints#signandsendtransaction). If the policy enforces a specific contract scope, pass `policyId`:

```bash
curl -X POST https://api.openfort.io/rpc/solana/mainnet-beta \
  -H "Authorization: Bearer $YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "signAndSendTransaction",
    "params": {
      "transaction": "<base64-encoded transaction>",
      "policyId": "pol_..."
    },
    "id": 1
  }'
```

If the transaction doesn't include a `TransferChecked` to an accepted mint, the request is rejected with a policy-violation error.

## API reference

### Create gas sponsorship

`POST /v2/fee-sponsorship`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | No | Display name |
| `strategy.sponsorSchema` | string | Yes | Must be `charge_custom_tokens` |
| `strategy.splTokens` | string\[] | Yes | SPL mint addresses (base58, 32–44 chars) accepted as fee payment. Duplicates are rejected. Must come from `getSupportedTokens` |
| `policyId` | string | Yes | Policy ID (`ply_...`) with a `sponsorSolTransaction` rule |

:::warning
`splTokens` is mutually exclusive with `tokenContract` and `tokenContractAmount`. Mixing them returns `400 Bad Request`. `tokenContractAmount` is also rejected on Solana SPL policies because Kora computes the exchange rate at request time.
:::
