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

An abandoned delegation is revoked

Source: subscriptions/tests/test_revoke_abandoned_delegation.rs L136

  • When: the user initializes a subscription authority
  • When: a fixed delegation is created
  • When: the user closes the subscription authority
  • When: an abandoned delegation is revoked
  • Then: the abandoned delegation is revoked ✓
  • Then: the delegation account is gone ✓
  • Then: the sponsor recovered the rent ✓

Result: ✓ success — 305 CU · claims 3/3 ✓

sequenceDiagram
    participant p0 as sponsor
    participant p1 as Subscriptions
    p0->>p1: revokeAbandonedDelegation
    activate p1
    p1-->>p0: ✓ 305cu
    deactivate p1

Authority

flowchart LR
    Subscriptions["Subscriptions"]:::program
    sponsor(["sponsor"]):::signer
    7JW9[("7JW9…")]:::state
    sponsor -->|signs| Subscriptions
    Subscriptions -->|writes| 7JW9
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Ownership

flowchart LR
    system["system"]:::program
    sponsor[("sponsor")]:::state
    7JW9[("7JW9…")]:::state
    system -->|owns| sponsor
    system -->|owns| 7JW9
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Accounts

AccountSignerWritableOwner
sponsorsystem
7JW9…·system
SA(alice, Mint)··system
Call tree and logs
An abandoned delegation is revoked
sponsor (305cu)
└─ Subscriptions::revokeAbandonedDelegation ✓ 305cu
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 invoke [1]
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 consumed 305 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};

// --- revokeAbandonedDelegation ---
let program_id: Pubkey = pubkey!("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44");
let payer: Pubkey = pubkey!("EgNLJSfXpchFPfE4Xd6XxWBzjhYfmtPAcUQDFUZ6vWog"); // sponsor: this scenario's value; supply your own
let delegation_account: Pubkey = pubkey!("7JW9nfbPZbx3UQUbDA69x9D9Tytmmh23p7wYJ6Eq9RWM"); // 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(payer, true), // payer
        AccountMeta::new(delegation_account, false), // delegationAccount
        AccountMeta::new_readonly(subscription_authority, false), // subscriptionAuthority
    ],
    // discriminator ++ borsh()
    data: vec![0x0f],
};
}

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 { getRevokeAbandonedDelegationInstructionAsync } from "your-generated-client";

const ix = await getRevokeAbandonedDelegationInstructionAsync({
  payer: payerSigner, // sponsor (a TransactionSigner)
  delegationAccount: address("7JW9nfbPZbx3UQUbDA69x9D9Tytmmh23p7wYJ6Eq9RWM"),
  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 sponsor_recovers_no_expiry_fixed_delegation_after_authority_closed() {
    let mut story = Story::load(SO, IDL);
    let alice = story.cast("alice");
    let sponsor = story.cast("sponsor");
    let delegatee = Pubkey::new_unique();

    let mint = story.mint(&alice, MINT_DECIMALS);
    story.fund(&alice, &[(mint, 1_000_000)]);
    init(&mut story, &alice, mint);

    let nonce: u64 = 0;
    let delegation_pda = story.delegation_pda(&alice.pubkey(), &mint, &delegatee, nonce);
    assert!(
        story
            .create_fixed_delegation(CreateFixedArgs {
                delegator: &alice,
                mint,
                delegatee,
                nonce,
                amount: 100,
                expiry_ts: NO_EXPIRY,
                sponsor: Some(&sponsor),
                expected_init_id: None,
                delegation_override: None,
            })
            .success
    );

    let delegation_rent = lamports(&story, &delegation_pda);
    // Alice closes her authority, abandoning the delegation.
    close(&mut story, &alice, mint);

    let sponsor_pubkey = sponsor.pubkey();
    let sponsor_before = lamports(&story, &sponsor_pubkey);
    let out = story.revoke_abandoned_delegation(RevokeAbandonedArgs {
        payer: &sponsor,
        delegator: alice.pubkey(),
        mint,
        delegatee,
        nonce,
        authority: None,
    });
    story.then_holds("the abandoned delegation is revoked", out.success);

    story.then_holds(
        "the delegation account is gone",
        is_gone(&story, &delegation_pda),
    );
    story.then("the sponsor recovered the rent", move |s| {
        let sponsor_after = lamports(s, &sponsor_pubkey);
        sponsor_after >= sponsor_before + delegation_rent - 10_000
    });
    emit_scenario(&story, &out, "An abandoned delegation is revoked");
}
}