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 delegator creates a recurring delegation

Source: subscriptions/tests/test_create_recurring_delegation.rs L154

  • When: the user initializes a subscription authority
  • When: a recurring delegation is created
  • Then: the recurring delegation is created ✓
  • Then: recurringDelegation.header.delegator = 2rj1EEgg32bupe12mMYnPFaKQCPPyYBhKYmDFZ8cXxpT ✓
  • Then: recurringDelegation.header.delegatee = 111MqiH3tDg8KtkQYkCteems4APA9GgUsEFQavs8Vb ✓
  • Then: recurringDelegation.header.payer = 2rj1EEgg32bupe12mMYnPFaKQCPPyYBhKYmDFZ8cXxpT ✓
  • Then: recurringDelegation.header.discriminator = 3 ✓
  • Then: recurringDelegation.amountPerPeriod = 50000000 ✓
  • Then: recurringDelegation.periodLengthS = 86400 ✓
  • Then: recurringDelegation.expiryTs = 604800 ✓
  • Then: recurringDelegation.amountPulledInPeriod = 0 ✓
  • Then: recurringDelegation.currentPeriodStartTs = 0 ✓

Result: ✓ success — 6563 CU · claims 10/10 ✓

sequenceDiagram
    participant p0 as alice
    participant p1 as Subscriptions
    participant p2 as system
    p0->>p1: createRecurringDelegation
    activate p1
    p1->>p2: createAccount
    activate p2
    p2-->>p1: ✓
    deactivate p2
    p1-->>p0: ✓ 6563cu
    deactivate p1

Authority

flowchart LR
    Subscriptions["Subscriptions"]:::program
    alice(["alice"]):::signer
    4gFR(["4gFR…"]):::signer
    system["system"]:::program
    alice -->|signs| Subscriptions
    Subscriptions -->|writes| 4gFR
    alice -->|signs| system
    4gFR -->|signs| system
    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
    Subscriptions["Subscriptions"]:::program
    4gFR[("4gFR…")]:::state
    system -->|owns| alice
    Subscriptions -->|owns| 4gFR
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Accounts

AccountSignerWritableOwner
alicesystem
SA(alice, Mint)··Subscriptions
4gFR…·Subscriptions
111M…··system
system··Nati…
Call tree and logs
A delegator creates a recurring delegation
alice (6563cu)
└─ Subscriptions::createRecurringDelegation ✓ 6563cu
   └─ system::createAccount ✓
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 invoke [1]
Program 11111111111111111111111111111111 invoke [2]
Program 11111111111111111111111111111111 success
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 consumed 6563 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};

// --- createRecurringDelegation ---
let program_id: Pubkey = pubkey!("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44");
let delegator: 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 delegation_account: Pubkey = pubkey!("4gFRG5Y1v18RTbbeTifYKT39Ckc116DqWMYo1RhWXiaw"); // supply your own
let delegatee: Pubkey = pubkey!("111MqiH3tDg8KtkQYkCteems4APA9GgUsEFQavs8Vb"); // supply your own
let system_program: Pubkey = pubkey!("11111111111111111111111111111111"); // the system program
let ix = Instruction {
    program_id,
    accounts: vec![
        AccountMeta::new(delegator, true), // delegator
        AccountMeta::new_readonly(subscription_authority, false), // subscriptionAuthority
        AccountMeta::new(delegation_account, false), // delegationAccount
        AccountMeta::new_readonly(delegatee, false), // delegatee
        AccountMeta::new_readonly(system_program, false), // systemProgram
    ],
    // discriminator ++ borsh(recurringDelegation: {nonce: 0, amountPerPeriod: 50000000, periodLengthS: 86400, startTs: 0, expiryTs: 604800, expectedSubscriptionAuthorityInitId: 0})
    data: vec![0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf0, 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x51, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
};
}

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

const ix = await getCreateRecurringDelegationInstructionAsync({
  delegator: delegatorSigner, // alice (a TransactionSigner)
  subscriptionAuthority: address("4RDKDQRnKtZC5ti4wunSAayArwxdZida5St595CiKJ7i"), // SA(alice, Mint)
  delegationAccount: address("4gFRG5Y1v18RTbbeTifYKT39Ckc116DqWMYo1RhWXiaw"),
  delegatee: address("111MqiH3tDg8KtkQYkCteems4APA9GgUsEFQavs8Vb"),
  // systemProgram defaults to the system program
  recurringDelegation: {
    nonce: 0n,
    amountPerPeriod: 50000000n,
    periodLengthS: 86400n,
    startTs: 0n,
    expiryTs: 604800n,
    expectedSubscriptionAuthorityInitId: 0n
  },
});
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 create_recurring_delegation() {
    let mut story = Story::load(SO, IDL);
    let alice = story.cast("alice");
    let amount_per_period: u64 = 50_000_000;
    let period_length_s: u64 = 86_400;
    let start_ts = story.now();
    let expiry_ts = start_ts + days(7) as i64;
    let nonce: u64 = 0;

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

    let delegatee = Pubkey::new_unique();
    let delegation_pda = story.delegation_pda(&alice.pubkey(), &mint, &delegatee, nonce);

    let out = story.create_recurring_delegation(CreateRecurringArgs {
        delegator: &alice,
        mint,
        delegatee,
        nonce,
        amount_per_period,
        period_length_s,
        start_ts,
        expiry_ts,
        sponsor: None,
        expected_init_id: None,
    });
    story.then_holds("the recurring delegation is created", out.success);

    let delegator =
        story.track_field::<Pubkey>("recurringDelegation", delegation_pda, "header.delegator");
    let delegatee_field =
        story.track_field::<Pubkey>("recurringDelegation", delegation_pda, "header.delegatee");
    let payer = story.track_field::<Pubkey>("recurringDelegation", delegation_pda, "header.payer");
    let discriminator = story.track_field::<u8>(
        "recurringDelegation",
        delegation_pda,
        "header.discriminator",
    );
    let amount_per_period_field =
        story.track_field::<u64>("recurringDelegation", delegation_pda, "amountPerPeriod");
    let period_length_field =
        story.track_field::<u64>("recurringDelegation", delegation_pda, "periodLengthS");
    let expiry_field = story.track_field::<i64>("recurringDelegation", delegation_pda, "expiryTs");
    let amount_pulled = story.track_field::<u64>(
        "recurringDelegation",
        delegation_pda,
        "amountPulledInPeriod",
    );
    let period_start = story.track_field::<i64>(
        "recurringDelegation",
        delegation_pda,
        "currentPeriodStartTs",
    );

    story.then_eq(&delegator, alice.pubkey());
    story.then_eq(&delegatee_field, delegatee);
    story.then_eq(&payer, alice.pubkey());
    story.then_eq(&discriminator, TAG_RECURRING_DELEGATION);
    story.then_eq(&amount_per_period_field, amount_per_period);
    story.then_eq(&period_length_field, period_length_s);
    story.then_eq(&expiry_field, expiry_ts);
    story.then_eq(&amount_pulled, 0u64);
    story.then_eq(&period_start, start_ts);
    emit_scenario(&story, &out, "A delegator creates a recurring delegation");
}
}