Repository navigation
add support for migration to spender program - #40
Draft
Brennan Lamey (brennanjl) wants to merge 8 commits into
Draft
Brennan Lamey (brennanjl) wants to merge 8 commits into
Brennan Lamey (brennanjl) wants to merge 8 commits into
Conversation
Brennan Lamey (brennanjl)
added a commit
that referenced
this pull request
Sep 10, 2026
…er_delegate Both instructions loaded BridgeCardsState only for the migrated check, which was replaced with the MigrationState PDA approach. Neither instruction uses the state account for any other constraint or logic, so it is redundant. Removing it reduces the interface change (callers only need to pass migration_state, not state + migration_state). Addresses reviewer comment on PR #40. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
Brennan Lamey (brennanjl)
added a commit
that referenced
this pull request
Sep 10, 2026
…mpatibility For a zero-downtime deploy, Monorail is updated first to send the extra migration_state account, and the new binary is deployed afterward. This only works if migration_state is at the END of each instruction's account list — extra accounts go to remaining_accounts in the old binary without shifting existing account positions. Addresses reviewer ordering concern on PR #40. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
Adds three new instructions to support the gradual migration from the bridge-cards program to the spender-program-solana: cpi_transfer: CPI target callable only by the spender program (verified via the SpenderState PDA as caller_proof). The spender validates destination allowlists; bridge-cards executes the SPL transfer using the existing UserDelegate PDA as signing authority. Intentionally skips the per-transfer and period limits enforced by debit_user — those controls are managed upstream by Monorail before a debit is authorized. set_migrated: Creates or closes a MigrationState PDA (seeds = [b"migration"]). When the PDA exists and is owned by bridge-cards, all instructions except cpi_transfer reject with ProgramMigrated. BridgeCardsState is unchanged (stays 41 bytes) so deploying this binary is safe against all existing mainnet accounts with zero downtime and no account resize. MigrationState PDA is placed last in every instruction's account list so old callers (not yet updated to pass it) continue to work — the extra account silently goes to remaining_accounts in the old binary. Gradual rollout: update Monorail first, deploy binary second. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
Brennan Lamey (brennanjl)
force-pushed
the
brennan/pulls-migration
branch
from
September 11, 2026 17:51
aca4f50 to
549fd03
Compare
…entional cpi_transfer design - realloc before assign in set_migrated(false) so bridge-cards still owns the account when the BPF runtime validates the resize at finalization - emit MigrationStateUpdated only when the PDA is actually created/closed, not on no-op idempotent calls - document in cpi_transfer that velocity limits and destination whitelist validation are intentionally delegated to the spender program Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
The previous guard (lamports > 0) would enter the close path for a dusted-but-never-created PDA, then fail at runtime when trying to debit lamports from a system-owned account. Symmetric with set_migrated(true) which correctly uses owner != &ID. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
New users can be onboarded onto legacy merchants without a separate add_or_update_user_delegate call. They pre-approve the deterministic UserDelegateState PDA address as their SPL delegate; the first call to transfer_using_legacy_delegate creates the account via init_if_needed in the same transaction. New accounts are zero-initialized (zero transfer limits), which is safe — cpi_transfer uses UserDelegateState only as an SPL signing authority and never reads limits. The spender program's destination whitelist and pause flag are the operative controls on this path. Adds debitor (mutable signer, payer) and system_program to the account list. New account ordering: caller_proof, user_delegate_account, debitor, user_token_account, destination_token_account, mint, token_program, system_program. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
- Remove seeds::program = ID from init_if_needed constraint on user_delegate_account; this was preventing Anchor from generating the Bumps accessor for ctx.bumps.user_delegate_account. The default seeds scope is already the current program, so the constraint was redundant. - cargo fmt: fix pre-existing formatting issues across close_account.rs, set_migrated.rs, debit_user_tests.rs, lib.rs, set_migrated_tests.rs. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
Anchor does not generate ctx.bumps entries for accounts that specify seeds::program pointing at the current program (equivalent to the default, but triggers the exclusion). Removing the redundant constraint restores the Bumps trait impl and fixes the build. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Committed-By-Agent: claude
The account that funds UserDelegateState init_if_needed creation is not the spender debitor — it is a separate payer supplied by the caller. Rename the field from `debitor` to `payer` throughout cpi_transfer to align with the spender change that introduces a dedicated payer account, decoupling rent from the global debitor and removing the shared writable choke point exploited by the CostTracker griefing attack. Audit finding: shared writable debitor enables CostTracker DoS (Impact: Medium, Likelihood: Low). Committed-By-Agent: claude Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.