Recurring Delegation

A recurring delegation lets a user approve another wallet or service to pull up to a limit that resets every period.

This guide keeps the moving parts visible. You derive the accounts first, initialize the Subscription Authority if needed, create the recurring delegation, then use that delegation PDA for transfers.

Install

pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token

Create The Delegation

The setup has four parts:

  1. Create a client with the user signer and subscriptions plugin.
  2. Derive the user's token account, Subscription Authority PDA, and recurring delegation PDA.
  3. Initialize the Subscription Authority if it does not exist yet.
  4. Create the recurring delegation with the period amount, period length, start time, and expiry.
import { address, createClient } from '@solana/kit';
import { solanaLocalRpc } from '@solana/kit-plugin-rpc';
import { signer } from '@solana/kit-plugin-signer';
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
import {
fetchMaybeSubscriptionAuthority,
findRecurringDelegationPda,
findSubscriptionAuthorityPda,
subscriptionsProgram,
} from '@solana/subscriptions';
const client = createClient()
.use(signer(userSigner))
.use(solanaLocalRpc({ rpcUrl: 'http://127.0.0.1:8899' }))
.use(subscriptionsProgram());
const tokenMint = address('TOKEN_MINT_ADDRESS_HERE');
const delegatee = address('DELEGATEE_WALLET_ADDRESS_HERE');
const now = BigInt(Math.floor(Date.now() / 1000));
const nonce = 0n;
const amountPerPeriod = 1_000_000n;
const periodLengthS = 86_400n;
const startTs = now;
const expiryTs = now + periodLengthS * 30n;
const [userAta] = await findAssociatedTokenPda({
mint: tokenMint,
owner: userSigner.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
const [subscriptionAuthorityPda] = await findSubscriptionAuthorityPda({
user: userSigner.address,
tokenMint,
});
const [delegationPda] = await findRecurringDelegationPda({
subscriptionAuthority: subscriptionAuthorityPda,
delegator: userSigner.address,
delegatee,
nonce,
});
const subscriptionAuthority = await fetchMaybeSubscriptionAuthority(
client.rpc,
subscriptionAuthorityPda,
);
if (!subscriptionAuthority.exists) {
await client.subscriptions.instructions
.initSubscriptionAuthority({
tokenMint,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
userAta,
})
.sendTransaction();
}
await client.subscriptions.instructions
.createRecurringDelegation({
tokenMint,
delegatee,
nonce,
amountPerPeriod,
periodLengthS,
startTs,
expiryTs,
})
.sendTransaction();

Transfer From The Delegation

The delegatee signs each transfer. The program checks the current period and rejects transfers that would exceed the period's remaining allowance.

const receiverAta = address('RECEIVER_TOKEN_ACCOUNT_ADDRESS_HERE');
await client.subscriptions.instructions
.transferRecurring({
delegatee: delegateeSigner,
delegator: userSigner.address,
delegatorAta: userAta,
tokenMint,
delegationPda,
amount: 100_000n,
receiverAta,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
})
.sendTransaction();

Revoke The Delegation

The delegator can revoke the recurring delegation at any time. Revoking closes the delegation PDA and returns its rent to the signer.

await client.subscriptions.instructions
.revokeDelegation({
authority: userSigner,
delegationAccount: delegationPda,
})
.sendTransaction();

Notes

  • amountPerPeriod is in base units. For a 6-decimal token, 1_000_000 means 1 token.
  • The program rejects transfers that exceed the current period's remaining allowance.
  • Once the next period starts, the pulled amount resets.
  • expiryTs is a hard stop: once it passes, transfers fail and the sponsor can recover rent. There is no spend-time grace period.
  • Set startTs to 0 to start the delegation when the transaction lands on chain instead of at a fixed time. This widens the window to get the user to sign and onboard. When you do this, expiryTs must be non-zero — a start-on-landing delegation cannot also be set to never expire.
  • The user signs setup and revoke transactions. The delegatee signs transfers.

Is this page helpful?

Table of Contents

Edit Page
© 2026 Solana Foundation. All rights reserved.