PaymentsSubscriptions
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:
- Create a client with the user signer and subscriptions plugin.
- Derive the user's token account, Subscription Authority PDA, and recurring delegation PDA.
- Initialize the Subscription Authority if it does not exist yet.
- 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
amountPerPeriodis in base units. For a 6-decimal token,1_000_000means1token.- The program rejects transfers that exceed the current period's remaining allowance.
- Once the next period starts, the pulled amount resets.
- The user signs setup and revoke transactions. The delegatee signs transfers.
Is this page helpful?