Skip to content

feat(cli,sdk): authorization lock commands and helpers - #240

Merged
edo-chan merged 14 commits into
mainfrom
authlock-v3-cli-sdk
Nov 5, 2026
Merged

edo-chan merged 14 commits into
mainfrom
authlock-v3-cli-sdk

Conversation

@austbot

@austbot austbot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Final merge validation (2026-11-05): base f9b14b769e73171d9c5a330d7952e0b50f741588, head 499be63296a9ae5c2df360fe369c116db62c87b7. Retargeted to main after #226 merged. The CLI/SDK diff is unchanged from the independently reviewed implementation; the ancestry update changes no files.

Final-head CI passed: standard 948, ProgramScope 266, SDK 416 (3 existing skips), stake 957. SBF/release builds, lint, release CLI and Test Results passed. Independent review passed; local CLI units: 2 passed; SDK test target compiled; formatting/diff checks passed.

The accepted lock-ID correlation NIT remains a follow-up. Review also recorded the unchanged CLI policies where listing may initialize a missing wallet and a post-confirmation read can fail before printing the add receipt; this merge does not claim to fix those behaviors.


Adds client support for authorization locks from merged #226.

Lock semantics (from #226)

Locks are card authorization holds. Other roles, All included, can spend only the balance minus the locks they don't hold. The holder can spend its held funds, and each SignV2 it signs lowers ("captures") its locks on the account by the net outflow, oldest first. A lock captured to 0 stays stored with amount == 0, is inactive, and can be pruned. Limits are charged on spend, not at lock time. For clients this means a lock's amount is what is still reserved and goes down over time, and a fully captured lock remains listed until it is removed.

SDK (swig-sdk)

  • add_sol_authorization_lock(amount, expiry_slot) locks the wallet's SOL.
  • add_token_authorization_lock(mint, amount, expiry_slot) locks the wallet's ATA for mint. It reads the mint's owner to pick SPL Token or Token-2022.
  • get_active_authorization_locks() returns only the locks that still bind: unexpired and not fully captured.
  • get_authorization_locks() returns every stored lock, including expired and fully captured (amount == 0) ones.
  • display_swig now shows the ManageAuthorizationLocks permission (with its max duration) and an AUTHORIZATION LOCKS section, where each lock's status is active, expired or fully captured.
  • ProgramExecClientRole now supports add_authorization_lock_instruction and remove_authorization_lock_instruction, so the wallet lock helpers work with a ProgramExec role. This relies on feat: authorization locks (supersedes #132) #226's new lock account order (swig, wallet address, payer, system program, ...), which lets ProgramExec bind to the config and wallet address. Every remove builder now takes the wallet address, and SwigInstructionBuilder derives it for you.

CLI

  • New commands: add-authorization-lock (--amount, optional --mint, and either --expiry-slot or --duration-slots), remove-authorization-lock --lock-id, and list-authorization-locks [--all]. All new flags are long-only (see below).
  • New permission JSON {"type":"manageAuthorizationLocks","maxDurationSlots":N}, and ManageAuthorizationLocks for update-authority --operation RemoveActionsByType.
  • Interactive mode gets an "Authorization Locks" menu (list, lock SOL, lock tokens, release/prune) and the permission is available in the permission picker and in remove-by-type.
  • Behavior fix: --permissions no longer splits on ,. Splitting broke every permission JSON with more than one field (e.g. {"type":"sol","amount":1}). To pass several permissions, repeat the flag.
  • list-authorization-locks shows the remaining (uncaptured) amount and marks captured locks as "fully captured". --all includes expired and fully captured locks.
  • Fix: remove-authority --remove-authority <PUBKEY> passed the base58 string's raw bytes to remove_authority, so it always reported "Authority not found". It now decodes the key as add-authority does: base58 for Ed25519 roles, or a hex public key with the new --remove-authority-type Secp256k1|Secp256r1. Unit tests cover both. The README showed the flag as --authority (the signing authority) and now shows --remove-authority.
  • README: documents the permission JSON format, the lock commands and the card-authorization semantics.

Merged with main's security sync

71d4af7 merges the updated #226, which now includes main through e17b7ca (the #245 security-review backports). The merge into this branch was clean with no SDK or CLI changes needed. Local results with the pinned verifiable build: standard 934/934, program_scope_test 255/255, rust_sdk_test 405 passed (3 skipped).

Testing

  • After merging feat: authorization locks (supersedes #132) #226 at 4628591 (in-place lock tail edits and compute tables; no client-visible change): standard 886/886, program_scope_test 229/229, and rust_sdk_test+program_scope_test 379 passed, 3 skipped. This still includes RemoveAuthority pruning a removed role's inactive lock records, so remove_authority also clears them from get_authorization_locks().
  • New SDK test should_lock_sol_and_tokens_through_convenience_helpers covers the SOL and token helpers and the active-lock filter. New test test_program_exec_adds_and_removes_authorization_locks adds and removes a SOL lock through a ProgramExec role. The full swig-sdk suite passes with rust_sdk_test (151 passed, 3 skipped). After merging the updated feat: authorization locks (supersedes #132) #226: standard 854/854, program_scope_test 224/224, rust_sdk_test+program_scope_test 373 passed, 3 skipped.
  • End-to-end on solana-test-validator with the release CLI:
    • created a wallet and added a delegate with manageAuthorizationLocks (500 slots) and a 2 SOL limit
    • delegate lock for 600 slots is rejected with InvalidAuthorizationLockExpiry (0x52)
    • delegate lock of 3 SOL is rejected with AuthorizationLockExceedsSpendLimit (0x55)
    • a valid 1.5 SOL lock is created and listed
    • the owner releasing the delegate's active lock is rejected with AuthorizationLockNotOwner (0x57)
    • the owner stripping only ManageAuthorizationLocks succeeds, and the delegate can still release its lock afterwards
    • token lock via --mint is created, and a second lock beyond the balance is rejected with AuthorizationLockExceedsBalance (0x54)
    • view shows the lock section

Existing CLI bugs found (not fixed here)

  • The existing subcommands give -a to authority_type, authority and authority_kp, so clap's debug asserts panic in debug builds (e.g. swig-cli balance --help). Release builds work. The new commands use long-only flags so they aren't affected.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

SDK:
- add_sol_authorization_lock / add_token_authorization_lock helpers that
  resolve the wallet address or its ATA (SPL Token or Token-2022)
- get_active_authorization_locks
- display_swig shows ManageAuthorizationLocks and stored locks

CLI:
- add-authorization-lock, remove-authorization-lock and
  list-authorization-locks commands
- manageAuthorizationLocks permission JSON and RemoveActionsByType support
- interactive "Authorization Locks" menu and permission option
- stop splitting --permissions on commas, which broke any permission JSON
  with more than one field

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austbot
austbot force-pushed the authlock-v3-cli-sdk branch from 3f8bdcb to c5edad6 Compare October 29, 2026 19:10

@edo-chan edo-chan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with the inline NIT on lock ID correlation.

Comment thread cli/src/commands.rs
let lock_id = wallet
.get_authorization_locks()?
.iter()
.filter(|lock| lock.role_id == role_id && lock.expiry_slot == expiry_slot)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Could the create call expose a transaction-correlated lock_id and have the CLI use it instead of selecting the highest ID with the same role and expiry? If two similar holds confirm before this read, the earlier command can print the later hold's ID, making capture or release ambiguous.

austbot and others added 8 commits October 30, 2026 11:35
ProgramExecClientRole now builds AddAuthorizationLockV1 and
RemoveAuthorizationLockV1 with the new ProgramExec interface builders,
so SwigWallet's lock helpers work with a ProgramExec role. The CLI only
uses the wallet helpers, so the new account order needs no CLI changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows the authlock-v3 change: other roles are held off a lock, the holder
spends and captures it, and fully captured locks (amount 0) are inactive
until pruned. The CLI and wallet displays show them as "fully captured",
and an SDK test covers capture, the active filter, and pruning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`swig remove-authority --remove-authority <PUBKEY>` handed the string's raw
bytes (44 base58 characters) to `remove_authority`, which looks roles up by
their decoded key bytes, so it always reported the authority as not found.
It now decodes the value the same way add-authority does: a base58 pubkey
for Ed25519 roles, or a hex public key with the new
`--remove-authority-type` for Secp256k1/Secp256r1 roles. The README showed
the flag as `--authority` (the signing authority) and now shows
`--remove-authority`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
austbot and others added 3 commits November 4, 2026 09:44
@edo-chan
edo-chan marked this pull request as draft November 5, 2026 14:37
@edo-chan
edo-chan changed the base branch from authlock-v3 to main November 5, 2026 14:57
@edo-chan
edo-chan marked this pull request as ready for review November 5, 2026 15:11

@edo-chan edo-chan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved the final head after independent review and green CI against main. The CLI/SDK diff is unchanged from the reviewed implementation. Standard 948, ProgramScope 266, SDK 416 (3 existing skips), and stake 957 passed. Parent #226 is merged; the accepted lock-ID NIT and unchanged CLI reporting/initialization follow-ups remain separate.

@edo-chan
edo-chan merged commit a05a9aa into main Nov 5, 2026
4 checks passed
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