Skip to content

feat: consume ts-sdk PR #554 (arkd signer rotation) + flag deprecated-signer contracts - #670

Merged
Kukks merged 25 commits into
masterfrom
feat/signer-rotation-sdk-pr554
Jun 15, 2026
Merged

Kukks merged 25 commits into
masterfrom
feat/signer-rotation-sdk-pr554

Conversation

@Kukks

@Kukks Kukks commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Draft — depends on the unpublished SDK PR arkade-os/ts-sdk#554. It must be merged and @arkade-os/sdk + @arkade-os/boltz-swap published to npm before this can merge (see the checklist).

What

Makes the wallet build against ts-sdk#554 (arkd signer-rotation support) and surfaces deprecated signers per contract.

1. Consume the unpublished SDK PR via vendored tarballs

ts-sdk is a pnpm monorepo built with tsup that ships only dist/, has no prepare script, keeps its build toolchain at the repo root, and links boltz-swap → sdk with workspace:* — so the packages can't be installed straight from a git ref.

  • scripts/prepare-arkade-sdk.js clones the ref pinned in package.json ("arkadeSdk", currently 52236be), builds both packages, and packs them into vendor/*.tgz.
  • Those tarballs are committed so every install context (CI, Playwright, Cloudflare previews, local) resolves them at checkout under a normal frozen lockfile.
  • package.json references them via file:, plus a pnpm.overrides entry so boltz-swap's workspace:* sdk dependency resolves to the same build (one SDK instance, not a mix with the published 0.4.34).

To bump the pinned commit: pnpm prepare:sdk (optionally ARKADE_SDK_DIR=/path/to/ts-sdk to reuse a checkout), then commit the regenerated vendor/*.tgz + pnpm-lock.yaml.

2. Flag deprecated-signer contracts (dev mode only)

Settings → Advanced → Contracts (already dev-gated) classifies each contract's params.serverPubKey against the operator's advertised signer set via the SDK's signerSetFromInfo / classifyAgainstSignerSet:

  • deprecated signer — minted under a deprecated signer, cutoff not yet passed.
  • deprecated signer · past cutoff (red) — the cooperative-migration cutoff has passed.

Background auto-migration (SettlementConfig.deprecatedSignerMigration, default true) is unchanged.

3. Clearer message when the client is too old

When the server enforces a minimum X-Build-Version and the wallet is below it, arkd rejects every call (including getInfo) with a BUILD_VERSION_TOO_OLD error — so the wallet previously showed a misleading "server unreachable". getAspInfo now detects the typed ArkError and surfaces "Your wallet is outdated and needs to be updated to be compatible with the latest Arkade version." across the Unavailable / Init / About / Server screens.

Note: on arkd v0.9.9-rc.1 the build-version and digest guard interceptors run outside the error-detail converter, so their REST errors arrive with an empty details[] and the structured name only in the message string. The SDK bump (52236be) makes maybeArkError fall back to parsing the NAME (code): message prefix, which also repairs DIGEST_MISMATCH detection (mid-session signer rotation) against rc.1. Root-cause fix belongs in arkd (reorder errorConverter to wrap the guards).

Verification

  • tsc --noEmit, pnpm lint, prettier (changed files) — clean
  • pnpm build (app + service-worker bundle) — builds against the PR SDK
  • pnpm test:unit — 276 passed, incl. badge tests (past-cutoff / deprecated / active-signer) and outdated-client tests (getAspInfo / aspErrorText)
  • Dev server boots and resolves the vendored SDK
  • CI=true pnpm install (frozen) resolves the committed tarballs

Merge checklist

  • ts-sdk#554 merged and @arkade-os/sdk + @arkade-os/boltz-swap published to npm
  • Revert the temporary plumbing: delete scripts/prepare-arkade-sdk.js, the arkadeSdk field, the pnpm.overrides, the committed vendor/ tarballs, and the .arkade-sdk-src ignore; point the two deps at the published versions

Summary by CodeRabbit

Release Notes

  • New Features
    • Upgraded Contracts screen with searchable, filterable, virtualized contract list, expandable details, and external explorer links; includes improved deprecated-signer indicators.
  • Bug Fixes
    • Improved detection and handling of outdated/unreachable server responses, with clearer “update your wallet” messaging across wallet and settings screens.
  • Tests
    • Added/updated unit and UI tests for server error messaging and Contracts filtering/rendering.
  • Chores
    • Updated SDK dependency versions.

…ed-signer contracts

Build @arkade-os/sdk and @arkade-os/boltz-swap from the unpublished
arkade-os/ts-sdk PR #554 (arkd signer-rotation support) and surface
deprecated signers in the dev-only Contracts list.

Dependency mechanism (temporary, until the SDK is published to npm):
- scripts/prepare-arkade-sdk.js builds both packages from the git ref
  pinned in package.json ("arkadeSdk") into gitignored vendor/*.tgz.
- package.json references them via file:, plus a pnpm override so
  boltz-swap's workspace:* sdk dependency resolves to the same build.
- CI builds the tarballs from the pinned ref before install (Node 24).

Feature:
- Settings > Advanced > Contracts (dev mode only): each contract is
  classified against the operator's advertised signer set. Contracts
  minted under a deprecated signer show a "deprecated signer" badge,
  and "deprecated signer / past cutoff" once the cooperative-migration
  cutoff has passed.
- Background auto-migration (SettlementConfig.deprecatedSignerMigration,
  default true) is unchanged and stays enabled.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d416e5c7-1ceb-4ae4-9e67-b645536d479d

📥 Commits

Reviewing files that changed from the base of the PR and between 4ef0a6d and 8f3c9e8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • package.json
  • src/screens/Wallet/Send/Form.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • src/screens/Wallet/Send/Form.tsx

Walkthrough

Adds BUILD_VERSION_TOO_OLD detection to getAspInfo, extending AspInfo with outdated and minBuildVersion fields and introducing an aspErrorText helper. This helper replaces hardcoded unreachable-server strings across all screens. The Contracts screen is rewritten with virtualization, search/filter chips, expandable cards, and deprecated-signer badges.

Changes

ASP Outdated Error Handling and Contracts Screen Refactor

Layer / File(s) Summary
AspInfo type, aspErrorText helper, and getAspInfo detection
src/providers/asp.tsx, src/lib/asp.ts, package.json, src/test/lib/asp.test.ts
AspInfo gains optional outdated and minBuildVersion fields. getAspInfo catches ArkError named BUILD_VERSION_TOO_OLD and returns unreachable+outdated state with extracted minBuildVersion (from metadata or parsed message). aspErrorText returns an update-required message when outdated. SDK deps bumped to 0.3.40/0.4.35. Tests cover both metadata paths and fallback/outdated branches.
aspErrorText propagation across screens
src/screens/Init/Init.tsx, src/screens/Settings/About.tsx, src/screens/Settings/Server.tsx, src/screens/Settings/Vtxos.tsx, src/screens/Wallet/Index.tsx, src/screens/Wallet/Notes/Form.tsx, src/screens/Wallet/Notes/Redeem.tsx, src/screens/Wallet/Send/Form.tsx, src/screens/Wallet/Unavailable.tsx
All screens replace hardcoded unreachable-server strings with aspErrorText(aspInfo, fallback). useEffect dependency arrays are expanded to include aspInfo.outdated. SendForm also updates the key-mismatch message to "Arkade server key mismatch".
Contracts screen virtualization, search, filter, and deprecated-signer badges
src/screens/Settings/Contracts.tsx, src/test/screens/settings/contracts.test.tsx
Contracts are enriched into ContractView (Taproot boarding address re-encoding, explorer URLs, signer deprecation classification, search haystack). New components DeprecatedSignerBadge, Chip, and expandable ContractCard replace static sections. useVirtualizer with dynamic row heights replaces non-virtualized rendering. Tests stub @tanstack/react-virtual for JSDOM and cover loading/empty/tab-filter/search/Taproot/deprecated-signer cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • arkade-os/wallet#637: Bumps the same @arkade-os/sdk and @arkade-os/boltz-swap dependencies in package.json, making it a direct predecessor to the SDK version bump in this PR.
  • arkade-os/wallet#645: Modifies src/screens/Settings/Contracts.tsx contract card and parameter display, which this PR extends further with virtualization, search, and filtering.

Suggested reviewers

  • bordalix
  • pietro909
  • arkanaai
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: updating to ts-sdk PR #554 for signer rotation and adding deprecated-signer contract flagging—both primary objectives of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/signer-rotation-sdk-pr554

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Code Review — #670

Reviewer: Arkana (automated, aggressive review)
Verdict: Request changes — CI broken, merge conflict likely, minor gaps


🔴 BLOCKING: CI broken — tarball integrity mismatch

Both CI runs fail at pnpm install. The prepare-arkade-sdk.js step succeeds (builds the tarballs), but the pnpm-lock.yaml committed in this PR has hardcoded integrity hashes that don't match the tarballs produced by a fresh build:

ERR_PNPM_TARBALL_INTEGRITY  Got unexpected checksum for
"vendor/arkade-os-boltz-swap.tgz".
Wanted "sha512-ocBak+y2r8vhR9rJB5X..."
Got    "sha512-SvAHOVWbxGu9RBeg2P7..."

File: pnpm-lock.yaml (lines ~243, ~255 — the integrity: fields for the two file:vendor/*.tgz entries)

The lockfile was generated from one build, but CI builds fresh tarballs with different checksums (tarball contents are non-deterministic across builds/environments). Fixes:

  1. Preferred: Add pnpm install --no-frozen-lockfile in CI (or --fix-lockfile) after the prepare step, OR
  2. Don't commit the tarball integrity hashes in the lockfile — regenerate after build with pnpm install as a CI step that precedes the "real" install

The first CI run (e2e workflow) also fails because it doesn't have the Build Arkade SDK step at all — the ci.yml changes only cover one workflow. Check if there's a second workflow file (e.g., e2e.yml or test.yml) that also needs the prepare step.


🟡 Likely merge conflict: Contracts.tsx diverged from master

The diff targets a version of src/screens/Settings/Contracts.tsx that uses Shadow, FlexRow, CopyRow components and Text tiny for badges. Current master has been refactored to use CSS class-based components (contract-card, contract-copy-field, CopyField, etc.) with a completely different ContractCard and Section signature. This PR will conflict on rebase.

Not blocking for a draft PR, but worth noting for when ts-sdk#554 merges and this is de-drafted.


🟡 Test coverage gaps

File: src/test/screens/settings/contracts.test.tsx

Three tests cover EXPIRED, MIGRATABLE, and CURRENT (active signer). Missing:

  • DUE_NOW — cutoff of 0n means "migrate immediately, no cutoff advertised". The badge groups it with MIGRATABLE (orange), but DUE_NOW is semantically more urgent. Consider either a distinct badge or at minimum a test proving the current behavior is intentional.
  • UNKNOWN_SIGNER — contract signer not in the operator's set at all. Currently renders no badge (falls through to return null). A test documenting this is intentional would be valuable.

🟢 Code quality — looks solid

scripts/prepare-arkade-sdk.js: Well-structured. Pinned commit hash (d748a5c), caching via ref marker, cross-drive safe (copyFileSync not rename), force-rebuild escape hatch, reuse-checkout support. The --config.ignore-scripts=true for pnpm pack is a nice touch to avoid the redundant prepack rebuild. No security concerns with the clone — the commit is pinned and verifiable.

src/screens/Settings/Contracts.tsx: The contractSignerStatus helper is properly defensive — catches exceptions, guards null signerSet and missing serverPubKey. The DeprecatedSignerBadge component is clean and does exactly what it should for a dev-gated display. signerSet is memoized once at the page level and shared across cards — correct pattern, no redundant computation.

package.json: The pnpm.overrides entry for @arkade-os/sdk correctly ensures a single SDK instance (preventing boltz-swap from resolving its workspace:* dep to a different version). The arkadeSdk config field is a clean way to pin the source of truth.


⚠️ Protocol-critical flag

This PR touches signer rotation classification — a protocol-adjacent concern. While the wallet changes here are display-only (badges on a dev-gated settings screen) and don't affect signing, migration, or VTXO handling, the upstream SDK functions (signerSetFromInfo, classifyAgainstSignerSet) are used by the SDK's migrateDeprecatedSignerVtxos and SettlementConfig.deprecatedSignerMigration for actual cooperative migration.

Requesting human review per protocol-critical policy. The display logic is correct, but a human should verify the integration assumptions (especially that aspInfo.signerPubkey and aspInfo.deprecatedSigners from the REST provider match what the classification functions expect).


Summary

Finding Severity File
CI tarball integrity mismatch 🔴 Blocking pnpm-lock.yaml, .github/workflows/ci.yml
Missing prepare step in e2e workflow 🔴 Blocking .github/workflows/ (second workflow)
Contracts.tsx diverged from master 🟡 Rebase needed src/screens/Settings/Contracts.tsx
Missing DUE_NOW + UNKNOWN_SIGNER tests 🟡 Coverage gap src/test/screens/settings/contracts.test.tsx
Protocol-critical: needs human sign-off ⚠️ Policy —

Good draft. Fix CI, rebase when ready, add the missing test cases.

🤖 Reviewed by Arkana

…lockfile

The first CI run failed at install: pnpm defaults to a frozen lockfile
under CI and rejected the rebuilt vendor/*.tgz (ERR_PNPM_TARBALL_INTEGRITY),
because the tarballs are not byte-reproducible. A preinstall hook can't fix
it either — pnpm resolves the file: dependencies before lifecycle scripts run,
so the tarballs must exist before "pnpm install" starts.

- .npmrc: frozen-lockfile=false (rebuilt tarballs have unstable integrity).
- ci.yml + playwright.yml: build the SDK from the pinned ref into vendor/*.tgz
  in a step before "pnpm install".

Note: Cloudflare Pages preview deploys are configured outside the repo and
cannot run a pre-install build step, so they will fail until the SDK is
published to npm.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8f3c9e8
Status: ✅  Deploy successful!
Preview URL: https://5123c2d2.wallet-bitcoin.pages.dev
Branch Preview URL: https://feat-signer-rotation-sdk-pr5.wallet-bitcoin.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8f3c9e8
Status: ✅  Deploy successful!
Preview URL: https://0425b199.arkade-wallet.pages.dev
Branch Preview URL: https://feat-signer-rotation-sdk-pr5.arkade-wallet.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026 •

Copy link
Copy Markdown

Deploying tmp-boltz-upstream-mainnet-arkade-wallet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8f3c9e8
Status: ✅  Deploy successful!
Preview URL: https://829e76be.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev
Branch Preview URL: https://feat-signer-rotation-sdk-pr5.tmp-boltz-upstream-mainnet-arkade-wallet.pages.dev

View logs

Cloudflare Pages preview deploys are configured outside the repo and run a
bare `pnpm install` with no pre-build step, so they could not build the
unpublished SDK from a git ref. Commit the prebuilt @arkade-os/sdk +
@arkade-os/boltz-swap tarballs so every install context (CI, Playwright,
Cloudflare previews, local) resolves them at checkout under a frozen lockfile.

- vendor/*.tgz are now tracked. Regenerate with `pnpm prepare:sdk` (builds
  from the ref pinned in package.json) and commit the updated tarballs plus
  pnpm-lock.yaml when bumping the ref.
- Revert the CI/Playwright "Build Arkade SDK" steps and the Node 24 bump, and
  remove .npmrc: frozen-lockfile is restored because the committed tarballs
  have stable integrity.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits 3e3a7f9, a03b5fad

Focus: only changes since my previous review.


🔴 Previous blockers — RESOLVED ✅

Both CI blockers from my first review are fixed:

  1. Tarball integrity mismatch → Fixed by committing the prebuilt vendor/*.tgz tarballs. The lockfile integrity hashes now match the committed binaries. pnpm install --frozen-lockfile works in all contexts.

  2. Missing prepare step in e2e/Playwright workflow → No longer needed. Both ci.yml and playwright.yml are unchanged from master (Node 22, no extra build steps). Clean.

Cloudflare Pages previews are now passing (were failing before). CI test checks are pending at time of review.

Approach assessment

Committing vendor tarballs is pragmatic for a draft PR. The committed binaries are from a pinned, verifiable git hash (d748a5c). The merge checklist correctly calls for removing all of this plumbing once the SDK is published.

One minor note: the vendor/*.tgz files add ~2MB of binary to git history. Not a concern for a temporary branch, but ensure the merge checklist item (revert to npm versions) is done before merging.

Remaining items from first review (unchanged, not repeating in detail)

  • 🟡 Contracts.tsx will conflict on rebase against current master (CSS refactor)
  • 🟡 Missing DUE_NOW and UNKNOWN_SIGNER test cases
  • ⚠️ Protocol-critical (signer rotation): still needs human sign-off per policy

No new issues found in the new commits. Previous request-changes verdict stands for the test coverage gap and human sign-off requirement.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@8b630a0. The new commits only
touch digest-mismatch error handling (ark provider) and a DigestMismatchError
export; the signer-rotation classification API the wallet uses is unchanged.

Also switch the contracts test's BigInt literals (1n) to BigInt(): the repo
targets < ES2020, so production code uses BigInt() — CI has no tsc step, so
the literals slipped through.

Verified against the new build: tsc, lint, prettier, build (app + worker),
270 unit tests, and a frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit e37055f9

Scope: SDK ref bump (d748a5c → 8b630a0) + BigInt literal fix in tests.

Findings

✅ Ref bump — Clean. The new ts-sdk commits (d748a5c..8b630a0) only touch digest-mismatch error handling in the ark provider and a DigestMismatchError export. The signer-rotation classification API (signerSetFromInfo, classifyAgainstSignerSet) this PR depends on is unchanged. Vendor tarball and .ref marker updated consistently.

✅ BigInt syntax fix (contracts.test.tsx:128,134,140) — Correct. The repo targets < ES2020, so BigInt(1) is the right form. 1n literals require ES2020 or later in tsconfig.json. Good catch that CI lacked a tsc step to surface this earlier.

No new issues. Previous request-changes verdict (human sign-off required for protocol-adjacent signer classification logic) still stands.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@00ed22d. New commits:
- feat: exclude post-cutoff deprecated-signer funds from spendable balance
- fix(delegate): don't send X-Build-Version to the delegate service (CORS)

The signer-rotation classification API and Contract type the wallet uses
are unchanged. Verified against the new build: tsc, build (app + worker),
270 unit tests, and a frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit f306a861

Scope: SDK ref bump (8b630a0 → 00ed22d), vendor tarball rebuild.

SDK delta (8b630a0..00ed22d)

Two new ts-sdk commits:

  1. fix(delegate): don't send X-Build-Version to the delegate service (CORS) — Non-critical, browser fetch header fix.
  2. feat(wallet): exclude post-cutoff deprecated-signer funds from spendable balance — Protocol-critical. Adds selectPendingRecoveryOutpoints() in vtxo-manager.ts, changes getBalance() to bucket EXPIRED-signer VTXOs under pendingRecovery instead of settled/preconfirmed, and filters them from coin selection in send(). This prevents selecting VTXOs the operator won't co-sign.

Impact on this PR

✅ None. The wallet's Contracts.tsx only uses signerSetFromInfo, classifyAgainstSignerSet, SignerSet, and SignerStatus — all unchanged. The new pendingRecoveryOutpoints(), balance bucketing, and coin-selection filtering are internal to the SDK's Wallet class and don't surface in this PR's diff.

✅ Tarball + lockfile + .ref marker all updated consistently.

Assessment

This commit is a clean ref bump. The protocol-critical logic lives entirely in ts-sdk#554 and should be reviewed there, not here. No wallet-side code changes.

Previous verdict stands: human sign-off still required (signer rotation is protocol-critical). Test coverage gaps noted in earlier reviews still apply.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@84267a3:
- fix(providers): don't send X-Build-Version to Esplora and indexer (CORS)

Provider HTTP headers only; the signer-rotation API and Contract type the
wallet uses are unchanged. Verified against the new build: tsc, build
(app + worker), 270 unit tests, and a frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit db3e1f12

Scope: SDK ref bump (00ed22d → 84267a3), vendor tarball rebuild.

SDK delta (00ed22d..84267a3)

One ts-sdk commit:

  • fix(providers): don't send X-Build-Version to Esplora and indexer (CORS) — Strips a custom HTTP header from Esplora/indexer provider calls to avoid CORS preflight failures. Files changed: providers/indexer.ts, providers/onchain.ts, and related tests. Non-critical.

Impact on this PR

✅ None. Only package.json (ref field), pnpm-lock.yaml (integrity hash), vendor/.ref, and the vendor/arkade-os-sdk.tgz binary changed. The boltz-swap tarball is unchanged. The signer-rotation API (signerSetFromInfo, classifyAgainstSignerSet, SignerSet, SignerStatus) consumed by Contracts.tsx is untouched.

✅ Tarball + lockfile + .ref marker all consistent.

Assessment

Clean ref bump. No wallet-side code changes. No new issues.

Previous verdict stands: human sign-off still required (signer rotation is protocol-critical). Test coverage gaps noted in earlier reviews (DUE_NOW and UNKNOWN_SIGNER status branches) still apply.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@6c86a52:
- fix(wallet): register baseline contracts under deprecated signers at boot
- test(e2e): isolate contract-params-change from leftover deprecated signers

The signer-rotation API and Contract type the wallet uses are unchanged.
Verified against the new build: tsc, build (app + worker), 270 unit tests,
and a frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 89909fce

Scope: SDK ref bump (84267a3 → 6c86a52), vendor tarball rebuild.

SDK delta (84267a3..6c86a52)

Two ts-sdk commits:

  1. fix(wallet): register baseline contracts under deprecated signers at boot — Protocol-relevant. Expands the baseline contract matrix in wallet.ts to iterate over both the current server signer AND all cached deprecated signers, deduped by scriptHex. This ensures a wallet loaded with funds minted under a now-rotated signer registers those contracts at boot (not only after restore()). The fan-out is O(signers × timelocks × scriptTypes) which is fine — signer rotations are rare, timelocks are a small fixed set.
  2. test(e2e): isolate contract-params-change from leftover deprecated signers — Test hygiene. Adds cleanup in e2e utils to prevent deprecated signer state leaking between tests.

Impact on this PR

✅ None. Only package.json (ref field), pnpm-lock.yaml (integrity hash), vendor/.ref, and vendor/arkade-os-sdk.tgz changed. The signer-rotation API (signerSetFromInfo, classifyAgainstSignerSet, SignerSet, SignerStatus) consumed by Contracts.tsx is untouched. No wallet-side code changes.

✅ Tarball + lockfile + .ref marker all consistent.

Assessment

Clean ref bump. The protocol-critical logic (deprecated-signer contract registration at boot) lives in ts-sdk#554 and should be reviewed there.

Previous verdict stands: human sign-off still required (signer rotation is protocol-critical). Test coverage gaps noted in earlier reviews (DUE_NOW and UNKNOWN_SIGNER status branches in Contracts.tsx) still apply.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@41c8ee0:
- fix(wallet): register boarding baseline contract under deprecated signers

The signer-rotation API and Contract type the wallet uses are unchanged.
Verified against the new build: tsc, build (app + worker), 270 unit tests,
and a frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit bdf6dcea

Scope: SDK ref bump (6c86a52 → 41c8ee0), vendor tarball rebuild.

What changed in the SDK

Single commit 41c8ee0: fix(wallet): register boarding baseline contract under deprecated signers

The ReadonlyWallet.getContractManager() now loops over baselineSigners (current + deprecated) when registering boarding contracts — previously it only registered the boarding baseline under the current signer. This means boarding UTXOs minted under a now-rotated signer are watched at boot without needing an explicit restore().

Assessment

  1. Logic is correct: The boarding loop mirrors the existing default/delegate matrix fan across baselineSigners. Dedup via seenBaselineScripts prevents double-registration when a boarding script collides with an already-registered default one (degenerate case where boardingExitDelay == unilateralExitDelay).

  2. Test updated: The test now sets boardingExitDelay: 288 (distinct from the mock's unilateralExitDelay: 144) so the boarding baseline is a separate contract, and asserts both default and boarding types exist under the deprecated signer.

  3. Wallet-side: Only the ref pin and vendor tarball changed — no wallet code changes.

  4. ⚠️ Protocol-critical: This touches signer rotation + boarding contract registration (funds at stake if a boarding UTXO under a rotated signer isn't watched). The SDK change looks correct — the loop + dedup + ensureWalletContract is the right pattern — but this needs human review on ts-sdk#554 before merging.

No new issues. Previous review findings still apply.

🤖 Reviewed by Arkana

Kukks added 2 commits June 13, 2026 19:20
Rework the dev-only Settings → Contracts screen to handle many contracts:

- Compact, collapsible rows (type · address · deprecated-signer badge ·
  state · age); tap to reveal address/script/parameters with copy actions.
- Active/Inactive tab, type-filter chips, and a search box (matches type,
  address, script, params).
- Virtualized list (@tanstack/react-virtual) with dynamic row heights and
  open-state kept in the parent so it survives rows scrolling out.
- Boarding contracts now show their on-chain Bitcoin Taproot address
  (bc1p/tb1p/bcrt1p), derived from the P2TR script, instead of the ark
  encoding.
- Per-section count.

Tests updated for the tabbed/filtered UI (virtualizer mocked under jsdom),
with added coverage for search, tab filtering, and the boarding address.
- Each contract's address links out to a block explorer: the Arkade
  explorer for ark addresses, the on-chain mempool explorer for boarding
  Bitcoin Taproot addresses (external-link icon on the expanded address row).
- Much more compact filter bar: a single-line search input and one wrapping
  chip row (Active/Inactive · type chips · count), replacing the tall search
  box and segmented control. Frees vertical space so the list no longer gets
  cut off at the bottom (also fixed the flexbox min-height:0 bound).
- Key the list/open-state by contract.script (the primary key) instead of
  address, which can collide across contracts (e.g. boarding) and made them
  expand together.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits 68b97435, 15c910eb

Scope: Compact/searchable/filterable contracts list with virtualization, explorer links, collapsible cards.

Verdict: ✅ No blocking issues

These are purely UI changes — no protocol-critical paths touched (no VTXO handling, signing, forfeit, round, or exit logic).

Notes

  1. getItemKey / openSet keyed by contract.script (Contracts.tsx:297, Contracts.tsx:303): The comment correctly notes that address can collide across boarding contracts. But script could also collide if two contracts share the same scriptPubKey (e.g. same boarding UTXO re-registered after a sweep). If that happens, the virtualizer would assign duplicate keys (console warning, potential rendering glitch) and toggling one card would toggle both. Low probability in practice — just noting it. If contracts have a unique ID field in the SDK, that would be safer.

  2. typeFilter not reset on data change (Contracts.tsx:267-275): If the user selects a type filter (e.g. "vhtlc"), then the wallet context changes and the new wallet has no "vhtlc" contracts, the chip stays selected but the list shows 0 results with no visible indication of why. Minor UX papercut — consider resetting typeFilter to 'all' in the useEffect that fetches contracts, or filtering out stale type chips.

  3. onchainTaprootAddress is correct (Contracts.tsx:37-45): P2TR scriptPubKey regex, witness version 1, bech32m encoding, correct HRP mapping (bc/bcrt/tb). Falls back to the original address on any error. Display-only — no signing or spending implications.

  4. Test coverage is solid: Virtualizer mock is clean, new tests cover tab filtering, search, and boarding address display. The deprecatedAspInfo / underSigner helpers reduce boilerplate nicely.

  5. New dep @tanstack/react-virtual@3.13.19: Locked in pnpm-lock, peer deps match React 18. Fine.

🤖 Reviewed by Arkana

The icon group was wrapped in a FlexRow, which defaults to width:100%, so
the icons left-aligned next to the value. Add `end` (width:undefined) so
the wrapper shrinks to its content and the row's space-between pushes it to
the right edge, matching the other copy rows.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 0456fba5

Scope: CSS fix — right-align copy/explorer icons in expanded contract rows.

Change

Contracts.tsx:111: Added end prop to inner <FlexRow> wrapping the copy + explorer icons in CopyRow. This pushes the icons to the right edge of the row instead of leaving them adjacent to the label text.

Assessment

✅ No issues. One-line cosmetic fix. No protocol, signing, or data-flow changes. No review action needed.

Previous review findings still apply (human sign-off required for protocol-critical signer rotation paths in ts-sdk#554).

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@0e7378b:
- fix(contractWatcher): recover promptly after subscription disruption (#564)
- merge master into the PR branch

The signer-rotation API and Contract type the wallet uses are unchanged.
Verified against the new build: tsc, build (app + worker), unit tests, and a
frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 19f4ed1f

Scope: Bump pinned ts-sdk ref from 41c8ee0 → 0e7378b.

What changed

  • package.json — arkadeSdk.ref updated
  • pnpm-lock.yaml — integrity hash updated for the rebuilt tarball
  • vendor/.ref — ref marker updated
  • vendor/arkade-os-sdk.tgz — rebuilt tarball (binary, ~480 bytes larger)

What's in the new SDK ref

The delta between 41c8ee0 and 0e7378b is two commits:

  1. 664033ab — fix(contractWatcher): recover promptly after subscription disruption (ts-sdk#564). Adds reconnect resilience to the contract watcher (+29 lines in contractWatcher.ts, +29 lines test).
  2. 0e7378b4 — Merge of origin/master into the signer-rotation feature branch. Catches the branch up.

Assessment

No wallet-side code changes. This is a clean SDK pin bump. The contractWatcher fix is a net positive — better reconnect handling reduces the chance of stale contract state in the wallet.

✅ No issues found.

🤖 Reviewed by Arkana

Rebuild vendor/*.tgz from arkade-os/ts-sdk@6c59b22. The new commits are
e2e/regtest/CI only (drive signer rotation through the regtest CLI,
run-on-all-PRs, regtest submodule bumps, merge #563) — no src changes,
so the SDK build is unchanged.

Verified against the new build: tsc, build (app + worker), unit tests, and a
frozen CI=true install all pass.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit c3cc6162

Scope: Bump pinned ts-sdk ref from 0e7378b → 6c59b22.

Trivial change — ref bump + regenerated vendor/arkade-os-sdk.tgz. The commit message confirms the upstream delta is e2e/regtest/CI-only (no src/ changes), so the SDK build artifact is functionally identical. package.json, pnpm-lock.yaml, vendor/.ref all consistent.

Verdict: ✅ No issues.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 0f6691d6

Scope: Bump pinned ts-sdk ref from 6c59b22 → ac4d3a5.

The upstream SDK change is a single-line version bump (buildVersion 0.9.7 → 0.9.9 in packages/ts-sdk/src/utils/fetch.ts). The wallet commit regenerates the vendored tarball and updates the lockfile integrity hash accordingly.

Files changed (all mechanical):

  • package.json — pinned ref updated
  • vendor/.ref — matches new ref
  • vendor/arkade-os-sdk.tgz — rebuilt tarball
  • pnpm-lock.yaml — integrity hash updated

No protocol, security, or logic changes. LGTM.

🤖 Reviewed by Arkana

Kukks and others added 7 commits June 14, 2026 13:23
arkd's guard interceptors run outside its error-detail converter, so the
REST rejection carries an empty details[] and the name only in the
message. Bump the vendored SDK to 52236be (maybeArkError message-prefix
fallback) and extract the min version from the message when metadata is
absent, so the wallet shows the outdated-client prompt instead of
'Arkade server unreachable'.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits a95ffb55..f367072f

Scope: 7 new commits — BUILD_VERSION_TOO_OLD detection, "Ark" → "Arkade" copy, vendored tarballs removed in favor of published SDK 0.4.35 / boltz-swap 0.3.40.


🔴 Blocking: Lockfile still references deleted vendor tarball

pnpm-lock.yaml is broken. package.json correctly points @arkade-os/sdk at 0.4.35, but the lockfile was not regenerated:

  • Line 8 still has overrides: '@arkade-os/sdk': file:./vendor/arkade-os-sdk.tgz (the pnpm.overrides was removed from package.json but the lockfile retains it)
  • Line 17-18 still resolves SDK as specifier: file:vendor/arkade-os-sdk.tgz / version: file:vendor/arkade-os-sdk.tgz
  • Lines 254, 4271 still define the @arkade-os/sdk@file:vendor/arkade-os-sdk.tgz package entry
  • vendor/arkade-os-sdk.tgz was deleted in commit f367072f

Result: pnpm install --frozen-lockfile (CI) will fail — the lockfile references a file that no longer exists.

Fix: Run pnpm install to regenerate the lockfile after the tarball removal, or do the two-step: first remove the pnpm.overrides + update package.json, then pnpm install, then commit the resulting lockfile.


⚠️ Minor: Inconsistent aspErrorText usage

aspErrorText() is used on 4 screens (Init, About, Server, Unavailable) but 5 others still use hardcoded 'Arkade server unreachable':

  • Vtxos.tsx:58
  • Wallet/Index.tsx:94
  • Notes/Form.tsx:32
  • Notes/Redeem.tsx:32
  • Send/Form.tsx:120, 458

If the server enforces a minimum build version mid-session (server upgrades while wallet is open), these screens will show "Arkade server unreachable" instead of the actionable "update your wallet" message. Not blocking, but worth making consistent.


✅ Good

  • asp.ts: ArkError + err.name check is well-structured. The regex fallback for minBuildVersion when metadata is missing is pragmatic given the arkd guard-interceptor ordering issue noted in the PR description.
  • asp.test.ts: Covers the three key paths (metadata present, metadata absent/regex, generic failure). Clean mock setup preserving ArkError as a real class for instanceof.
  • No protocol-critical code touched — these are all UI/error-display changes.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits a95ffb55..f367072f

Scope: 7 new commits — BUILD_VERSION_TOO_OLD detection, "Ark" → "Arkade" copy, vendored tarballs removed in favor of published SDK 0.4.35 / boltz-swap 0.3.40.


🔴 Blocking: Lockfile still references deleted vendor tarball

pnpm-lock.yaml is broken. package.json correctly points @arkade-os/sdk at 0.4.35, but the lockfile was not regenerated:

  • Line 8 still has overrides: '@arkade-os/sdk': file:./vendor/arkade-os-sdk.tgz (the pnpm.overrides was removed from package.json but the lockfile retains it)
  • Line 17-18 still resolves SDK as specifier: file:vendor/arkade-os-sdk.tgz / version: file:vendor/arkade-os-sdk.tgz
  • Lines 254, 4271 still define the @arkade-os/sdk@file:vendor/arkade-os-sdk.tgz package entry
  • vendor/arkade-os-sdk.tgz was deleted in commit f367072f

Result: pnpm install --frozen-lockfile (CI) will fail — the lockfile references a file that no longer exists.

Fix: Run pnpm install to regenerate the lockfile after the tarball removal, or do the two-step: first remove the pnpm.overrides + update package.json, then pnpm install, then commit the resulting lockfile.


⚠️ Minor: Inconsistent aspErrorText usage

aspErrorText() is used on 4 screens (Init, About, Server, Unavailable) but 5 others still use hardcoded 'Arkade server unreachable':

  • Vtxos.tsx:58
  • Wallet/Index.tsx:94
  • Notes/Form.tsx:32
  • Notes/Redeem.tsx:32
  • Send/Form.tsx:120, 458

If the server enforces a minimum build version mid-session (server upgrades while wallet is open), these screens will show "Arkade server unreachable" instead of the actionable "update your wallet" message. Not blocking, but worth making consistent.


✅ Good

  • asp.ts: ArkError + err.name check is well-structured. The regex fallback for minBuildVersion when metadata is missing is pragmatic given the arkd guard-interceptor ordering issue noted in the PR description.
  • asp.test.ts: Covers the three key paths (metadata present, metadata absent/regex, generic failure). Clean mock setup preserving ArkError as a real class for instanceof.
  • No protocol-critical code touched — these are all UI/error-display changes.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits 66a5c1f6, 2e4478c6

Both blocking issues from my previous review are resolved:

🔴→✅ Lockfile fixed (66a5c1f6): pnpm-lock.yaml now correctly resolves @arkade-os/sdk@0.4.35 and @arkade-os/boltz-swap@0.3.40 from the registry. No remaining references to the deleted vendor/*.tgz files. pnpm install --frozen-lockfile should pass.

⚠️→✅ aspErrorText consistency (2e4478c6): All 5 screens I flagged now use aspErrorText(aspInfo, fallback) instead of hardcoded strings:

  • Vtxos.tsx:58 ✅
  • Wallet/Index.tsx:94 ✅
  • Notes/Form.tsx:32 ✅
  • Notes/Redeem.tsx:32 ✅
  • Send/Form.tsx:120, 458 ✅

Send/Form.tsx correctly adds aspInfo.outdated to the useEffect dependency array (line 465) so the error text updates if the outdated state changes mid-session.

Cosmetic nit (non-blocking): Send/Form.tsx:463 still sets setLabel('Server unreachable') even when the issue is an outdated client. Could say info.outdated ? 'Update required' : 'Server unreachable' for consistency with the error message, but this is minor.

No protocol-critical code in this delta. LGTM.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commits 66a5c1f6, 2e4478c6

Both blocking issues from my previous review are resolved:

🔴→✅ Lockfile fixed (66a5c1f6): pnpm-lock.yaml now correctly resolves @arkade-os/sdk@0.4.35 and @arkade-os/boltz-swap@0.3.40 from the registry. No remaining references to the deleted vendor/*.tgz files. pnpm install --frozen-lockfile should pass.

⚠️→✅ aspErrorText consistency (2e4478c6): All 5 screens I flagged now use aspErrorText(aspInfo, fallback) instead of hardcoded strings:

  • Vtxos.tsx:58 ✅
  • Wallet/Index.tsx:94 ✅
  • Notes/Form.tsx:32 ✅
  • Notes/Redeem.tsx:32 ✅
  • Send/Form.tsx:120, 458 ✅

Send/Form.tsx correctly adds aspInfo.outdated to the useEffect dependency array (line 465) so the error text updates if the outdated state changes mid-session.

Cosmetic nit (non-blocking): Send/Form.tsx:463 still sets setLabel('Server unreachable') even when the issue is an outdated client. Could say info.outdated ? 'Update required' : 'Server unreachable' for consistency with the error message, but this is minor.

No protocol-critical code in this delta. LGTM.

🤖 Reviewed by Arkana

@pietro909
pietro909 marked this pull request as ready for review June 15, 2026 12:55

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Code Review — feat/signer-rotation-sdk-pr554

Overall this is a well-structured PR: good SDK vendoring approach (with clear cleanup plan), solid test coverage for the new paths, and the virtualized contract list is a nice perf improvement. That said, I'm flagging a few issues — one bug, one protocol-critical note, and some minor items.


🐛 Bug: Missing aspInfo.outdated in useEffect dependency arrays (3 files)

Send/Form.tsx and Server.tsx correctly added aspInfo.outdated to their useEffect deps after introducing aspErrorText(aspInfo, ...). Three other files did not:

  • src/screens/Wallet/Notes/Form.tsx — }, [aspInfo.unreachable]) (line ~33 in diff)
  • src/screens/Wallet/Notes/Redeem.tsx — }, [aspInfo.unreachable]) (line ~32 in diff)
  • src/screens/Settings/Vtxos.tsx — }, [aspInfo.unreachable]) (line ~58 in diff)

All three call aspErrorText(aspInfo, ...) which reads aspInfo.outdated, but the effect won't re-fire if outdated changes while unreachable stays true. Scenario: server flips from BUILD_VERSION_TOO_OLD to a generic network error (or vice versa) — unreachable stays true both times, but the error text is stale. Fix: add aspInfo.outdated to each deps array, matching what Server.tsx and Send/Form.tsx already do.

This is also an eslint-plugin-react-hooks/exhaustive-deps violation.


⚠️ Protocol-Critical: Signer rotation classification — requesting human review

The contractSignerStatus() function in Contracts.tsx and the DeprecatedSignerBadge component surface signer rotation status to users. While this is display-only (the actual migration logic lives in the SDK's SettlementConfig.deprecatedSignerMigration), incorrect classification could mislead users about the safety of their VTXOs — e.g., showing "active signer" when the signer is actually deprecated and past cutoff, potentially causing users to delay a time-critical unilateral exit.

The classification logic delegates correctly to classifyAgainstSignerSet() from the SDK, and the badge mapping (EXPIRED → red "past cutoff", MIGRATABLE/DUE_NOW → orange "deprecated signer", everything else → no badge) is sound.

Per protocol rules: this touches signer rotation and implicitly affects user decisions about exit paths. Requesting explicit human sign-off.


Minor observations (non-blocking)

  1. Silent catch blocks — onchainTaprootAddress, contractSignerStatus, and the signerSetFromInfo memo all silently swallow errors with bare catch {}. Consider at least a console.debug in dev mode so misconfigurations don't fail silently during development.

  2. onchainTaprootAddress correctness ✅ — Verified: the P2TR scriptPubKey check (/^5120[0-9a-f]{64}$/i), witness program extraction, HRP mapping (bc/tb/bcrt), and bech32m encoding with witness version 1 are all correct.

  3. Virtualizer key choice — Using contract.script as the virtualizer key is a good call (avoids address collisions on boarding contracts). The comment explaining this is appreciated.

  4. SDK error boundary — The getAspInfo catch path correctly checks err instanceof ArkError && err.name === BUILD_VERSION_TOO_OLD, and the fallback regex err.message.match(/>=\s*v?([\d.]+)/) handles the rc.1 case where metadata is absent. The test covers both paths (with and without metadata). Solid.

  5. Trailing period inconsistency — Unavailable.tsx passes 'Arkade server unreachable.' (with period) while every other caller passes 'Arkade server unreachable' (without). Pre-existing, not introduced by this PR, but worth a cleanup pass.


Verdict

Request changes for the missing deps bug. The protocol-critical signer display needs a human to sign off.

The vendoring approach, SDK consumption, error handling, and UI improvements are all well-done. Fix the three dep arrays and this is ready for human approval.

🤖 Reviewed by Arkana

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/screens/Wallet/Unavailable.tsx (1)

17-28: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Effect is not reactive to ASP status changes.

Line 18 reads aspInfo.unreachable/aspInfo.outdated, but the effect runs only once ([]). If ASP status changes after mount, the message can stay stale and miss the outdated-wallet text.

Suggested fix
-  useEffect(() => {
-    if (aspInfo.unreachable) return setError(aspErrorText(aspInfo, 'Arkade server unreachable.'))
+  useEffect(() => {
+    if (aspInfo.unreachable) {
+      setError(aspErrorText(aspInfo, 'Arkade server unreachable.'))
+      return
+    }
     detectJSCapabilities()
       .then((result) => {
         if (result.isSupported) return
         // Use specific error message or fallback to iOS/generic message
         setError(result.errorMessage || getRestrictedEnvironmentMessage(isIOS()))
       })
       .catch(() => {
         setError(getRestrictedEnvironmentMessage(isIOS()))
       })
-  }, [])
+  }, [aspInfo.unreachable, aspInfo.outdated])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Wallet/Unavailable.tsx` around lines 17 - 28, The useEffect hook
starting with the aspInfo.unreachable check depends on the aspInfo object but
has an empty dependency array, causing the effect to run only once at mount and
miss updates to ASP status. Add aspInfo to the dependency array of the useEffect
call to make the effect reactive to changes in the aspInfo object, ensuring the
error message updates when ASP status changes from reachable to unreachable or
when the outdated state updates.
🧹 Nitpick comments (1)
src/screens/Settings/Contracts.tsx (1)

147-166: 💤 Low value

Consider adding keyboard accessibility to Chip.

The Chip component uses a <div> with onClick but lacks role="button", tabIndex={0}, and onKeyDown handling for Enter/Space. Users relying on keyboard navigation cannot activate these filter pills.

♿ Proposed accessibility fix
 function Chip({ label, active, onClick }: { label: string; active: boolean; onClick: () => void }) {
   return (
     <div
       onClick={onClick}
+      onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && (e.preventDefault(), onClick())}
+      role="button"
+      tabIndex={0}
       style={{
         padding: '0.25rem 0.6rem',
         borderRadius: '999px',
         cursor: 'pointer',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Settings/Contracts.tsx` around lines 147 - 166, The Chip
component currently uses a div with onClick but lacks keyboard accessibility
attributes and handlers, preventing keyboard users from interacting with the
filter pills. Add role="button" to the div to identify it as an interactive
element, add tabIndex={0} to make it focusable via keyboard navigation, and
implement an onKeyDown handler that triggers the onClick callback when the Enter
or Space keys are pressed. This ensures the component is fully accessible to
keyboard and screen reader users.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 66: The prepare:sdk script entry is defined in package.json but the
required arkadeSdk configuration object is missing. Add an arkadeSdk
configuration block to package.json with the required repo and ref properties
that the prepare-arkade-sdk.js script expects (as checked on lines 28-31 of that
script), or alternatively remove the prepare:sdk script entry from package.json
until the necessary configuration is added.

In `@scripts/prepare-arkade-sdk.js`:
- Line 107: Add the --frozen-lockfile flag to the pnpm install command in the
run() function call to ensure deterministic vendor artifacts. Modify the pnpm
install invocation to include --frozen-lockfile, which will fail fast if the
lockfile and package manifests diverge, preventing dependency-resolution drift
in the vendoring pipeline.
- Around line 75-90: The git checkout logic in the
`fs.existsSync(path.join(srcDir, '.git'))` block verifies that the commit hash
matches `cfg.ref` but does not verify that the working tree is clean. Add a
check using git status (e.g., `git status --porcelain`) to detect if there are
uncommitted changes or untracked files in the working tree. If the working tree
is dirty, either clean it automatically, fail with an error message, or warn and
proceed based on your project's reproducibility requirements. This check should
be performed after confirming the .git directory exists and before proceeding
with the fetch or checkout operations to prevent modified files from being
included in the generated tarballs.

In `@src/screens/Settings/Vtxos.tsx`:
- Around line 57-59: The useEffect hooks in three files are calling
aspErrorText(...) which depends on aspInfo.outdated, but the dependency arrays
only include aspInfo.unreachable. This causes the effect to not re-run when only
the outdated state changes, leaving stale error text. Add aspInfo.outdated to
the dependency array in the useEffect hook at src/screens/Settings/Vtxos.tsx
lines 57-59, the useEffect hook at src/screens/Wallet/Notes/Form.tsx lines
32-34, and the useEffect hook at src/screens/Wallet/Notes/Redeem.tsx lines 31-33
so the error message updates whenever either unreachable or outdated state
changes.

In `@src/screens/Wallet/Send/Form.tsx`:
- Around line 457-465: The conditional error clearing logic on line 460 only
removes the error if it matches the current errTxt (the unreachable message).
When transitioning from an outdated state to a reachable state, the outdated
message persists because it doesn't match the unreachable errTxt. Fix this by
unconditionally clearing the error to an empty string when aspInfo.unreachable
is false, rather than conditionally checking if prev === errTxt. This ensures
both ASP-unavailable message variants (unreachable and outdated) are properly
cleared when the server becomes reachable. Additionally, update the dependency
array to [aspInfo.unreachable] since that is the primary state being checked in
the if condition.

---

Outside diff comments:
In `@src/screens/Wallet/Unavailable.tsx`:
- Around line 17-28: The useEffect hook starting with the aspInfo.unreachable
check depends on the aspInfo object but has an empty dependency array, causing
the effect to run only once at mount and miss updates to ASP status. Add aspInfo
to the dependency array of the useEffect call to make the effect reactive to
changes in the aspInfo object, ensuring the error message updates when ASP
status changes from reachable to unreachable or when the outdated state updates.

---

Nitpick comments:
In `@src/screens/Settings/Contracts.tsx`:
- Around line 147-166: The Chip component currently uses a div with onClick but
lacks keyboard accessibility attributes and handlers, preventing keyboard users
from interacting with the filter pills. Add role="button" to the div to identify
it as an interactive element, add tabIndex={0} to make it focusable via keyboard
navigation, and implement an onKeyDown handler that triggers the onClick
callback when the Enter or Space keys are pressed. This ensures the component is
fully accessible to keyboard and screen reader users.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2756b74-2caa-41b9-9c2f-cf3db9d08596

📥 Commits

Reviewing files that changed from the base of the PR and between 13f2652 and 2e4478c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .gitignore
  • package.json
  • scripts/prepare-arkade-sdk.js
  • src/lib/asp.ts
  • src/providers/asp.tsx
  • src/screens/Init/Init.tsx
  • src/screens/Settings/About.tsx
  • src/screens/Settings/Contracts.tsx
  • src/screens/Settings/Server.tsx
  • src/screens/Settings/Vtxos.tsx
  • src/screens/Wallet/Index.tsx
  • src/screens/Wallet/Notes/Form.tsx
  • src/screens/Wallet/Notes/Redeem.tsx
  • src/screens/Wallet/Send/Form.tsx
  • src/screens/Wallet/Unavailable.tsx
  • src/test/lib/asp.test.ts
  • src/test/screens/settings/contracts.test.tsx
  • vitest.config.ts

Comment thread package.json Resolved
Comment thread scripts/prepare-arkade-sdk.js Resolved
Comment thread scripts/prepare-arkade-sdk.js Resolved
Comment thread src/screens/Settings/Vtxos.tsx Resolved
Comment thread src/screens/Wallet/Send/Form.tsx Resolved

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 4ef0a6d1

Three changes, all clean:

1. Orphaned vendor script cleanup ✅

Removes scripts/prepare-arkade-sdk.js, the prepare:sdk script entry, .gitignore entries for .arkade-sdk-src/ and vendor/, and the vitest exclude patterns. All of these became dead code after 54916bdf switched to published SDK 0.4.35. No issues.

2. Chip keyboard a11y ✅

Contracts.tsx:150-158 — Adds role='button', tabIndex={0}, and Enter/Space onKeyDown handler. Standard pattern, correct.

3. aspInfo.outdated dependency arrays ✅

Vtxos.tsx:59, Notes/Form.tsx:34, Notes/Redeem.tsx:33, Unavailable.tsx:27 — Adding aspInfo.outdated to useEffect deps ensures the error message re-evaluates when the outdated flag flips, not just when unreachable changes. Without this, a transition from generic-unreachable to outdated (or vice versa) while unreachable stays true would leave stale text. Correct fix.

Send/Form.tsx:462-465 — The error-clearing logic now also clears the outdated-client message when the server becomes reachable. The approach of synthesizing aspErrorText({ ...aspInfo, outdated: true }, errTxt) to get the outdated string is slightly indirect but correct and forward-compatible with any future aspErrorText changes.

No protocol-critical changes. All modifications are UI error display and cleanup.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — new commit 4ef0a6d1

Three changes, all clean:

1. Orphaned vendor script cleanup ✅

Removes scripts/prepare-arkade-sdk.js, the prepare:sdk script entry, .gitignore entries for .arkade-sdk-src/ and vendor/, and the vitest exclude patterns. All of these became dead code after 54916bdf switched to published SDK 0.4.35. No issues.

2. Chip keyboard a11y ✅

Contracts.tsx:150-158 — Adds role='button', tabIndex={0}, and Enter/Space onKeyDown handler. Standard pattern, correct.

3. aspInfo.outdated dependency arrays ✅

Vtxos.tsx:59, Notes/Form.tsx:34, Notes/Redeem.tsx:33, Unavailable.tsx:27 — Adding aspInfo.outdated to useEffect deps ensures the error message re-evaluates when the outdated flag flips, not just when unreachable changes. Without this, a transition from generic-unreachable to outdated (or vice versa) while unreachable stays true would leave stale text. Correct fix.

Send/Form.tsx:462-465 — The error-clearing logic now also clears the outdated-client message when the server becomes reachable. The approach of synthesizing aspErrorText({ ...aspInfo, outdated: true }, errTxt) to get the outdated string is slightly indirect but correct and forward-compatible with any future aspErrorText changes.

No protocol-critical changes. All modifications are UI error display and cleanup.

🤖 Reviewed by Arkana

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/screens/Wallet/Unavailable.tsx (1)

17-28: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent stale async capability checks from overwriting ASP outage/outdated errors.

detectJSCapabilities() can resolve after aspInfo.unreachable/outdated flips, and then setError(...) in the old promise chain can replace the newer server/outdated message. Please guard the async path with effect cleanup cancellation.

Suggested fix
   useEffect(() => {
+    let cancelled = false
+
     if (aspInfo.unreachable) return setError(aspErrorText(aspInfo, 'Arkade server unreachable.'))
     detectJSCapabilities()
       .then((result) => {
+        if (cancelled) return
         if (result.isSupported) return
         // Use specific error message or fallback to iOS/generic message
         setError(result.errorMessage || getRestrictedEnvironmentMessage(isIOS()))
       })
       .catch(() => {
+        if (cancelled) return
         setError(getRestrictedEnvironmentMessage(isIOS()))
       })
+    return () => {
+      cancelled = true
+    }
   }, [aspInfo.unreachable, aspInfo.outdated])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/Wallet/Unavailable.tsx` around lines 17 - 28, The useEffect hook
in the Unavailable component has a race condition where the async
detectJSCapabilities() promise can resolve after aspInfo.unreachable or
aspInfo.outdated values change, causing the stale promise to overwrite the newer
server error message with setError(). To fix this, add a cleanup mechanism using
an AbortController or a mounted flag. Before calling setError() in the .then()
and .catch() handlers of detectJSCapabilities(), check if the flag is still
valid (or if the signal is not aborted), and only proceed if the effect is still
active. In the cleanup function returned from useEffect, either abort the
controller or set the flag to false to prevent stale callbacks from executing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/screens/Wallet/Unavailable.tsx`:
- Around line 17-28: The useEffect hook in the Unavailable component has a race
condition where the async detectJSCapabilities() promise can resolve after
aspInfo.unreachable or aspInfo.outdated values change, causing the stale promise
to overwrite the newer server error message with setError(). To fix this, add a
cleanup mechanism using an AbortController or a mounted flag. Before calling
setError() in the .then() and .catch() handlers of detectJSCapabilities(), check
if the flag is still valid (or if the signal is not aborted), and only proceed
if the effect is still active. In the cleanup function returned from useEffect,
either abort the controller or set the flag to false to prevent stale callbacks
from executing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f746e372-7cb1-4736-8199-c04f3fc9f972

📥 Commits

Reviewing files that changed from the base of the PR and between 2e4478c and 4ef0a6d.

📒 Files selected for processing (7)
  • package.json
  • src/screens/Settings/Contracts.tsx
  • src/screens/Settings/Vtxos.tsx
  • src/screens/Wallet/Notes/Form.tsx
  • src/screens/Wallet/Notes/Redeem.tsx
  • src/screens/Wallet/Send/Form.tsx
  • src/screens/Wallet/Unavailable.tsx
💤 Files with no reviewable changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/screens/Wallet/Notes/Redeem.tsx
  • src/screens/Settings/Vtxos.tsx
  • src/screens/Wallet/Notes/Form.tsx
  • src/screens/Wallet/Send/Form.tsx
  • src/screens/Settings/Contracts.tsx

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — merge commit 8f3c9e8d

Scope: Merge of origin/master into feat/signer-rotation-sdk-pr554, bringing in two already-merged PRs:

  • 89e75ac7 — Branta SDK upgrade 0.0.9 → 3.1.3 (#673)
  • cd5931eb — LNURL fixed-amount field population fix (#671)

Findings: None. The merge is clean:

  • No conflict markers in the combined Send/Form.tsx
  • The aspErrorText integration from 4ef0a6d1 (this PR) is intact alongside both merged changes
  • LNURL fix correctly uses setState() (not setAmount) so the amount input reflects fixed-amount LNURLs — consistent with this PR's setState pattern
  • Branta v3.1.3 API (BrantaService, Payment, PrivacyMode.Strict, getPaymentsByQrCode/getPayments) is properly wired with theme-aware logo selection and the verifyUrl anchor
  • Test coverage for the LNURL regression is included
  • package.json and lockfile are consistent

No protocol-critical code touched in this merge. Approval stands.

🤖 Reviewed by Arkana

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Arkana Follow-up Review — merge commit 8f3c9e8d

Scope: Merge of origin/master into feat/signer-rotation-sdk-pr554, bringing in two already-merged PRs:

  • 89e75ac7 — Branta SDK upgrade 0.0.9 → 3.1.3 (#673)
  • cd5931eb — LNURL fixed-amount field population fix (#671)

Findings: None. The merge is clean:

  • No conflict markers in the combined Send/Form.tsx
  • The aspErrorText integration from 4ef0a6d1 (this PR) is intact alongside both merged changes
  • LNURL fix correctly uses setState() (not setAmount) so the amount input reflects fixed-amount LNURLs — consistent with this PR's setState pattern
  • Branta v3.1.3 API (BrantaService, Payment, PrivacyMode.Strict, getPaymentsByQrCode/getPayments) is properly wired with theme-aware logo selection and the verifyUrl anchor
  • Test coverage for the LNURL regression is included
  • package.json and lockfile are consistent

No protocol-critical code touched in this merge. Approval stands.

🤖 Reviewed by Arkana

@pietro909
pietro909 requested a review from bordalix June 15, 2026 14:44
@Kukks
Kukks merged commit 036bfc1 into master Jun 15, 2026
6 checks passed
@pietro909
pietro909 deleted the feat/signer-rotation-sdk-pr554 branch June 15, 2026 14:53
@coderabbitai coderabbitai Bot mentioned this pull request Jun 18, 2026
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