Skip to content

feat(claim): Phase 2 Path A — SDK-driven claim flow with claim-tx-sig attribution#281

Merged
rz1989s merged 13 commits into
mainfrom
feat/spec-4-path-a-claim-phase-2
May 17, 2026
Merged

feat(claim): Phase 2 Path A — SDK-driven claim flow with claim-tx-sig attribution#281
rz1989s merged 13 commits into
mainfrom
feat/spec-4-path-a-claim-phase-2

Conversation

@rz1989s
Copy link
Copy Markdown
Member

@rz1989s rz1989s commented May 17, 2026

Summary

  • Replaces Phase 1 claim stub with an end-to-end SDK-driven claim flow per Spec 4 Path A. executeClaim now resolves the stealth context from sipher's VaultWithdrawEvent (mirroring scanForPayments), delegates ECDH derivation + SPL transfer + broadcast to @sip-protocol/sdk's claimStealthPayment, and returns the real claim tx signature.
  • Torque attribution becomes honest from day one — sipher_private_claim_completed now carries data.tx_signature = <claim_tx_sig> instead of the input deposit-tx-sig.
  • 11 commits, +10 net tests, no breaking API changes for external consumers. All commits GPG-signed.

Test plan

  • tests/tools/claim-helpers.test.ts — 12 tests (1 happy + 3 happy-path variants + 8 error paths covering all 4 discriminated codes incl. the Buffer-data ATA branch)
  • tests/claim.test.ts — 13 tests (3 tool-definition + 3 happy-path + 4 input-validation regression + 3 SDK/helper error wraps)
  • tests/integrations/torque/growth-hook.test.ts — +1 test locking in claim-tx-sig attribution priority
  • tests/tools.test.ts — duplicate executeClaim describe block removed; dispatch test updated to verify the new resolveStealthContext code path
  • Full agent suite: 1612 passed + 2 pre-existing skipped (was 1602 baseline, net +10)
  • Workspace typecheck: clean (4 packages)
  • All 11 commits GPG-signed (BF47B9DC1FA320FA)
  • No AI attribution in any commit body

Scope notes

Path A trade-offs are documented in docs/superpowers/specs/2026-05-15-claim-phase-2-design.md (Amendment — SDK reality check). Summary:

  • ✅ One-shot server execution. No SignTxCard round-trip.
  • ✅ Honest Torque attribution. Minimum new code.
  • ❌ Loses explicit consent moment at sign-time — the chat tool invocation IS the consent.

Path B (SDK extension + SignTxCard UX) is tracked as a post-judges follow-up.

Other deliberate scope choices:

  • destinationWallet is now required in ClaimParams + Anthropic schema (was optional with runtime throw — schema-vs-runtime gap closed). Auto-derive from spending pubkey is a follow-up.
  • SOL claims work transparently — sipher's deposit model wraps SOL to WSOL on send, so the stealth ATA always holds an SPL token. Claim then uses SDK's SPL transfer primitive.
  • spl-token-2022 (Token Extensions) supported alongside spl-token in the SPL transfer finder.
  • Inner-instruction search included for Jupiter-routed deposits (private swap → stealth output → claim).

Mid-PR revision

A first attempt at Task 1 assumed sipher's deposits emit spl-memo instructions with SIP:1: format (the SDK's standalone send flow). Code review caught that sipher's actual production flow uses the vault's withdraw_private instruction which emits an Anchor VaultWithdrawEvent in tx.meta.logMessagesno SPL memo. The plan was revised (commit 7621df6) to parse the event-log format that sipher's own scanForPayments already uses (single source of truth for both scan AND claim event parsing). The first Task 1 attempt was reset before any wrong tests landed.

Verification

  • Pre-deploy: https://sipher-api.sip-protocol.org/admin/api/torque/status should remain { enabled: true, network: 'devnet', ingesterReachable: true } after deploy.
  • Post-deploy: trigger a claim from sipher chat with a known recent deposit signature; confirm the Torque event in the dashboard attributes to the claim tx signature (not the deposit signature).

Follow-ups (separate issues, post-merge)

Will be filed against this PR's merge:

  • Spec 4 Path B — SDK extension + SignTxCard claim UX
  • Spec 5 PR-A — durable-nonce family (scheduleSend + drip)
  • Spec 5 PR-B — Squads delegation family (sweep + recurring)
  • Spec 5 PR-C — COURIER hardening (retries, observability, single-flight)
  • Claim Path A polish — auto-derive destinationWallet from spending pubkey, human-readable amount display in chat message
  • PR review polish — add tests/** to packages/agent/tsconfig.json, extract normalizeKey to shared utility, add beforeEach mock reset in claim.test.ts error-paths describe

Plan + spec references

  • Implementation plan: docs/superpowers/plans/2026-05-17-claim-phase-2-path-a.md (this PR adds the plan as commit 037cffa, revised as 7621df6)
  • Design spec: docs/superpowers/specs/2026-05-15-claim-phase-2-design.md (on the design-specs PR docs(superpowers): 5 design specs from PR-E + sipher#262 follow-up list #276 — read the "Amendment — SDK reality check" section for Path A vs Path B trade-off)

rz1989s added 11 commits May 17, 2026 06:44
…arsing

Original Task 1 helper assumed spl-memo SIP:1: format (SDK's standalone
send flow). Sipher's actual production deposits emit Anchor
VaultWithdrawEvent in tx.meta.logMessages via the vault's withdraw_private
instruction — no SPL memo. Code review caught this before any wrong tests
landed; this revision rewrites Tasks 1 + 2 to parse the event-log format
that sipher's own scanForPayments already uses (single source of truth for
both scan AND claim event parsing).

Also addresses reviewer findings:
- spl-token-2022 support in SPL transferChecked finder
- Inner-instruction search for Jupiter-routed deposits
- Cleaner type guard via ParsedInstruction type narrowing
- ATA-owner equality check against event stealth_recipient
- Dropped unused invalid_withdraw_event code (parser skips malformed
  events and continues — code was unreachable)
- destinationWallet now required in both type + Anthropic schema
  (was optional with runtime throw; schema-vs-runtime gap)
- Remove deriveDestinationFromSpending placeholder helper
- viewingKey + spendingKey schema descriptions clarify hex-only input
- Replace USDC_MINT fallback with invariant throw (dead code; was a
  devnet footgun)
- Drop unused USDC_MINT import
- Update input-validation tests to pass destinationWallet for type
  conformance (tests still verify the validation error path)
@vercel
Copy link
Copy Markdown

vercel Bot commented May 17, 2026

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

Project Deployment Actions Updated (UTC)
sipher Ready Ready Preview, Comment May 17, 2026 1:39am

@rz1989s
Copy link
Copy Markdown
Member Author

rz1989s commented May 17, 2026

Follow-up issues filed

Path A made destinationWallet required in ClaimParams. consolidate.ts
schedules action='claim' ops via createScheduledOp; without
destinationWallet in params, COURIER would throw at the SDK boundary
on every fire and reset the op to 'pending' for perpetual 60s retry.

Use params.wallet as the destination — that's the user's main wallet,
which is also where consolidate intends the consolidated funds to land.

Caught by final cross-cutting code review of PR #281.
@rz1989s
Copy link
Copy Markdown
Member Author

rz1989s commented May 17, 2026

Final cross-cutting code review caught one Important issue: consolidate.ts schedules action: 'claim' ops without destinationWallet, and Path A made that field required. COURIER would throw at the SDK boundary every 60s and reset the op to pending — perpetual noisy retry.

Fixed in 3b937ef by adding destinationWallet: params.wallet to the scheduled op's params (the user's main wallet IS the intended destination for consolidate). 1-line addition, no test changes needed (scheduled-op broadcast paths are still gated on Spec 5 PR-A/B/C — #283/#284/#285). 1612 tests still passing, typecheck clean.

PR #280's recursive typecheck (`pnpm -r --filter='!sipher' run typecheck`)
exposed a build-order issue that the previous root-only typecheck masked.
The agent package imports from @sipher/sdk (workspace dep) whose types live
in `dist/index.d.ts` — without building the SDK first, tsc fails with
'Cannot find module @sipher/sdk' for every consumer.

Local typecheck passes because dist/ exists from prior builds; CI does
fresh install without building. This has been failing on main since
PR #280 merged 2 days ago (last green: 7389a3e on PR #279 merge).

Add a Build SDK step between Install and Typecheck. Fixes both the
Typecheck and Test steps in one shot (tests also import the runtime
@sipher/sdk via dist/index.js).
@rz1989s rz1989s merged commit 4501045 into main May 17, 2026
8 checks passed
@rz1989s rz1989s deleted the feat/spec-4-path-a-claim-phase-2 branch May 17, 2026 01:41
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.

1 participant