@solana/subscriptions
v0.5.0
Published
TypeScript SDK and @solana/kit plugin for the Subscriptions Solana program: token delegations, recurring payments, and subscription plans.
Maintainers
Readme
@solana/subscriptions
TypeScript SDK for the Subscriptions Solana program: token delegation, recurring payments, and subscriptions. Ships as a @solana/kit plugin.
Source & issues: https://github.com/solana-foundation/subscriptions
Installation
npm install @solana/subscriptionsQuick Start
The SDK exports a subscriptionsProgram() Kit plugin. The plugin derives program PDAs, fills the configured identity/payer where possible, and can send transactions directly through Kit.
import { address, createClient } from '@solana/kit';
import { solanaLocalRpc } from '@solana/kit-plugin-rpc';
import { signer } from '@solana/kit-plugin-signer';
import { subscriptionsProgram } from '@solana/subscriptions';
const client = createClient()
.use(signer(walletSigner))
.use(solanaLocalRpc({ rpcUrl: 'http://127.0.0.1:8899' }))
.use(subscriptionsProgram());
// 1. Initialize the SubscriptionAuthority for a user's token account (once per mint)
await client.subscriptions.instructions
.initSubscriptionAuthority({
tokenMint: address('TokenMintAddress...'),
userAta: address('...'),
tokenProgram: address('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
})
.sendTransaction();
// 2. Create a fixed delegation (e.g., allow spending 1,000,000 tokens)
await client.subscriptions.instructions
.createFixedDelegation({
tokenMint: address('TokenMintAddress...'),
delegatee: address('DelegateeAddress...'),
nonce: 0n,
amount: 1_000_000n,
expiryTs: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour
})
.sendTransaction();For custom wallet flows, use the exported get*OverlayInstruction* functions. They return a single Kit Instruction or Promise<Instruction> that you can add to your own transaction builder.
Sponsored (gasless) flows
Installing a distinct payer() alongside identity() makes that signer sponsor rent, not just fees, on every creation instruction (init-authority, create-delegation, subscribe, create-plan). Some sponsored rent is not unilaterally recoverable by the sponsor: an open SubscriptionAuthority closes only by the user, non-expiring delegations and perpetual subscriptions stay open indefinitely, and sponsored plan rent refunds to the merchant on delete. Prefer finite expiryTs/endTs when sponsoring, and enforce per-user and aggregate rent quotas relayer-side. To cover fees without sponsoring rent, set the instruction's payer to the user so rent is self-funded while client.payer still pays the fee.
Capabilities
Delegation Management
| Plugin instruction / builder | Description |
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| initSubscriptionAuthority / getInitSubscriptionAuthorityOverlayInstructionAsync | Set up the per-mint SubscriptionAuthority PDA and token approval |
| closeSubscriptionAuthority / getCloseSubscriptionAuthorityOverlayInstructionAsync | Close the SubscriptionAuthority PDA and reclaim rent. Does not revoke the ATA's SPL delegate — use revokeSubscriptionAuthority to stop spending. A same-slot re-init reuses init_id and keeps existing delegations live, so this is not a kill switch. |
| createFixedDelegation / getCreateFixedDelegationOverlayInstructionAsync | One-time token allowance with optional expiry |
| createRecurringDelegation / getCreateRecurringDelegationOverlayInstructionAsync | Periodic allowance (amount per time period) |
| revokeDelegation / getRevokeDelegationOverlayInstruction | Permanently close a fixed or recurring delegation and reclaim rent |
| revokeSubscription / getRevokeSubscriptionOverlayInstruction | Close a subscription delegation PDA (requires planPda) and reclaim rent |
| revokeSubscriptionAuthority / getRevokeSubscriptionAuthorityOverlayInstructionAsync | Revoke the per-mint SPL delegate and close the SubscriptionAuthority PDA, reclaiming rent (pass receiver when a sponsor funded it) |
Abandoned delegation and subscription PDAs (Subscription Authority closed or re-initialized) can be closed by the recorded payer via the generated getRevokeAbandonedDelegationInstruction / getRevokeAbandonedSubscriptionInstruction builders (no overlay wrapper).
Transfers
| Plugin instruction / builder | Description |
| ------------------------------------------------------------------------- | ------------------------------------------ |
| transferFixed / getTransferFixedOverlayInstructionAsync | Pull tokens from a fixed delegation |
| transferRecurring / getTransferRecurringOverlayInstructionAsync | Pull tokens from a recurring delegation |
| transferSubscription / getTransferSubscriptionOverlayInstructionAsync | Pull tokens from a subscription delegation |
For Token-2022 mints with a configured transfer hook, the plugin resolves and appends the hook accounts automatically. For standalone overlay usage, call resolveTransferHookAccounts and pass the result as transferHookAccounts.
Subscription Plans
| Plugin instruction / builder | Description |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| createPlan / getCreatePlanOverlayInstructionAsync | Publish a subscription plan with billing terms |
| updatePlan / getUpdatePlanOverlayInstruction | Update plan status, end date, pullers, or metadata |
| deletePlan / getDeletePlanOverlayInstruction | Delete an expired plan and reclaim rent |
| subscribe / getSubscribeOverlayInstructionAsync | Subscribe to a plan |
| cancelSubscription / getCancelSubscriptionOverlayInstructionAsync | Cancel a subscription (grace period until end of billing period) |
| cancelSubscriptionNow / getCancelSubscriptionNowOverlayInstructionAsync | Cancel immediately with subscriber and plan-owner approval |
| resumeSubscription / getResumeSubscriptionOverlayInstructionAsync | Resume a cancelled subscription before revocation |
Account Queries
| Function | Description |
| ----------------------------- | ---------------------------------------------------------- |
| fetchDelegationsByDelegator | All delegations where wallet is the delegator |
| fetchDelegationsByDelegatee | All delegations where wallet is the delegatee |
| fetchPlansForOwner | All plans owned by an address |
| fetchSubscriptionsForUser | All subscriptions for a user |
| decodeDelegationAccount | Decode raw delegation accounts (fans out by discriminator) |
PDA Helpers
Use the generated find*Pda helpers directly: findSubscriptionAuthorityPda,
findFixedDelegationPda, findRecurringDelegationPda, findSubscriptionDelegationPda,
findPlanPda, findEventAuthorityPda. They return [address, bump]. The first five take a
seeds object; findEventAuthorityPda takes only an optional { programAddress } config (its
seed is the constant "event_authority").
Types
Delegation- discriminated union:{ kind: "fixed" | "recurring" | "subscription"; address; data }. Narrow withd.kind === '...'.PlanWithAddress,DelegationKindId(string union); transfer input types areTransferDelegationInput/TransferSubscriptionInput- Error handling: client-side
ValidationError; on-chain errors use the generatedSUBSCRIPTIONS_ERROR__*constants andisSubscriptionsError/getSubscriptionsErrorMessage.
API Reference
Full API documentation is generated from source with TypeDoc. Run npx typedoc to generate the reference into ./docs/ locally.
Development
Generated bindings in src/generated/ are produced by Codama and gitignored. Regenerate from the repo root:
just generate-clientsLicense
MIT
