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 merchant updates a plan

Source: subscriptions/tests/test_update_plan.rs L126

  • When: the merchant creates plan 1
  • Then: staging createPlan should succeed ✓
  • When: the owner updates the plan
  • Then: the merchant sunsets its own plan ✓
  • Then: plan.status = 0 ✓
  • Then: plan.data.endTs = 5184000 ✓
  • Then: plan.data.metadataUri = “https://example.com/updated.json” ✓

Result: ✓ success — 1949 CU · claims 5/5 ✓

sequenceDiagram
    participant p0 as merchant
    participant p1 as Subscriptions
    p0->>p1: updatePlan
    activate p1
    p1->>p1: emit:planUpdatedEvent
    activate p1
    p1-->>p1: ✓ 137cu
    deactivate p1
    p1-->>p0: ✓ 1949cu
    deactivate p1

Authority

flowchart LR
    Subscriptions["Subscriptions"]:::program
    merchant(["merchant"]):::signer
    planmerchant1[("plan(merchant, 1)")]:::state
    eventAuthority(["eventAuthority"]):::signer
    merchant -->|signs| Subscriptions
    Subscriptions -->|writes| planmerchant1
    eventAuthority -->|signs| Subscriptions
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Ownership

flowchart LR
    system["system"]:::program
    merchant[("merchant")]:::state
    Subscriptions["Subscriptions"]:::program
    planmerchant1[("plan(merchant, 1)")]:::state
    system -->|owns| merchant
    Subscriptions -->|owns| planmerchant1
    classDef program fill:#dae8fc,stroke:#6c8ebf;
    classDef signer fill:#d5e8d4,stroke:#82b366;
    classDef state fill:#ffe6cc,stroke:#d79b00;

Accounts

AccountSignerWritableOwner
merchantsystem
plan(merchant, 1)·Subscriptions
eventAuthority··system
Subscriptions··BPFL…
Call tree and logs
A merchant updates a plan
merchant (1949cu)
└─ Subscriptions::updatePlan ✓ 1949cu
   └─ Subscriptions::emit:planUpdatedEvent ✓ 137cu
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 invoke [1]
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 invoke [2]
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 consumed 137 of 198212 compute units
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 success
Program De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44 consumed 1949 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};

// --- updatePlan ---
let program_id: Pubkey = pubkey!("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44");
let owner: Pubkey = pubkey!("4jnW4cX63scWb3VGDVaacYunDWNFsGERr3YYUKPuNxJp"); // merchant: this scenario's value; supply your own
let plan_pda: Pubkey = pubkey!("2PQuEytmHE7C2a9ynpmsRfYK7UgXNtCqSR4qnNNgeZwp"); // plan(merchant, 1): this scenario's value; supply your own
let event_authority = Pubkey::find_program_address(&[b"event_authority"], &program_id).0;
let ix = Instruction {
    program_id,
    accounts: vec![
        AccountMeta::new_readonly(owner, true), // owner
        AccountMeta::new(plan_pda, false), // planPda
        AccountMeta::new_readonly(event_authority, false), // eventAuthority
        AccountMeta::new_readonly(program_id, false), // selfProgram
    ],
    // discriminator ++ borsh(updatePlanData: {status: 0, endTs: 5184000, pullers: [1111…, 1111…, 1111…, 1111…], metadataUri: "https://example.com/updated.json"})
    data: vec![0x08, 0x00, 0x00, 0x1a, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 { getUpdatePlanInstructionAsync } from "your-generated-client";

const ix = await getUpdatePlanInstructionAsync({
  owner: ownerSigner, // merchant (a TransactionSigner)
  planPda: address("2PQuEytmHE7C2a9ynpmsRfYK7UgXNtCqSR4qnNNgeZwp"), // plan(merchant, 1)
  updatePlanData: {
    status: 0,
    endTs: 5184000n,
    pullers: [address("11111111111111111111111111111111"), address("11111111111111111111111111111111"), address("11111111111111111111111111111111"), address("11111111111111111111111111111111")],
    metadataUri: "https://example.com/updated.json"
  },
});
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 update_plan_happy_path() {
    let mut story = Story::load(SO, IDL);
    let plan_pda = stage_plan(&mut story, 1_000_000, 720, 0, vec![]);
    let owner = merchant(&mut story);

    let end_ts = story.now() + days(60);
    let outcome = story.update_plan(
        &owner,
        plan_pda,
        UpdatePlanArgs {
            status: STATUS_SUNSET,
            end_ts,
            metadata_uri: "https://example.com/updated.json".into(),
            ..Default::default()
        },
    );
    story.then_holds("the merchant sunsets its own plan", outcome.success);

    let status = story.track_field::<u8>("plan", plan_pda, "status");
    let end_ts_field = story.track_field::<i64>("plan", plan_pda, "data.endTs");
    let metadata_uri = story.track_field::<String>("plan", plan_pda, "data.metadataUri");

    story.then_eq(&status, STATUS_SUNSET);
    story.then_eq(&end_ts_field, end_ts);
    story.then_eq(
        &metadata_uri,
        "https://example.com/updated.json".to_string(),
    );
    emit_scenario(&story, &outcome, "A merchant updates a plan");
}
}