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

# Sponsor Solana Transaction Fees

> Use Para's Solana Kit signer with an app-owned fee payer so users can transact without holding SOL

Solana fee sponsorship only requires two wallets: the user's wallet and a funded sponsor wallet controlled by your app. Solana lets the user authorize an action while the sponsor wallet pays its network fee. This requires two signatures on the same transaction:

1. The user's authenticated Para wallet signs the transaction in the client.
2. Your app's sponsor wallet signs as the fee payer on the server.

The user's signature authorizes the action from their wallet. The sponsor's signature confirms that your app agrees to pay the network fee.

<Warning>
  Keep the Para partner API key and sponsor signer on your server. Before adding the sponsor signature, validate that the transaction matches an action your app allows and that the configured sponsor is its fee payer. Never sponsor arbitrary transaction bytes submitted by a client.
</Warning>

## Install

Follow [Setup Solana Libraries](/v3/react/guides/web3-operations/solana/setup-libraries), then install the packages used in this guide:

```bash theme={null}
npm install @getpara/react-sdk @getpara/rest-sdk @solana/kit@2.3.0 @solana-program/system@0.7.0
```

<Info>
  Para's Solana signers use the Solana Kit v2 signer interfaces. The versions above are compatible with the Para signer types used in this guide.
</Info>

## 1. Create a Sponsor Wallet

The sponsor can be any Solana wallet your app controls and keeps funded with SOL. You can create and manage a keypair yourself, use a Para pregenerated wallet with a server-side signer, or create a [REST API wallet](/v3/rest/overview).

This guide uses a REST API wallet. Para manages the private key material, while your application keeps the wallet ID and address needed to use it. Run this code once from a trusted server environment:

```ts theme={null}
import { ParaRestClient } from "@getpara/rest-sdk";

const para = new ParaRestClient({
  apiKey: process.env.PARA_API_KEY!,
  env: "BETA",
});

const sponsorWallet = await para.createWallet(
  {
    type: "SOLANA",
    userIdentifier: "solana-fee-sponsor",
    userIdentifierType: "CUSTOM_ID",
  },
  { idempotencyKey: "solana-fee-sponsor" },
);

console.log(sponsorWallet);
```

The API may return the wallet with a `creating` status. Use `para.getWallet(sponsorWallet.id)` until the wallet is `ready` and has an address.

Retain the ready wallet's `id` and `address` wherever your application keeps its configuration. Fund the address with enough SOL to cover transaction fees. The examples below use `SPONSOR_WALLET_ID` and `SPONSOR_WALLET_ADDRESS` as placeholders for those two stored values.

<Note>
  You can store the returned wallet ID and address, or keep the `CUSTOM_ID` and retrieve the wallet later with `para.listWallets()`. Use whichever approach fits your existing server configuration.
</Note>

This REST wallet is now the app's fee payer. See [REST API Wallets](/v3/rest/overview) for wallet management options.

## 2. Sign with the User's Wallet

Run this code in the client after the user authenticates with Para. The sponsor's public address is the fee payer, while the user's Para signer remains the authority for the transfer instruction.

```tsx theme={null}
import { useParaSolanaSigner } from "@getpara/react-sdk";
import {
  type Address,
  address,
  appendTransactionMessageInstruction,
  createSolanaRpc,
  createTransactionMessage,
  getBase64EncodedWireTransaction,
  lamports,
  partiallySignTransactionMessageWithSigners,
  pipe,
  setTransactionMessageFeePayer,
  setTransactionMessageLifetimeUsingBlockhash,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";

const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const sponsorAddress = address("SPONSOR_WALLET_ADDRESS");

export function SponsoredTransfer({ recipient }: { recipient: Address }) {
  const { solanaSigner } = useParaSolanaSigner({ rpc });

  const signTransaction = async () => {
    if (!solanaSigner) return;

    const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
    const transferInstruction = getTransferSolInstruction({
      source: solanaSigner,
      destination: recipient,
      amount: lamports(100_000n),
    });

    const transactionMessage = pipe(
      createTransactionMessage({ version: "legacy" }),
      (tx) => setTransactionMessageFeePayer(sponsorAddress, tx),
      (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
      (tx) => appendTransactionMessageInstruction(transferInstruction, tx),
    );

    const userSignedTransaction =
      await partiallySignTransactionMessageWithSigners(transactionMessage);

    const transactionBase64 =
      getBase64EncodedWireTransaction(userSignedTransaction);

    // Pass this value to your server using your application's existing API.
    return transactionBase64;
  };

  return <button onClick={signTransaction}>Create sponsored transfer</button>;
}
```

`partiallySignTransactionMessageWithSigners` calls the user's Para signer because it is attached to the transfer instruction. `transactionBase64` contains the user's signature, but it cannot be broadcast until the sponsor signs it.

Send the returned base64 transaction to your server using your application's existing client-to-server transport.

<Note>
  You can send this value through an existing authenticated API. Fee sponsorship does not require a particular route, framework, or authentication model.
</Note>

## 3. Sign with the Sponsor Wallet

Run this code on your server after it receives the user's partially signed transaction. Create a Para REST Solana signer from the sponsor wallet details saved in step 1, then add its signature to the transaction.

The signer needs the wallet ID to tell Para which wallet should sign. It needs the address so Solana Kit can place the returned signature in the fee payer's signature slot.

```ts theme={null}
import { ParaRestClient } from "@getpara/rest-sdk";
import { createParaRestSolanaSigner } from "@getpara/rest-sdk/solana";
import {
  getBase64Encoder,
  getTransactionDecoder,
} from "@solana/kit";

const para = new ParaRestClient({
  apiKey: process.env.PARA_API_KEY!,
  env: "BETA",
});

const sponsorSigner = createParaRestSolanaSigner({
  client: para,
  walletId: "SPONSOR_WALLET_ID",
  address: "SPONSOR_WALLET_ADDRESS",
});

export async function addSponsorSignature(transactionBase64: string) {
  const transaction = getTransactionDecoder().decode(
    getBase64Encoder().encode(transactionBase64),
  );

  // Validate the transaction against your sponsorship rules before signing.

  const [sponsorSignature] = await sponsorSigner.signTransactions([
    transaction,
  ]);
  const sponsoredTransaction = {
    ...transaction,
    signatures: {
      ...transaction.signatures,
      ...sponsorSignature,
    },
  };

  return sponsoredTransaction;
}
```

The sponsor signs the same transaction bytes that the user approved. The returned transaction now contains both the user's signature and the sponsor's fee-payer signature.

<Note>
  Apply your existing authorization rules before the sponsor signs. Check the expected user signer, allowed programs and instructions, transaction amounts, and fee limits.
</Note>

## 4. Submit the Transaction

Both signatures are now present. Submit the transaction with a Solana RPC client:

```ts theme={null}
import {
  type Transaction,
  createSolanaRpc,
  getBase64EncodedWireTransaction,
} from "@solana/kit";

const rpc = createSolanaRpc(process.env.SOLANA_RPC_URL!);

export function submitTransaction(sponsoredTransaction: Transaction) {
  return rpc
    .sendTransaction(
      getBase64EncodedWireTransaction(sponsoredTransaction),
      {
        encoding: "base64",
        skipPreflight: false,
      },
    )
    .send();
}
```

Pass the transaction returned by `addSponsorSignature` to `submitTransaction`. The network verifies both signatures, executes the user's authorized instructions, and charges the transaction fee to the sponsor wallet.

## Self-Managed Sponsor Wallet

If you manage the sponsor key yourself, replace `createParaRestSolanaSigner` with a Solana Kit keypair signer loaded from a base64-encoded 64-byte secret key in your server environment:

```ts theme={null}
import { createKeyPairSignerFromBytes } from "@solana/kit";

const sponsorSecretKey = new Uint8Array(
  Buffer.from(process.env.SOLANA_SPONSOR_SECRET_KEY_BASE64!, "base64"),
);

const sponsorSigner = await createKeyPairSignerFromBytes(sponsorSecretKey);
```

A Para [pregenerated wallet](/v3/server/guides/pregen) with a server-side Solana signer also works. In every case, the user signs first, the sponsor adds the second signature, and the server submits the transaction. For more detail on the native fee-payer model, see Solana's [Fee Sponsorship cookbook](https://solana.com/developers/cookbook/transactions/fee-sponsorship).
