Skip to content
Alchemy Logo

Sponsor fees & rent on Solana

Fees and rent are a significant barrier to entry for new users of your app. Sponsor them to let users transact without holding SOL.

When you request sponsorship for a transaction using a configured policy, the policy engine determines if that transaction is eligible. If eligible, the Gas Manager sets the fee payer, signs the transaction, and returns the updated serialized transaction. The sponsored cost is added to your monthly bill.

  • Fees: the cost of executing transactions. Alchemy's fee payer replaces yours on every sponsored transaction.
  • Rent: the minimum payment to store data onchain.
    • Top-level rent sponsorship: on every sponsored transaction, any top-level SystemProgram.createAccount and Associated Token Program Create/CreateIdempotent instructions are rewritten so the paymaster funds them directly. You do not need to opt in — this happens for all requests.
    • CPI rent sponsorship: for account creation that happens inside a cross-program invocation (CPI), opt in per request with prefundRent: true. See CPI rent prefunding below — this mode adds a hard requirement on payerKey.
    • We do not support sponsoring rent for SystemProgram.createAccountWithSeed.

  • API key from your dashboard
  • Wallet APIs for Solana set up in your project if you want to enable sign up/login for creation of wallets
  • A sponsorship policy to cover fees and/or rent: create a policy

Here’s an example of creating a serialized transfer transaction using JavaScript:

example.ts
import * as solanaWeb3 from "@solana/web3.js";
import { connection, keypair } from "./config.ts";
 
const instructions = [
  solanaWeb3.SystemProgram.transfer({
    fromPubkey: keypair.publicKey,
    toPubkey: keypair.publicKey,
    lamports: 5,
  }),
];
 
const { blockhash } = await connection.getLatestBlockhash();
 
const message = new solanaWeb3.TransactionMessage({
  payerKey: new solanaWeb3.PublicKey(
    // Placeholder: Set this to an address different than any other address included in the tx, will be replaced by alchemy's feePayer
    "Amh6quo1FcmL16Qmzdugzjq3Lv1zXzTW7ktswyLDzits",
  ),
  recentBlockhash: blockhash,
  instructions,
}).compileToV0Message();
 
const transaction = new solanaWeb3.VersionedTransaction(message);
export const serializedTransaction = Buffer.from(transaction.serialize()).toString(
  "base64",
);

To sponsor fees on Solana, 1) set the payerKey field of the transaction, and 2) call alchemy_requestFeePayer.

alchemy_requestFeePayer updates the feePayer and adds the fee-payer signature if the transaction satisfies the rules defined in your policy.

What to put at payerKey depends on the request:

  • Default (fees + top-level rent): any placeholder address that is not otherwise referenced in the transaction. The paymaster replaces it as the fee payer, and any top-level createAccount/createAssociatedTokenAccount instructions are rewritten so the paymaster funds them — the identity of the placeholder does not affect sponsorship.
  • With prefundRent: true: the user's wallet, specifically the account your program's CPI will debit for rent. See CPI rent prefunding for the full contract.
example.ts
import {
  ALCHEMY_API_KEY,
  POLICY_ID,
  RPC_URL,
  type AlchemyFeePayerResponse,
} from "./config.ts";
import { serializedTransaction } from "./prepare.ts";
 
const response = await fetch(RPC_URL, {
  method: "POST",
  headers: {
    accept: "application/json",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    id: 1,
    jsonrpc: "2.0",
    method: "alchemy_requestFeePayer",
    params: [{ policyId: POLICY_ID, serializedTransaction }],
  }),
});
 
const data = (await response.json()) as AlchemyFeePayerResponse;
const serializedSponsoredTransaction = data.result?.serializedTransaction;

Here is an example of signing and broadcasting a transaction using JavaScript:

example.ts
import { VersionedTransaction } from "@solana/web3.js";
import { connection, keypair } from "./config.ts";
import { sponsoredSerializedTransaction } from "./request.ts";
 
const tx = VersionedTransaction.deserialize(
  Buffer.from(sponsoredSerializedTransaction, "base64"),
);
 
tx.sign([keypair]);
 
const signature = await connection.sendRawTransaction(tx.serialize());

Use prefundRent: true to opt into cross-program invocation (CPI) rent prefunding when a transaction may create accounts through CPI.

This feature is allowlisted. Contact support@alchemy.com to request access.

{
  "policyId": "<POLICY_ID>",
  "serializedTransaction": "<base64-serialized-tx>",
  "prefundRent": true
}

When prefundRent is enabled, the service simulates the sponsored transaction to estimate any rent that may be needed for CPI-driven account creation. If needed, it adds a SystemProgram.transfer from the paymaster to the user's wallet — signaled by being the original payerKey on the submitted transaction, before the paymaster replaces it as fee payer — so the user's wallet holds enough SOL to cover the CPI's account creation.

When a prefund simulation runs, the response includes prefundLamports and simulationSlot alongside the normal fee sponsorship fields. Both are omitted when no simulation ran — either because prefundRent was not requested or because the fee payer was replaced rather than injected, leaving no user wallet to top up.

We support sponsoring rent for inner SystemProgram.createAccount calls. SystemProgram.createAccountWithSeed is not sponsored today.

  • payerKey MUST be set to the user's wallet — the same account your program's CPI will debit for rent. This is different from fee and top-level rent sponsorship, where payerKey can be any placeholder address. With prefundRent: true, the service targets payerKey by position: it transfers SOL to whatever account sits there, and only detects inner createAccount calls funded by that same account. If payerKey is a placeholder (or any account other than the CPI funder), prefunding silently misses and the transaction will fail on-chain for lack of rent.
  • Set maxSpendPerTxnUsd on every policy used with prefundRent. The request is rejected if the policy does not have this cap.

  • Enable prefundRent only for flows that may create accounts through CPI.
  • Submit the returned transaction promptly from your backend after collecting signatures.
  • For extra protection against stale state, when the response includes simulationSlot, consider passing it as minContextSlot when submitting the transaction.

  • Simulation is not the same as execution. Account state can change between sponsorship and submission, and programs can take different CPI paths at execution time.
    • minContextSlot reduces stale-state risk, but it does not guarantee identical execution.
    • If execution needs more lamports than the simulation predicted, the transaction will fail. If execution needs less, the extra SOL remains in the funded wallet and is still billed to your policy.
  • Important: Only use prefundRent for payloads you control or can validate. If an attacker can influence the transaction or the programs it invokes, they may be able to grief your policy by causing failed or unnecessarily expensive prefunds.
  • prefundRent is not transaction-shape-preserving. When prefunding is needed, the returned transaction includes an added top-level SystemProgram.transfer instruction before your program flow. Programs that inspect sibling instructions or depend on exact top-level instruction ordering can behave differently or fail.
  • Durable nonce transactions have a simulation caveat: the service simulates a modified copy without AdvanceNonceAccount, then returns a nonce-preserving transaction for submission.
Was this page helpful?