# How to set up a Kora fee payer without running a Kora node

This guide shows you how to give your Solana app a [Kora](https://github.com/solana-foundation/kora) fee payer by pointing `@solana/kora`'s `KoraClient` at Pimlico, which runs a hosted fee payer that speaks the Kora JSON-RPC API, instead of deploying `kora rpc` yourself. Use it if you want Kora's client flow without hosting the node, its signer keys and its SOL balance. If you want to compare this with running your own fee payer or self-hosting Kora, see [Solana fee sponsorship](/guides/conceptual/solana-fee-sponsorship).

## Prerequisites

* A Pimlico API key. [Create one](/guides/create-api-key).
* On mainnet, a Pimlico balance or card in the [dashboard](https://dashboard.pimlico.io). Fees you sponsor are charged to it. Devnet sponsorship is free.

## Steps

:::steps
### Install the SDK

```sh
npm install @solana/kora @solana/kit@6 @solana-program/token@0.12
```

`@solana/kora` 0.2 is built on `@solana/kit` 6, so install the matching versions of `@solana/kit` and `@solana-program/token`.

### Point KoraClient at Pimlico

Use the Pimlico URL as `rpcUrl`, where you would otherwise use your Kora node's URL (such as `http://localhost:8080`). Pass your API key in the URL's `apikey` query parameter, not in `KoraClient`'s `apiKey` option: that option sends an `x-api-key` header, which Pimlico does not read.

```ts
import { KoraClient } from "@solana/kora";

const kora = new KoraClient({
    rpcUrl: "https://solana.pimlico.io/v1/mainnet/rpc?apikey=YOUR_API_KEY",
});
```

To use devnet, replace `mainnet` with `devnet` in the URL.

### Check the fee payer responds

Call `getPayerSigner`. If it returns an address, your client is connected and that address is the fee payer for your transactions.

```ts
const { signer_address } = await kora.getPayerSigner();
console.log(`Fee payer: ${signer_address}`);
```

### Build the transaction with the fee payer

Add this helper to your app. Pass it your instructions, and it returns a transaction message with the Pimlico fee payer as the fee payer and its signature slot left empty.

```ts [sponsored-transaction.ts]
import type { KoraClient } from "@solana/kora";
import {
    type Instruction,
    address,
    appendTransactionMessageInstructions,
    blockhash,
    createNoopSigner,
    createTransactionMessage,
    pipe,
    setTransactionMessageFeePayerSigner,
    setTransactionMessageLifetimeUsingBlockhash,
} from "@solana/kit";

export async function createSponsoredTransactionMessage(
    kora: KoraClient,
    instructions: Instruction[],
) {
    const { signer_address } = await kora.getPayerSigner();
    const { blockhash: latestBlockhash } = await kora.getBlockhash();

    // pipe passes the message through each step below, in order
    return pipe(
        // Start an empty version 0 transaction message
        createTransactionMessage({ version: 0 }),
        // The paymaster pays the fee. It signs later, so a noop signer leaves its signature slot empty.
        (m) => setTransactionMessageFeePayerSigner(createNoopSigner(address(signer_address)), m),
        // The blockhash keeps the transaction valid for about a minute. lastValidBlockHeight is only
        // read by @solana/kit's own send-and-confirm, and the paymaster sends the transaction for you.
        (m) =>
            setTransactionMessageLifetimeUsingBlockhash(
                { blockhash: blockhash(latestBlockhash), lastValidBlockHeight: 0n },
                m,
            ),
        // Add your app's instructions (transfers, swaps, etc.)
        (m) => appendTransactionMessageInstructions(instructions, m),
    );
}
```

Use the fee payer only as the fee payer, or as the rent payer of an account your transaction creates. Any other use of it is rejected.

### Sign and send the transaction

Have your user sign, then call `signAndSendTransaction`. The fee payer validates and simulates the transaction, adds its signature, broadcasts it, and responds once it is `confirmed`. This example sends 1 USDC from a user who holds no SOL.

```ts
import {
    address,
    createKeyPairSignerFromBytes,
    getBase64EncodedWireTransaction,
    partiallySignTransactionMessageWithSigners,
} from "@solana/kit";
import {
    findAssociatedTokenPda,
    getTransferInstruction,
    TOKEN_PROGRAM_ADDRESS,
} from "@solana-program/token";
import { createSponsoredTransactionMessage } from "./sponsored-transaction";

const USDC = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

const user = await createKeyPairSignerFromBytes(USER_SECRET_KEY);
const recipient = address("RecipientPubkey11111111111111111111111111111");

const [source] = await findAssociatedTokenPda({
    owner: user.address,
    mint: USDC,
    tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
const [destination] = await findAssociatedTokenPda({
    owner: recipient,
    mint: USDC,
    tokenProgram: TOKEN_PROGRAM_ADDRESS,
});

const message = await createSponsoredTransactionMessage(kora, [
    getTransferInstruction({ source, destination, authority: user, amount: 1_000_000n }),
]);

// Your user signs as the token authority. The fee payer adds its signature.
const transaction = getBase64EncodedWireTransaction(
    await partiallySignTransactionMessageWithSigners(message),
);

const { signature } = await kora.signAndSendTransaction({ transaction });
console.log(`https://solscan.io/tx/${signature}`);
```

Open the link. The explorer shows the Pimlico fee payer as the fee payer, and the fee is charged to your Pimlico balance.
:::

## Replace a self-hosted Kora node

If you already run Kora, keep your client code and change `rpcUrl` to the Pimlico URL. Then use this table to see where each part of your Kora setup lives with Pimlico.

| On a self-hosted Kora node | With Pimlico |
|---|---|
| `kora rpc` server and its host | Pimlico runs it. Use `https://solana.pimlico.io/v1/{mainnet,devnet}/rpc`. |
| `signers.toml` (fee payer keys) and the SOL that funds them | Pimlico holds the fee payer key and pays the SOL. Read its address with `getPayerSigner`. Sponsored fees are charged to your Pimlico balance. |
| `[kora.auth]` `api_keys` | Your Pimlico API key, in the `apikey` query parameter or an `Authorization: Bearer` header. |
| `[validation]` `allowed_spl_paid_tokens` | Fixed list: USDC and USDT on mainnet, USDC on devnet. Read it with `getSupportedTokens`, and see [supported tokens](/guides/how-to/solana/supported-tokens). |
| `[validation]` `max_allowed_lamports` | Fixed at 0.1 SOL per transaction, including the fee, priority fee and rent. |
| `[validation.fee_payer_policy]` | Fixed: the fee payer may only pay the fee and the rent of accounts the transaction creates. |
| `[validation.price]` | The token fee is the SOL the fee payer spends, measured by simulation and priced in the token at the current price. |
| `[kora.enabled_methods]` | Pimlico implements `getPayerSigner`, `getBlockhash`, `getSupportedTokens`, `estimateTransactionFee` and `signAndSendTransaction`. See the [endpoint reference](/references/paymaster/solana/endpoints). |
| `[kora] rate_limit` | Set by your Pimlico plan. See [pricing](/guides/pricing). |

If your app uses a Kora method that isn't in the endpoint reference, such as `signTransaction` or `transferTransaction`, rebuild that flow around `signAndSendTransaction`.

## Let users pay the fee in USDC or USDT

To have your user repay the fee in a token instead of charging your Pimlico balance, call `kora.getPaymentInstruction` and append the returned transfer before your user signs. See [SPL Token Sponsorships](/guides/how-to/solana/token-sponsorship).

## Sign in a browser wallet

If your users sign in Phantom or another Wallet Standard wallet, use the wallet as the signer. See [Phantom and Other Wallets](/guides/how-to/solana/external-wallets). If they log in with Privy, see [Privy](/guides/how-to/solana/privy).

## Call the JSON-RPC API without KoraClient

To call the fee payer from another language, send Kora JSON-RPC requests to the same URL. Parameters go in a single object, with snake\_case fields.

```sh
curl -X POST "https://solana.pimlico.io/v1/devnet/rpc?apikey=YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"getPayerSigner","id":1}'
```

## Related

* [Solana fee sponsorship](/guides/conceptual/solana-fee-sponsorship)
* [Solana Sponsorships overview](/guides/how-to/solana)
* [Gas Sponsorships](/guides/how-to/solana/verifying-sponsorship)
* [Solana Sponsorships endpoints](/references/paymaster/solana/endpoints)
* [Solana Sponsorships errors](/references/paymaster/solana/errors)
* [Kora on GitHub](https://github.com/solana-foundation/kora)
