# Solana sponsorship

> Sponsor Solana transaction fees and rent with Wallet APIs

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

Use Solana sponsorship to cover transaction fees and rent for Solana calls submitted through `@alchemy/wallet-apis`.

## Prerequisites

* API key from your [dashboard](https://dashboard.alchemy.com/apps)
* Solana gas sponsorship policy from the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager/policy/create)
* Solana signer configured in your app. See [Solana signer adapters](/docs/wallets/solana/signers).

## Implementation

<Tabs>
  <Tab title="JavaScript" language="typescript">
    Use `paymaster` on the smart wallet client to apply a Solana sponsorship policy to all `sendCalls` and `prepareCalls` requests from that client.

<CodeBlocks>
  ```ts title="sendCalls.ts"
  import { client } from "./client.ts";
  import { transferSolCall } from "./utils.ts";

  const { id } = await client.sendCalls({
    calls: [
      transferSolCall({
        from: client.solanaAccount,
        to: client.solanaAccount,
        lamports: 0n,
      }),
    ],
  });

  const result = await client.waitForCallsStatus({ id });

  console.log(result);
  ```

  ```ts title="client.ts"
  import {
    alchemyWalletTransport,
    createSmartWalletClient,
  } from "@alchemy/wallet-apis";
  import { fromKitSigner } from "@alchemy/wallet-apis/solana";
  import { createKeyPairSignerFromBytes } from "@solana/kit";

  export const config = {
    policyId: process.env.SOLANA_POLICY_ID!,
  };

  const secretKeyBytes = new Uint8Array(
    Buffer.from(process.env.SOLANA_SECRET_KEY!, "base64"),
  );
  const kitSigner = await createKeyPairSignerFromBytes(secretKeyBytes);

  export const client = createSmartWalletClient({
    signer: fromKitSigner(kitSigner),
    transport: alchemyWalletTransport({
      apiKey: process.env.ALCHEMY_API_KEY!,
    }),
    chain: "solana:devnet",
    paymaster: { policyId: config.policyId },
  });
  ```

  ```ts title="utils.ts"
  import {
    getTransferSolInstructionDataEncoder,
    SYSTEM_PROGRAM_ADDRESS,
  } from "@solana-program/system";
  import { address } from "@solana/kit";
  import { bytesToHex } from "viem";

  export function transferSolCall(args: {
    from: string;
    to: string;
    lamports: bigint;
  }) {
    const source = address(args.from);
    const destination = address(args.to);
    const data = bytesToHex(
      getTransferSolInstructionDataEncoder().encode({
        amount: args.lamports,
      }),
    );

    return {
      programId: SYSTEM_PROGRAM_ADDRESS,
      accounts: [
        { pubkey: source, isSigner: true, isWritable: true },
        { pubkey: destination, isSigner: false, isWritable: true },
      ],
      data,
    };
  }
  ```
</CodeBlocks>

  </Tab>

  <Tab title="API" language="bash">
    Sponsorship is applied by passing `capabilities.paymasterService` to
`wallet_prepareCalls`. The examples below use `curl` and `jq`; signing is
wallet-dependent because Solana signatures are Ed25519.

See the [`wallet_prepareCalls`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-prepare-calls), [`wallet_sendPreparedCalls`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-send-prepared-calls), and [`wallet_getCallsStatus`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-get-calls-status) API references for full parameter descriptions.

<Steps>

<Step title="Declare your secrets and call data">

The call below is a 0-lamport System Program self-transfer. Its instruction data
is `SystemInstruction::Transfer` (`2`) plus a little-endian `u64` lamport amount.

```bash twoslash
ALCHEMY_API_KEY=your-alchemy-api-key
SOLANA_POLICY_ID=your-solana-policy-id
CHAIN_ID=solana:devnet
SIGNER_ADDRESS=your-solana-address

RPC_URL="https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY"
SYSTEM_PROGRAM_ID=11111111111111111111111111111111
TRANSFER_0_LAMPORT_DATA=0x020000000000000000000000
```

</Step>

<Step title="Prepare calls with sponsorship">

Attach `capabilities.paymasterService.policyId` to apply your Solana sponsorship
policy. Add `prefundRent: true` to opt in to CPI rent prefunding, and
`webhookData` to pass extra context to custom rules.

```bash twoslash
PREPARE_CALLS_RESPONSE=$(jq -n \
  --arg signer "$SIGNER_ADDRESS" \
  --arg chain_id "$CHAIN_ID" \
  --arg policy_id "$SOLANA_POLICY_ID" \
  --arg system_program "$SYSTEM_PROGRAM_ID" \
  --arg transfer_data "$TRANSFER_0_LAMPORT_DATA" \
  '{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "wallet_prepareCalls",
    "params": [
      {
        "calls": [
          {
            "programId": $system_program,
            "accounts": [
              {
                "pubkey": $signer,
                "isSigner": true,
                "isWritable": true
              },
              {
                "pubkey": $signer,
                "isSigner": false,
                "isWritable": true
              }
            ],
            "data": $transfer_data
          }
        ],
        "from": $signer,
        "chainId": $chain_id,
        "capabilities": {
          "paymasterService": {
            "policyId": $policy_id
          }
        }
      }
    ]
  }' | curl --request POST \
    --url "$RPC_URL" \
    --header 'accept: application/json' \
    --header 'content-type: application/json' \
    --data @-)

echo "$PREPARE_CALLS_RESPONSE" | jq '.result.feePayment'
```

</Step>

<Step title="Sign the prepared transaction">

`signatureRequest.data` already contains the paymaster signature embedded; the
user only needs to add their Ed25519 signature.

For a raw Kit keypair, decode `signatureRequest.data` with
`getTransactionDecoder()`, sign with
`partiallySignTransaction([signer.keyPair], transaction)`, and base58-encode the
entry in `signed.signatures[signer.address]` with `getBase58Decoder()`.

```bash twoslash
SIGNATURE_REQUEST_HEX=$(echo "$PREPARE_CALLS_RESPONSE" | jq -r '.result.signatureRequest.data')

# Sign SIGNATURE_REQUEST_HEX with your Solana wallet.
# The signature must be the user's base58 Ed25519 signature.
SIGNATURE_BASE58=your-base58-solana-signature
```

</Step>

<Step title="Send and poll">

```bash twoslash
CALL_TYPE=$(echo "$PREPARE_CALLS_RESPONSE" | jq -r '.result.type')
CALL_DATA=$(echo "$PREPARE_CALLS_RESPONSE" | jq -c '.result.data')

SEND_CALLS_RESPONSE=$(jq -n \
  --arg call_type "$CALL_TYPE" \
  --arg chain_id "$CHAIN_ID" \
  --argjson call_data "$CALL_DATA" \
  --arg signature "$SIGNATURE_BASE58" \
  '{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "wallet_sendPreparedCalls",
    "params": [
      {
        "type": $call_type,
        "chainId": $chain_id,
        "data": $call_data,
        "signature": {
          "type": "ed25519",
          "data": $signature
        }
      }
    ]
  }' | curl --request POST \
    --url "$RPC_URL" \
    --header 'accept: application/json' \
    --header 'content-type: application/json' \
    --data @-)

CALL_ID=$(echo "$SEND_CALLS_RESPONSE" | jq -r '.result.id')

curl --request POST \
  --url "$RPC_URL" \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --data '
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "wallet_getCallsStatus",
  "params": [
    "'$CALL_ID'"
  ]
}'
```

</Step>

</Steps>

<Warning>
  CPI rent prefunding (`prefundRent: true`) is allowlisted. Contact
  [support@alchemy.com](mailto:support@alchemy.com) to request access. Policies
  used with `prefundRent` must set `maxSpendPerTxnUsd`, and the transaction
  payer must be the user's wallet.
</Warning>

  </Tab>
</Tabs>

## Advanced

<Accordion title="Per-request policy">
  Pass `capabilities.paymaster` to override the client-level policy for a single `sendCalls` or `prepareCalls` request. Request-level `capabilities.paymaster.policyId` takes priority over the client-level policy.

  ```ts title="sponsor-solana-request.ts"
import { client } from "./client.ts";
import { transferSolCall } from "./utils.ts";

const { id } = await client.sendCalls({
  calls: [
    transferSolCall({
      from: client.solanaAccount,
      to: client.solanaAccount,
      lamports: 0n,
    }),
  ],
  capabilities: {
    paymaster: {
      policyId: "OTHER_SOLANA_POLICY_ID",
    },
  },
});

const result = await client.waitForCallsStatus({ id });
```

</Accordion>

<Accordion title="Rent sponsorship">
  Solana sponsorship can also cover rent for account creation.

  Top-level rent sponsorship applies automatically when a sponsored transaction includes top-level `SystemProgram.createAccount` or Associated Token Program `Create` / `CreateIdempotent` instructions. No extra request parameter is required for those top-level instructions.

  For accounts created inside a cross-program invocation (CPI), opt in per request with `prefundRent: true`. This asks the service to simulate the transaction, estimate the CPI rent requirement, and prefund the user's wallet before the transaction is submitted.

  ```ts title="sponsor-solana-cpi-rent.ts"
import { client, config } from "./client.ts";
import { transferSolCall } from "./utils.ts";

const { id } = await client.sendCalls({
  calls: [
    transferSolCall({
      from: client.solanaAccount,
      to: client.solanaAccount,
      lamports: 0n,
    }),
  ],
  capabilities: {
    paymaster: {
      policyId: config.policyId,
      prefundRent: true,
    },
  },
});

const result = await client.waitForCallsStatus({ id });
```


  <Warning>
    CPI rent prefunding is allowlisted. Contact
    [support@alchemy.com](mailto:support@alchemy.com) to request access. Policies
    used with `prefundRent` must set `maxSpendPerTxnUsd`, and the transaction
    payer must be the user's wallet: the same account your program's CPI will
    debit for rent.
  </Warning>
</Accordion>

<Info>
  If you are not using the Wallet APIs client and need to sponsor a serialized
  Solana transaction directly, use the lower-level
  [`alchemy_requestFeePayer`](/docs/wallets/transactions/solana/sponsor-gas)
  flow.
</Info>

## Next steps

Build more:

* [Send Solana transactions](/docs/wallets/transactions/send-transactions#solana-transactions)
* [Use Solana signer adapters](/docs/wallets/solana/signers)

Troubleshooting:

* [Gas manager errors](/docs/reference/gas-manager-errors)