Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A user closes their subscription authority

Source: subscriptions/tests/test_close_subscription_authority.rs L98

  • When: the user initializes a subscription authority
  • When: the user closes the subscription authority
  • Then: Alice closes her own authority ✓
  • Then: the authority account is gone or emptied ✓
  • Then: Alice’s balance grew ✓
  • Then: Alice recovers the rent (less fees) ✓

Result: ✓ success — 1832 CU · claims 4/4 ✓

sequenceDiagram
    participant p0 as alice
    participant p1 as Subscriptions
    p0->>p1: closeSubscriptionAuthority
    activate p1
    p1-->>p0: ✓ 1832cu
    deactivate p1

Authority

flowchart LR
    Subscriptions["Subscriptions"]:::program
    alice(["alice"]):::signer
    SAaliceMint[("SA(alice, Mint)")]:::state
    alice -->|signs| Subscriptions
    Subscriptions -->|writes| SAaliceMint
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Ownership

flowchart LR
    system["system"]:::program
    alice[("alice")]:::state
    SAaliceMint[("SA(alice, Mint)")]:::state
    system -->|owns| alice
    system -->|owns| SAaliceMint
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Accounts

AccountSignerWritableOwner
alicesystem
SA(alice, Mint)·system
Call tree and logs
A user closes their subscription authority
alice (1832cu)
└─ Subscriptions::closeSubscriptionAuthority ✓ 1832cu
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 invoke [1]
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 consumed 1832 of 200000 compute units
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 success

Integration

What an integrator writes: the derivations and the instruction, byte-identical to the transaction this page witnessed. Addresses are this scenario’s; supply your own.

#![allow(unused)]
fn main() {
use solana_instruction::{AccountMeta, Instruction};
use solana_pubkey::{pubkey, Pubkey};

// --- closeSubscriptionAuthority ---
let program_id: Pubkey = pubkey!("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44");
let user: Pubkey = pubkey!("2rj1EEgg32bupe12mMYnPFaKQCPPyYBhKYmDFZ8cXxpT"); // alice: this scenario's value; supply your own
let subscription_authority: Pubkey = pubkey!("4RDKDQRnKtZC5ti4wunSAayArwxdZida5St595CiKJ7i"); // SA(alice, Mint): this scenario's value; supply your own
let ix = Instruction {
    program_id,
    accounts: vec![
        AccountMeta::new(user, true), // user
        AccountMeta::new(subscription_authority, false), // subscriptionAuthority
    ],
    // discriminator ++ borsh()
    data: vec![0x06],
};
}

The same call through the Codama-generated TypeScript client (the async builder derives every PDA the resolver derived here, so only the free inputs appear):

import { address } from "@solana/kit";
import { getCloseSubscriptionAuthorityInstructionAsync } from "your-generated-client";

const ix = await getCloseSubscriptionAuthorityInstructionAsync({
  user: userSigner, // alice (a TransactionSigner)
  subscriptionAuthority: address("4RDKDQRnKtZC5ti4wunSAayArwxdZida5St595CiKJ7i"), // SA(alice, Mint)
});
How this page verifies itself: the test that ran it

The narrative above is this test; the page and the code cannot drift.

#![allow(unused)]
fn main() {
#[test]
fn close_subscription_authority() {
    let mut story = Story::load(SO, IDL);
    let alice = story.cast("alice");
    let mint = story.mint(&alice, MINT_DECIMALS);
    story.fund(&alice, &[(mint, 1_000_000)]);
    story
        .init_subscription_authority(InitAuthorityArgs {
            user: &alice,
            mint,
            sponsor: None,
            token_program: None,
        })
        .expect_success();

    let pda = story.subscription_authority_pda(&alice.pubkey(), &mint);
    let rent = story
        .svm
        .get_account(&pda)
        .expect("the authority exists before close")
        .lamports;
    let alice_before = story.svm.get_account(&alice.pubkey()).unwrap().lamports;

    let outcome = story.close_subscription_authority(CloseAuthorityArgs {
        user: &alice,
        mint,
        receiver: None,
        authority: None,
    });
    story.then_holds("Alice closes her own authority", outcome.success);

    let after = story.svm.get_account(&pda);
    story.then_holds(
        "the authority account is gone or emptied",
        after.is_none() || after.as_ref().map(|a| a.lamports).unwrap_or(0) == 0,
    );

    let alice_after = story.svm.get_account(&alice.pubkey()).unwrap().lamports;
    story.then_holds("Alice's balance grew", alice_after > alice_before);
    story.then_holds(
        "Alice recovers the rent (less fees)",
        alice_after >= alice_before + rent - 10_000,
    );
    emit_scenario(
        &story,
        &outcome,
        "A user closes their subscription authority",
    );
}
}