Skip to content

fix(program)!: fixes for next release - #222

Merged
dev-jodee merged 11 commits into
mainfrom
docs/cancel-now-approval-granularity
Jul 29, 2026
Merged

fix(program)!: fixes for next release#222
dev-jodee merged 11 commits into
mainfrom
docs/cancel-now-approval-granularity

Conversation

@dev-jodee

@dev-jodee dev-jodee commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • update_plan (breaking): add expected_created_at, expected_end_ts, expected_pullers, expected_metadata_uri to UpdatePlanData and reject with new StalePlanApproval (522) when any differs from the live plan. A withheld owner-signed update can no longer restore a puller removed by a later update, nor apply to a plan recreated at the same PDA. TS plugin client auto-fills the expected fields from the live plan; the standalone overlay requires them explicitly; the webapp passes the displayed plan state
  • create_plan IDL: declare the optional rent payer (writable signer, omitted-account strategy) so generated TS/Rust clients can express sponsored plan creation; client test pins the payer's position and role
  • Security Considerations reorg: group the flat list under authority-lifecycle / signed-transaction-freshness / collectability headings, state init_id slot semantics once, and document (a) that cancel_subscription_now approvals bind to second-granular current_period_start_ts, and (b) that a held one-tx init + sentinel subscribe/create bundle survives revokeSubscriptionAuthority — the reliable defense is to keep the held transaction from landing

Test Plan

  • just integration-test — 274 passed, incl. new update_plan_rejects_stale_approval_that_restores_removed_puller and update_plan_rejects_approval_from_previous_plan_lifecycle
  • just test-client — 66 + 1 offline passed
  • pnpm vitest run test/create-plan-payer.test.ts

Breaking Changes

  • update_plan instruction data grows by 272 bytes of expected_* fields; transactions built against the prior format fail with InvalidInstructionData. TS SDK plugin callers are unaffected (auto-fill); direct overlay/builder callers must supply the observed plan state.

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
solana-subscriptions-program Ready Ready Preview, Comment Jul 29, 2026 5:14pm

Request Review

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR strengthens plan-update freshness and sponsored plan-creation support.

  • Adds expected live-plan fields and stale-approval rejection to update_plan.
  • Updates the TypeScript SDK and webapp to populate the expected plan snapshot.
  • Declares the optional create_plan rent payer in the IDL and adds client coverage.
  • Reorganizes authority lifecycle, transaction freshness, and collectability documentation.

Confidence Score: 4/5

The implementation appears safe to merge, but the outstanding security documentation wording should be corrected.

The plan-update freshness checks and client plumbing appear coherent; the remaining issue is that the cancellation replay documentation still excludes the reachable case where a transfer executes before the replayed cancellation.

Files Needing Attention: README.md

Important Files Changed

Filename Overview
program/src/instructions/update_plan.rs Adds live-plan snapshot comparisons before applying an owner-approved plan update.
clients/typescript/src/plugin.ts Extends update-plan builders and auto-fetches omitted expected fields through the plugin client.
idl/subscriptions.json Updates the update-plan payload and exposes the optional sponsored create-plan payer.
webapp/src/hooks/use-subscriptions-mutations.ts Passes the displayed plan snapshot when constructing update-plan instructions.
README.md Reorganizes the documented authority lifecycle, signed-transaction freshness, and collectability behavior.

Sequence Diagram

sequenceDiagram
    participant App
    participant SDK
    participant Program
    participant Plan
    App->>SDK: updatePlan(new values)
    SDK->>Plan: Fetch live plan snapshot
    Plan-->>SDK: createdAt, endTs, pullers, metadataUri
    SDK->>Program: UpdatePlan(new values + expected snapshot)
    Program->>Plan: Compare expected and live fields
    alt Snapshot matches
        Program->>Plan: Apply update
    else Snapshot differs
        Program-->>App: StalePlanApproval
    end
Loading

Reviews (15): Last reviewed commit: "test(fuzz): thread update_plan expected_..." | Re-trigger Greptile

Comment thread README.md Outdated
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Compute Unit Report

Instruction Samples CUs Est Cost (Low) [SOL] Est Cost (Med) [SOL] Est Cost (High) [SOL] Δ Avg vs main
cancel_subscription 28 1838 0.000005000 0.000005073 0.000005919
cancel_subscription_now 5 1873 0.000005000 0.000005074 0.000005936
close_subscription_authority 22 1804 0.000005000 0.000005072 0.000005902
create_fixed_delegation 50 3524 0.000005001 0.000005140 0.000006762
create_plan 141 3501 0.000005001 0.000005140 0.000006750
create_recurring_delegation 35 3556 0.000005001 0.000005142 0.000006778
delete_plan 10 359 0.000005000 0.000005014 0.000005179
init_subscription_authority 231 4856 0.000005001 0.000005194 0.000007428
resume_subscription 4 2041 0.000005000 0.000005081 0.000006020
revoke_abandoned_delegation 3 403 0.000005000 0.000005016 0.000005201
revoke_abandoned_subscription 2 2093 0.000005000 0.000005083 0.000006046
revoke_delegation 24 282 0.000005000 0.000005011 0.000005141
revoke_subscription_authority 8 3576 0.000005001 0.000005143 0.000006788
subscribe 52 6642 0.000005001 0.000005265 0.000008321
transfer_fixed 10 5638 0.000005001 0.000005225 0.000007819
transfer_recurring 21 5749 0.000005001 0.000005229 0.000007874
transfer_subscription 14 6029 0.000005001 0.000005241 0.000008014
update_plan 31 2142 0.000005000 0.000005085 0.000006071

🔺 increase · 🔻 decrease · – unchanged · 🆕 new · 🗑 removed (vs main)

Generated: 2026-07-29

@dev-jodee dev-jodee changed the title docs: note cancel-now approval second granularity fix(idl): declare optional create_plan payer Jul 27, 2026
@dev-jodee dev-jodee changed the title fix(idl): declare optional create_plan payer fix(program)!: bind update_plan to observed plan state Jul 27, 2026
@dev-jodee
dev-jodee force-pushed the docs/cancel-now-approval-granularity branch from 87bfc62 to ef30668 Compare July 27, 2026 14:34
@dev-jodee
dev-jodee force-pushed the docs/cancel-now-approval-granularity branch from ef30668 to 653dec5 Compare July 27, 2026 14:40
@dev-jodee
dev-jodee force-pushed the docs/cancel-now-approval-granularity branch from eb2962c to 7ffef20 Compare July 27, 2026 15:09
@dev-jodee dev-jodee changed the title fix(program)!: bind update_plan to observed plan state fix(program)!: fixes for next release Jul 27, 2026
@dev-jodee
dev-jodee requested a review from amilz July 29, 2026 16:57
The on-chain handler accepts a trailing optional rent payer (writable
signer), but the Codama definition listed only five accounts, so the
IDL and generated clients could not express sponsored plan creation.
Declare the payer with the omitted-account strategy, matching the other
creation instructions, and regenerate the IDL. Adds a client test
pinning the payer's position and writable-signer role.
UpdatePlanData carried only replacement values, so an owner-signed but
unsubmitted update (durable nonce or reorder window) stayed valid after
a later update changed the plan — notably restoring a puller the owner
had removed, re-authorizing it to pull from every subscription under
the plan. A stale update could likewise apply to a plan recreated at
the same PDA after deletion.

Add expected_created_at, expected_end_ts, expected_pullers and
expected_metadata_uri to the instruction data and reject with the new
StalePlanApproval error when any differs from the live plan, binding
each signed update to the exact state the owner observed. The TS plugin
client auto-fills the expected fields from the live plan; the overlay
requires them explicitly, and the webapp passes the displayed plan
state.

BREAKING CHANGE: update_plan instruction data grows by 272 bytes of
expected_* fields. Transactions built against the prior format fail
with InvalidInstructionData.
Group the flat list under authority-lifecycle, signed-transaction
freshness, and collectability headings, and state the init_id slot
semantics once instead of restating them per bullet.

Add a note that a held one-transaction init + sentinel subscribe/create
bundle survives revokeSubscriptionAuthority: it re-creates the authority
in its landing slot and the sentinel binds, restoring a spendable
permission with no fresh signature. The existing wording only covered a
standalone sentinel create needing a same-slot re-init, understating the
self-contained-bundle case.
build.rs swallowed generate_idl errors into a cargo:warning and exited
zero, so a change that still compiles but breaks IDL generation could
pass generate-idl/check-generated and be released with stale generated
clients. Return the Result from main so cargo exits nonzero on failure,
propagating through every release and publish path that compiles the
program.
The update_plan wire change added expected_created_at/end_ts/pullers/
metadata_uri, so the fuzz action_update_plan literal no longer matches
UpdatePlanData. Populate the expected_* fields from the live plan so the
harness compiles and its updates still pass the new StalePlanApproval
check.
@dev-jodee
dev-jodee merged commit debb4f7 into main Jul 29, 2026
19 checks passed
@dev-jodee
dev-jodee deleted the docs/cancel-now-approval-granularity branch July 29, 2026 17:21
dev-jodee added a commit that referenced this pull request Jul 30, 2026
* docs: add changelog entries for stale-approval guards

Document the Unreleased breaking changes across the program and both
client changelogs: resume_subscription expected_expires_at_ts (#214),
cancel_subscription_now expected_current_period_start_ts (#221),
update_plan expected_* plan-state binding (#222), the same-slot
abandoned-recovery guard (#221), and the new StaleSubscriptionApproval
(521) / StalePlanApproval (522) errors.

* docs: fold cancel-now consent binding into its Added entries

CancelSubscriptionNow landed after the 0.4.0 release and has never
shipped, so binding its instruction data is part of the new feature,
not a breaking change. Describe expected_current_period_start_ts in
the existing Added bullets and drop the Breaking entries.

* docs: note cancel-now emits SubscriptionCancelledEvent

---------

Co-authored-by: Jo D <dev-jodee@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants