Skip to content

fix: phase-one wallet hardening integration - #55

Merged
h33p merged 26 commits into
mainfrom
codex/phase1-fee-sidepanel-integration
Oct 7, 2026
Merged

h33p merged 26 commits into
mainfrom
codex/phase1-fee-sidepanel-integration

Conversation

@thomas-kroes

@thomas-kroes thomas-kroes commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Keep the side panel as the default when no display preference exists, including upgrades from versions that did not store the key; preserve every explicit preference afterward.
  • Default a missing auto-lock preference to 10 minutes while preserving an explicitly stored 0 as Never.
  • Add the read-only nock_buildSimpleTransaction primitive. It returns the exact unsigned V1 transaction, exact selected native input notes, projected outputs, actual and minimum fee metadata, and a witness-independent intent ID. Public builds do not reserve notes or write history.
  • Build provider SEND_TRANSACTION requests once before approval. The approval screen, signer, and broadcaster now consume that exact transaction rather than estimating and rebuilding later.
  • Bind approval to the witnessless spends intent rather than the unsigned raw transaction ID, which legitimately changes when witness signatures are attached.
  • Report the actual fee committed by WASM (curFee), use calcFee only as the minimum, and reject explicit fees below that minimum.
  • Revalidate origin, account, lifecycle generation, transaction context, network provenance, and exact input availability before signing/submission.
  • Reserve exact inputs and create wallet history in one durable snapshot. Persist a signed transaction ID and broadcast_pending before submission; retain reservations when post-attempt network errors make submission status ambiguous.
  • Serialize encrypted account writes, wallet lifecycle transitions, approved-origin writes, and pending-approval session snapshots so lock/reset/restart races cannot resurrect stale state.
  • Bind cached UTXOs to the normalized RPC endpoint, advance synced height even when no notes change, fail closed while sync provenance is missing/incomplete, and refuse cross-network reinterpretation of ambiguous pending transactions.
  • Apply the same lifecycle/provenance/submission invariants to popup sends, bridge sends, and raw-transaction signing.

Review structure

This is the phase-one release integration artifact and supersedes merging #51 and #52 independently. The large diff is organized conceptually into these review layers:

  1. Exact unsigned transaction build and fee contract.
  2. Provider build → approval → sign → broadcast binding.
  3. Ordered persistence and wallet lifecycle foundations.
  4. UTXO network provenance, migration, and post-unlock bootstrap.
  5. Adaptation of popup send, bridge send, and raw-sign consumers.

Keep this PR unmerged until the planned human review and cross-repository release ordering are complete.

Compatibility and UX

  • Existing sendTransaction({ to, amount, fee? }) request shapes remain supported; omitted fees are calculated by the wallet and explicit fees remain exact.
  • Public unsigned builds work for external/watch-only accounts; local send still correctly rejects accounts without local signing material.
  • A public build is an unreserved point-in-time snapshot. If its inputs become stale, signing/sending fails and the dApp must rebuild; the wallet never silently substitutes different notes.
  • Existing display-mode and auto-lock choices are preserved. Never remains available and persists only when explicitly selected.
  • Existing vault migration, keyfile export/import, seed copying, and HTTP/HTTPS dApp transport behavior are not removed by this change.
  • While Iris still depends on SDK 0.2, the new provider method is defined locally. The SDK compatibility mapper passes unknown methods unchanged; the temporary definition should be removed after the final SDK 0.3 dependency bump.

Validation

  • npm test — 13 files / 67 tests
  • npm run typecheck
  • npm run format:check
  • npm run build
  • git diff --check
  • Real packaged-WASM regression: unsigned → raw → signed → validated → protobuf, with stable spends intent/inputs/fee and the expected signed-ID change.
  • Independent release-blocker audits found no remaining security, correctness, durability, or compatibility blocker in the current Iris/SDK changes.

Cross-repository release order

  1. Human-review and merge chore(release): prepare iris-wasm 0.2.1 iris-rs#29, then deliberately publish @nockbox/iris-wasm@0.2.1.
  2. Update Feat: build unsigned simple transactions iris-sdk#6 from exact WASM 0.2.0 to 0.2.1, regenerate the lockfile, rerun validation, merge, and publish SDK 0.3.
  3. Update this PR to SDK 0.3, remove the temporary local provider constant, rerun CI and funded/manual Chrome acceptance, then human-review and merge.

Manual acceptance checklist

  • Existing install without a display-mode key opens the side panel; switching to popup persists across worker/extension restarts.
  • Missing auto-lock preference defaults to 10 minutes; explicitly selecting Never persists.
  • Provider approval displays the exact recipient, amount, fee, and inputs that are subsequently signed/broadcast.
  • A stale public build fails with a rebuild-required error and does not leave note reservations or history behind.
  • Lock, reset, account switch, origin revocation, and RPC/config changes invalidate an open approval.
  • RPC endpoint changes require a successful UTXO resync before signing; renaming only the display label does not.
  • Pre-submission failures release owned reservations; ambiguous post-submission failures retain pending state for recovery.
  • Restart recovery releases provably unbroadcast exact/managed created records while preserving broadcast_pending submissions.
  • Raw signing succeeds when only witnesses change and rejects changed transaction intent or unverified inputs.
  • Existing vault upgrade, keyfile export/import, seed copy, and HTTP/HTTPS dApp flows remain functional.

thomas-kroes and others added 23 commits June 10, 2026 15:03
Let users choose between the toolbar pop-up and Chrome side panel via
Settings > Display mode. Side panel reuses the same React app with fluid
layout; dApp approvals route to the panel with popup-window fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
When display mode is side panel, always notify the panel and never fall
back to approval popup windows if sidePanel.open() fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
Covers the case where an approval arrived while the panel was hidden or
the runtime message was missed before the panel remounted.

Co-authored-by: Cursor <cursoragent@cursor.com>
Open the side panel on the user-gesture chain before provider requests
that may need approval. Persist connect/sign/tx pending state to
chrome.storage.session so the side panel can restore after SW restarts.
Also restyle the display mode settings icon to match other settings icons.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use the same solid/hollow evenodd split as the theme icon — a window
frame with a cut-out side panel — instead of two thin filled blocks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep fluid w-full h-full layout on SendReviewScreen from the side panel work.

Co-authored-by: Cursor <cursoragent@cursor.com>
Exposes the wallet's existing internal fee estimation
(wallet:estimateTransactionFee -> vault.estimateTransactionFee, WASM
auto-calc) as a public dApp-callable provider method, so dApps can show
the required fee without duplicating transaction-building logic.

- Register nock_estimateTransactionFee in PROVIDER_METHODS (defined
  locally until @nockbox/iris-sdk >= 0.3.0 is published) and
  isProviderMethod()
- New background handler: gated on approved origin + unlocked vault
  (read-only, no approval popup, same gating as nock_getWalletInfo);
  validates params and returns { fee } in canonical nicks (string)
…mation

Previously, omitting fee in nock_sendTransaction failed with 'Missing
fee', forcing dApps to ask users for a fee or duplicate wallet/WASM
transaction-building logic.

Now, when a dApp omits fee:
- The background estimates it via vault.estimateTransactionFee before
  showing the approval popup (estimation failure rejects the request
  up front with -32603, no dangling pending request)
- The approval popup labels the fee as '(estimated)'
- On approval, undefined is passed to vault.sendTransactionV2 so WASM
  auto-calculates the exact fee at build time, eliminating the
  'Insufficient fee' late-failure for this path
- The dApp response reports the actual fee used (walletTx.fee)

Explicitly-provided fees keep the existing behavior verbatim: parsed
with allowZero, respected as-is, and echoed back in the response.
For omitted-fee nock_sendTransaction, approval now passes the wallet's
fresh estimate as the fee (matching the popup send flow) instead of
undefined. The undefined branch in sendTransactionV2 reserves a
2-NOCK placeholder for note selection, so an account that could afford
amount + estimate but not amount + 2 NOCK failed at approval with
'Insufficient available funds'. Passing the concrete estimate sizes
note selection to the fee actually shown and charged.

Also drops the '~' prefix on the popup fee since the displayed value is
now applied verbatim rather than approximated.

Addresses review feedback (P2) on PR #52.
Fixes the failing typecheck-and-format CI check.

Co-authored-by: Cursor <cursoragent@cursor.com>
chrome.sidePanel and chrome.tabs are unavailable in content scripts, so
the previous content-script open call never ran. Open the panel
synchronously in the onMessage listener instead, where Chrome preserves
the dApp click gesture (any await consumes it), using an in-memory
display mode cache.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace the 15 numbered wallet icons with a new 29-icon pack: 5 kept
  classics (styles 1, 2, 3, 8, 14 - default unchanged) plus 24 new
  iris-themed icons, normalised to the shared tintable-SVG convention
  (var(--fill-0) placeholder, container-sized root).
- Add shared/walletStyles.ts as the single source of truth: icon
  registry with visual families (picker groups similar icons together),
  a 17-colour palette (7 existing + 10 new in the same poppy scheme),
  and a deterministic assignment sequence that round-robins across
  icon families and strides the colour rainbow so consecutive wallets
  always look distinct; combinations only repeat after all 493 are used.
- Switch persisted iconStyleId to stable string ids with transparent
  migration of legacy numeric ids (retired styles fall back to default).
- Drive AccountIcon and WalletStylingScreen from the shared registry.

https://claude.ai/code/session_01EPxCTHkLN8QM5iuNR5qqrn
feat: replace wallet icon pack and rework style assignment logic
@thomas-kroes
thomas-kroes marked this pull request as ready for review July 31, 2026 12:00
@thomas-kroes

Copy link
Copy Markdown
Contributor Author

@greptile-apps

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

This PR integrates wallet hardening and fee-estimation behavior across the extension.

  • Defaults missing display-mode preferences to the side panel and missing auto-lock preferences to ten minutes while preserving explicit settings.
  • Adds restart-safe approval snapshots, requester/account validation, and side-panel approval routing.
  • Adds advisory fee estimation, one-time underfunded-build retry, and reconciliation against the transaction actually built.
  • Verifies raw-transaction approvals with stable witnessless transaction-intent hashes.
  • Expands CI and automated coverage for approval restoration, side-panel behavior, RPC methods, and fee reconciliation.

Confidence Score: 5/5

The PR appears safe to merge from the reviewed code, with no concrete blocking or independently actionable non-blocking defect identified.

The changed approval, transaction-building, reservation-cleanup, origin-validation, and settings-migration paths retain their required guards and reconcile persisted state with the transaction actually built.

Important Files Changed

Filename Overview
extension/background/index.ts Integrates display-mode routing, approval persistence and validation, advisory-fee handling, and hardened raw-transaction approval.
extension/shared/vault.ts Adds transaction-intent verification and reconciles reservations, fees, inputs, change, and history with the transaction produced by WASM.
extension/shared/transaction-fee.ts Introduces bounded advisory-fee retry and strict reconciliation helpers for built transaction inputs and amounts.
extension/shared/pending-approval-state.ts Adds serializable pending-approval snapshots with filtering, expiration handling, and queue promotion.
extension/content/index.ts Hardens the page-to-extension message envelope and supplies authoritative extension-side display context.
extension/popup/hooks/useApprovalDetection.ts Updates approval discovery and routing to support both popup and side-panel surfaces.
extension/shared/constants.ts Defines display-mode, auto-lock, runtime-message, and approval-related constants used by the integrated hardening flow.
package.json Adds the Vitest-based test command and development dependency.
.github/workflows/ci.yml Runs the test suite as part of the required CI workflow.

Sequence Diagram

sequenceDiagram
    participant D as dApp
    participant B as Background worker
    participant U as Approval UI
    participant V as Vault/WASM
    participant N as Network
    D->>B: Provider transaction request
    B->>V: Estimate advisory fee when omitted
    B->>U: Display approval request
    U->>B: Approve
    B->>B: Revalidate origin, account, and requester
    B->>V: Reserve notes and build transaction
    alt Advisory fee is underfunded
        B->>V: Reserve remaining available notes
        B->>V: Retry build once
    end
    V-->>B: Actual inputs, fee, and change
    B->>N: Broadcast built transaction
    B->>V: Reconcile reservations and history
    B-->>D: Transaction result
Loading

Reviews (1): Last reviewed commit: "fix: harden advisory fees and raw signin..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9a62b42ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extension/background/index.ts Outdated
method === PROVIDER_METHODS.SEND_TRANSACTION ||
method === PROVIDER_METHODS.GET_WALLET_INFO ||
method === PROVIDER_METHODS.SIGN_TX ||
method === PROVIDER_METHODS.ESTIMATE_TRANSACTION_FEE ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bypass SDK mapping for the locally added fee method

When a dApp calls nock_estimateTransactionFee without an api field—the supported default-provider request shape—resolveSourceApiVersion treats it as API 0 and this classification sends it through mapRpcRequest from @nockbox/iris-sdk 0.2.0. That SDK predates this locally added method, so the request is rejected before reaching the new switch handler; the outgoing bridge repeats the same unsupported mapping. Special-case this method as a pass-through, or provide explicit request/response mappings until the SDK is upgraded.

Useful? React with 👍 / 👎.

@h33p
h33p merged commit 3ff1835 into main Oct 7, 2026
1 check failed
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.

3 participants