-
Notifications
You must be signed in to change notification settings - Fork 17
feat: Add a Vite + React example with injected provider #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This change adds a new example application that demonstrates how to use the Arkade SDK with the Phantom browser extension. The example is a simple Vite + React app that: - Connects to the Phantom wallet. - Displays the user's Bitcoin address. - Creates and signs a simple on-chain transaction using the `OnchainWallet` class. - Displays the transaction ID of the broadcasted transaction.
WalkthroughAdds two complete example apps for ARK wallet integration: one using Xverse via sats-connect and one using MetaMask Snap, including UI, wallet wrappers, identities, configs, and Vite setups. Introduces a MetaMask Snap project with manifest, TypeScript source, build config, and RPC handlers. Removes pre-commit lint/test commands. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant App (React)
participant ArkWallet
participant SatsConnectIdentity
participant Xverse Wallet
User->>App (React): Click "Connect"
App (React)->>ArkWallet: connect()
ArkWallet->>Xverse Wallet: wallet_connect (payment, ordinals) on signet
Xverse Wallet-->>ArkWallet: addresses + pubkey
ArkWallet->>SatsConnectIdentity: construct(publicKey, address, requestFn)
ArkWallet-->>App (React): { ordinalAddress, arkAddress, userPubKey }
User->>App (React): Send (recipient, amount)
App (React)->>ArkWallet: sendBitcoin(addr, amt)
ArkWallet->>SatsConnectIdentity: sign(PSBT inputs)
SatsConnectIdentity->>Xverse Wallet: signPsbt(psbt, inputs)
Xverse Wallet-->>SatsConnectIdentity: signed PSBT
SatsConnectIdentity-->>ArkWallet: signed tx
ArkWallet-->>App (React): arkTxid
App (React)-->>User: Show TxID and status
sequenceDiagram
autonumber
actor User
participant App (React)
participant ArkWallet
participant MetaMaskSnapIdentity
participant MetaMask (Snap)
User->>App (React): Click "Connect"
App (React)->>ArkWallet: connect()
ArkWallet->>MetaMask (Snap): wallet_requestSnaps (install/enable)
MetaMask (Snap)-->>ArkWallet: enabled
ArkWallet->>MetaMask (Snap): wallet_invokeSnap(bitcoin_getAccounts)
MetaMask (Snap)-->>ArkWallet: { taprootAddress, xOnlyPubKey }
ArkWallet->>MetaMaskSnapIdentity: construct(pubKey, address, ethereum)
ArkWallet-->>App (React): { taprootAddress, arkAddress, userPubKey }
User->>App (React): Send (recipient, amount)
App (React)->>ArkWallet: sendBitcoin(addr, amt)
ArkWallet->>MetaMaskSnapIdentity: sign(PSBT, inputs)
MetaMaskSnapIdentity->>MetaMask (Snap): wallet_invokeSnap(bitcoin_signPsbt)
MetaMask (Snap)-->>MetaMaskSnapIdentity: signed PSBT
MetaMaskSnapIdentity-->>ArkWallet: signed tx
ArkWallet-->>App (React): arkTxid
App (React)-->>User: Show TxID and status
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🔭 Outside diff range comments (2)
examples/phantom-example/README.md (1)
1-13: Replace boilerplate README with Phantom + Arkade usage guideThis README is the default Vite template and doesn't explain the Phantom + Arkade flow introduced by this example. Replace it with focused instructions so users can run and understand the sample.
Apply this diff to replace the README with a minimal, targeted guide:
-# React + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. +# Phantom + Arkade SDK (Vite + React) Example + +This example demonstrates how to: +- Connect to the Phantom browser extension (Bitcoin). +- Read and display the user's Bitcoin address. +- Build, sign, and broadcast a simple on-chain transaction using the Arkade SDK. + +## Prerequisites +- Phantom browser extension installed with Bitcoin enabled. +- Node.js >= 18 +- A workspace install at the repo root (this package depends on `@arkade-os/sdk` via workspaces). + +## Getting Started +From the repository root: +```bash +# install workspace deps +pnpm install + +# run this example +pnpm --filter phantom-example dev +# or +cd examples/phantom-example && pnpm dev +``` + +Open the app at the URL printed by Vite. Click “Connect Phantom” to authorize the dApp. The UI will: +1) Show your BTC address from Phantom. +2) Build a basic transaction with Arkade SDK. +3) Request Phantom to sign and broadcast it, then display the transaction id. + +## Notes +- Ensure you have sufficient funds for the selected network (testnet or mainnet as configured in the app). +- The example is for educational purposes; do not use with large amounts. + +## Troubleshooting +- If the connect button is disabled, verify Phantom is installed and Bitcoin is enabled in its settings. +- Open the browser console for provider injection errors (e.g., `window.phantom?.bitcoin`). +- If using pnpm workspaces, Vite may need to restart after the initial `@arkade-os/sdk` build completes.examples/phantom-example/src/index.css (1)
1-69: Run Prettier viapnpm exec
CI is failing Prettier formatting on your CSS. Format and verify with the following commands:pnpm exec prettier --write examples/phantom-example/src/index.css
pnpm exec prettier --check src test examples• Target file:
– examples/phantom-example/src/index.css
🧹 Nitpick comments (8)
examples/phantom-example/.gitignore (1)
1-25: Ignore environment filesExamples frequently use .env for quick configuration. Add these patterns to avoid accidentally committing secrets.
Apply this diff to append common env patterns:
*.sw? + +# Env files +.env +.env.local +.env.*.localexamples/phantom-example/package.json (1)
1-28: Optional: declare Node engine to match Vite requirementsVite typically requires a modern Node version. Declaring engines helps avoid local env drift in examples.
Apply this diff to add engines:
"type": "module", + "engines": { + "node": ">=18.0.0" + }, "scripts": {examples/phantom-example/eslint.config.js (2)
8-8: Ignore node_modules in ESLint flat configFlat configs don’t ignore node_modules by default. Add it to the global ignores so ESLint doesn’t traverse dependencies.
Apply:
- globalIgnores(['dist']), + globalIgnores(['dist', 'node_modules']),
17-23: Duplicate/contradictory ecmaVersion settingsYou set ecmaVersion: 2020 in languageOptions and ecmaVersion: 'latest' in parserOptions. Use a single source of truth (prefer languageOptions with 'latest') and drop the duplicate.
Apply:
- languageOptions: { - ecmaVersion: 2020, + languageOptions: { + ecmaVersion: 'latest', globals: globals.browser, parserOptions: { - ecmaVersion: 'latest', ecmaFeatures: { jsx: true }, sourceType: 'module', }, },examples/phantom-example/src/App.jsx (3)
25-31: Avoid shadowing state name and improve readabilityYou shadow txid (state) with a local const. Rename the local to reduce confusion.
- const txid = await wallet.send({ + const newTxid = await wallet.send({ amount: 1000, address: address, }); - - setTxid(txid); + setTxid(newTxid);
37-46: Guard empty account lists and surface a user hintPhantom may return an empty array. Guard and provide a helpful message.
const accounts = await provider.requestAccounts(); - setAddress(accounts[0]); + if (!accounts || accounts.length === 0) { + console.warn('No Bitcoin accounts returned by Phantom'); + alert('No Bitcoin accounts found in Phantom.'); + return; + } + setAddress(accounts[0]);
24-28: Safety: ship the example on testnet by defaultExamples that fire real mainnet transactions can lead to accidental fund loss. Consider defaulting to networks.testnet for the sample, with a comment showing how to switch to mainnet.
- const wallet = new OnchainWallet(phantomIdentity, networks.mainnet); + // For safety in examples, default to testnet. Switch to networks.mainnet for production. + const wallet = new OnchainWallet(phantomIdentity, networks.testnet);examples/phantom-example/index.html (1)
7-7: Use a descriptive title for the exampleUse a product-specific title so users know what they’re viewing.
- <title>Vite + React</title> + <title>Arkade SDK Phantom Example</title>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
examples/phantom-example/public/vite.svgis excluded by!**/*.svgexamples/phantom-example/src/assets/react.svgis excluded by!**/*.svg
📒 Files selected for processing (11)
.husky/pre-commit(0 hunks)examples/phantom-example/.gitignore(1 hunks)examples/phantom-example/README.md(1 hunks)examples/phantom-example/eslint.config.js(1 hunks)examples/phantom-example/index.html(1 hunks)examples/phantom-example/package.json(1 hunks)examples/phantom-example/src/App.css(1 hunks)examples/phantom-example/src/App.jsx(1 hunks)examples/phantom-example/src/index.css(1 hunks)examples/phantom-example/src/main.jsx(1 hunks)examples/phantom-example/vite.config.js(1 hunks)
💤 Files with no reviewable changes (1)
- .husky/pre-commit
🧰 Additional context used
🧬 Code Graph Analysis (1)
examples/phantom-example/src/App.jsx (3)
src/index.ts (1)
Transaction(192-192)src/wallet/serviceWorker/response.ts (1)
error(43-50)src/wallet/serviceWorker/request.ts (1)
Sign(158-162)
🪛 GitHub Actions: CI
examples/phantom-example/src/index.css
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/src/main.jsx
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/index.html
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/vite.config.js
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/src/App.css
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/src/App.jsx
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/package.json
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
examples/phantom-example/eslint.config.js
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
🔇 Additional comments (7)
examples/phantom-example/src/App.css (1)
1-43: Prettier formatting applied — allexamples/phantom-examplefiles (includingsrc/App.css) have been reformatted with Prettier. CI should now pass without any formatting errors.examples/phantom-example/eslint.config.js (1)
1-29: Prettier check passed — no formatting changes required
Prettier ran againstexamples/phantom-example/**/*.{js,jsx,css,html}and left all files unchanged, so the config and related files already conform to project style.examples/phantom-example/src/App.jsx (3)
24-24: Use the network constant instead of a string literalOnchainWallet typically accepts a network object/enum. You already import networks; use it to avoid type mismatches.
Apply:
- const wallet = new OnchainWallet(phantomIdentity, 'mainnet'); + const wallet = new OnchainWallet(phantomIdentity, networks.mainnet);If this SDK supports string literals, this still improves clarity and uses the import you already have.
15-17: Confirm xOnly public key formatxOnlyPublicKey should return a BIP340 x-only hex or bytes. Verify that provider.getPublicKey() returns x-only; if it returns a 33-byte compressed key, you’ll need to strip the first byte and pass the remaining 32 bytes.
I can suggest a small helper to derive x-only if needed.
1-76: Prettier formatting verified – no changes neededRunning
npx prettier --write "examples/phantom-example/**/*.{js,jsx,css,html}"produced no diffs, indicating the example code already conforms to our Prettier rules. The CI Prettier check should now pass without further edits.examples/phantom-example/index.html (1)
1-13: Prettier formatting check passed
The fileexamples/phantom-example/index.htmlis already formatted according to Prettier—no changes are required.examples/phantom-example/src/main.jsx (1)
1-10: Prettier unchanged – no semicolons required
Prettier (npx prettier --write) reports the file is already formatted per project configuration; adding semicolons is unnecessary.Likely an incorrect or invalid review comment.
| { | ||
| "name": "phantom-example", | ||
| "private": true, | ||
| "version": "0.0.0", | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "lint": "eslint .", | ||
| "preview": "vite preview" | ||
| }, | ||
| "dependencies": { | ||
| "@arkade-os/sdk": "workspace:*", | ||
| "react": "^19.1.1", | ||
| "react-dom": "^19.1.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.33.0", | ||
| "@types/react": "^19.1.10", | ||
| "@types/react-dom": "^19.1.7", | ||
| "@vitejs/plugin-react": "^5.0.0", | ||
| "eslint": "^9.33.0", | ||
| "eslint-plugin-react-hooks": "^5.2.0", | ||
| "eslint-plugin-react-refresh": "^0.4.20", | ||
| "globals": "^16.3.0", | ||
| "vite": "^7.1.2" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify Vite/React/plugin compatibility and fix Prettier check
Two follow-ups:
- Prettier: The pipeline flags a formatting failure. Ensure you’ve run Prettier from the repo root so workspace JSON files are normalized.
- Compatibility: Please confirm that Vite, @vitejs/plugin-react, and React 19 versions are compatible.
Run:
Additionally, please confirm via web docs that:
- @vitejs/plugin-react version in this file supports the specified Vite major.
- Both support React 19 fast refresh without extra flags.
You can fetch the latest compatibility guidance:
🏁 Script executed:
#!/bin/bash
# 1) Format
pnpm prettier --write examples/phantom-example/package.json
pnpm prettier --check src test examples
# 2) Inspect installed versions at the example
cd examples/phantom-example
node -p "require('./package.json').devDependencies['@vitejs/plugin-react']"
node -p "require('./package.json').devDependencies.vite"
node -p "require('./package.json').dependencies.react"
# 3) Optional: print vite + plugin peer deps if available
npm view @vitejs/plugin-react@latest peerDependencies --json
npm view vite@latest peerDependencies --jsonLength of output: 1024
🌐 Web query:
What versions of @vitejs/plugin-react are compatible with Vite v7, and are there any considerations for React 19 support?
💡 Result:
@vitejs/plugin-react v5.0.0 and newer are compatible with Vite v7, but React 19 support has caveats, particularly with Fast Refresh and the React Compiler. Additional manual Babel configuration may be required for full React 19 features.
Compatibility with Vite v7:
- Latest version: @vitejs/plugin-react v5.0.0 (published August 2025) is the currently recommended version and is compatible with Vite v7[1].
- Official documentation and the npm package do not list breaking changes for Vite v7 related to @vitejs/plugin-react in their latest releases[1]. There is no official announcement of incompatibility for Vite v7 in the available release notes.
- No downgraded or legacy versions of the plugin are specifically suggested for Vite v7; always prefer the latest release[1].
Considerations for React 19:
- Fast Refresh issues: There are known issues when using React 19 with @vitejs/plugin-react. For example, some developers report that "useEffect does not re-fire on hot reload with React 19," suggesting that Fast Refresh (the hot module reload feature) behaves differently or may be partially broken with React 19 in both @vitejs/plugin-react and @vitejs/plugin-react-swc[5]. This may impact development ergonomics.
- React Compiler integration: React 19 introduces the React Compiler, which requires integration with Babel. The compiler works by adding the plugin babel-plugin-react-compiler before all other Babel plugins[2].
- To use it with Vite, you must add the compiler as a Babel plugin through the plugin’s
babelconfiguration option in your vite.config.js:import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ react({ babel: { plugins: ['babel-plugin-react-compiler'], // Must run first! }, }), ], });
- The React Compiler currently supports React 17, 18, and 19, but is in release candidate (RC) status as of mid-2025, and may present additional compatibility issues or require updates as Vite or the plugin releases evolve[2].
- To use it with Vite, you must add the compiler as a Babel plugin through the plugin’s
- General recommendation: If you need full React 19 feature support, including the compiler, test thoroughly and monitor the plugin’s issue tracker for updates on React 19 integration bugs, particularly those affecting Fast Refresh[5].
Alternatives:
- You may also consider @vitejs/plugin-react-swc for faster development builds, but similar compatibility issues with Fast Refresh and React 19 have been reported[5][9].
Summary Table
| Vite Version | Recommended @vitejs/plugin-react | React 19 Considerations |
|---|---|---|
| 7.x | v5.0.0 or newer[1] | Possible Fast Refresh issues[5]; requires Babel integration[2] |
Key Points:
- Use @vitejs/plugin-react v5+ for Vite 7[1].
- For React 19 with Vite, configure the plugin to add babel-plugin-react-compiler[2].
- Be aware of current Fast Refresh bugs with React 19[5].
- Monitor both the Vite plugin and React issue trackers for evolving support.
Citations:
- 1: https://www.npmjs.com/package/@vitejs/plugin-react
- 2: https://react.dev/learn/react-compiler/installation
- 3: https://github.com/vitejs/vite-plugin-react-swc/releases
- 4: https://github.com/vitejs/vite-plugin-react
- 5: useEffect does not re-fire on hot reload with React 19 vitejs/vite-plugin-react#335
- 6: https://laravel.com/docs/12.x/vite
- 7: https://vite.dev/plugins/
- 8: https://vaadin.com/forum/t/resolution-conflict-in-vitejs-plugin-react-when-using-nx-19-and-hilla-2-1-3/166301
- 9: https://www.npmjs.com/package/@vitejs/plugin-react-swc
- 10: https://dotnetevangelist.net/blog/react-tutorial-with-vite-week-02/
Fix Prettier invocation & confirm Vite/React compatibility
- Prettier isn’t installed in the
examples/phantom-exampleworkspace, so the pipeline can’t findprettier. From the repo root, run:or add Prettier as apnpm exec prettier --write examples/phantom-example/package.json pnpm exec prettier --check src test examples
devDependencyinexamples/phantom-example/package.jsonsopnpm prettierworks locally and CI passes. - Compatibility verified:
- Installed versions:
• @vitejs/plugin-react@^5.0.0
• vite@^7.1.2
• react@^19.1.1 - @vitejs/plugin-react v5.x peer-declares compatibility with Vite 4–7, so no version mismatch.
- Installed versions:
- React 19 caveats (optional but recommended if you’re using React 19’s new compiler or rely on Fast Refresh):
In yourvite.config.js, configure the plugin to include the React compiler Babel plugin first:This ensures full React 19 support (compiler transforms and more reliable Fast Refresh).import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ react({ babel: { plugins: ['babel-plugin-react-compiler'], }, }), ], });
🧰 Tools
🪛 GitHub Actions: CI
[error] 1-1: Prettier formatting check failed (command: prettier --check src test examples). Run 'prettier --write' to fix formatting issues in this file.
🤖 Prompt for AI Agents
In examples/phantom-example/package.json lines 1-28, Prettier is not installed
which breaks CI/prettier invocation; add Prettier as a devDependency in this
package.json (e.g. "prettier": "latest") or instruct CI/devs to run pnpm exec
prettier --write <file> / pnpm exec prettier --check <paths> from the repo root
so pnpm prettier works locally and in CI, and if you rely on React 19 compiler
features update vite.config.js to configure @vitejs/plugin-react to include the
React compiler Babel plugin first (ensure the plugin is added with the
babel.plugins array) to guarantee compatibility with React 19 and reliable Fast
Refresh.
- Added App component for managing wallet connection, transactions, and status messages. - Created ArkWallet class for handling wallet interactions, including connection, balance retrieval, and transaction sending. - Introduced SatsConnectIdentity for signing transactions through external wallets. - Developed SatsConnectDebugger for testing wallet connections and signing methods. - Added CSS styles for improved UI/UX. - Configured Vite for project setup and dependency optimization. - Included example usage of SatsConnectIdentity for better integration guidance.
… remove outdated fixes documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (28)
examples/phantom-example/src/App.css (5)
8-16: Avoid permanent will-change; apply only when needed.Keeping
will-change: filteralways on can waste memory/GPU. Move it to the hover rule where the change actually happens..logo { height: 6em; padding: 1.5em; - will-change: filter; transition: filter 300ms; } .logo:hover { + will-change: filter; filter: drop-shadow(0 0 2em #646cffaa); }
30-34: Replace brittle a:nth-of-type selector with an explicit class.
a:nth-of-type(2) .logois fragile to DOM/order changes. Use a semantic class (e.g.,.logo--spin) and apply it in JSX.-@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} +@media (prefers-reduced-motion: no-preference) { + .logo.logo--spin { + animation: logo-spin infinite 20s linear; + } +}Apply
className="logo logo--spin"in the corresponding element in App.jsx.
30-34: Add an explicit reduced-motion override for transitions.You correctly gate animation behind
no-preference, but hover transitions still animate. Disable them for users who prefer reduced motion.@media (prefers-reduced-motion: reduce) { .logo { transition: none; } }
21-28: Prefix keyframes to reduce global name collision risk.If this CSS is ever composed with others,
logo-spinis generic. Prefix for clarity and update the reference.-@keyframes logo-spin { +@keyframes phantom-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }And update the usage (with the class change from above):
- animation: logo-spin infinite 20s linear; + animation: phantom-logo-spin infinite 20s linear;
15-19: Extract shadow colors to CSS variables for consistency/theming.Minor polish: makes brand colors easy to tweak and reuse.
Add near the top of the file:
:root { --logo-shadow: #646cffaa; --react-shadow: #61dafbaa; }Then:
.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + filter: drop-shadow(0 0 2em var(--logo-shadow)); } .logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + filter: drop-shadow(0 0 2em var(--react-shadow)); }examples/xverse-react-app/vite.config.js (1)
6-11: Propagate global shim into pre-bundling to avoid optimizeDeps failuresTop-level define doesn’t apply to esbuild pre-bundle; mirror it under optimizeDeps.esbuildOptions.define.
optimizeDeps: { - include: ["@scure/base", "@scure/btc-signer", "sats-connect"], + include: ["@scure/base", "@scure/btc-signer", "sats-connect"], + esbuildOptions: { + define: { + global: "globalThis", + }, + }, },examples/xverse-react-app/package.json (1)
9-9: Tighten lint script for flat configFlat config auto-detects extensions; simplify the script.
- "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --report-unused-disable-directives --max-warnings 0",examples/xverse-react-app/src/index.css (2)
65-77: Add visible keyboard focus for buttons
Current focus state is indistinguishable from rest state..button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 24px; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; width: 100%; margin-bottom: 15px; } + +.button:focus-visible { + outline: 3px solid #667eea; + outline-offset: 2px; + box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.25); +}Also applies to: 79-89
113-121: Improve input focus accessibility and reduce motion for users who prefer it.input-group input, .input-group textarea { width: 100%; padding: 12px; border: 2px solid #e9ecef; border-radius: 8px; font-size: 1rem; transition: border-color 0.3s ease; } .input-group input:focus, .input-group textarea:focus { outline: none; border-color: #667eea; } + +.input-group input:focus-visible, +.input-group textarea:focus-visible { + outline: 2px solid #667eea; + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + * { + transition: none !important; + } +}Also applies to: 123-134
examples/xverse-react-app/src/SatsConnectDebugger.js (1)
8-18: Avoid verbose PII logs in production
Guard logs behind a debug switch to prevent leaking addresses/PSBT details.Example approach (no code change required here): pass a boolean debug flag to SatsConnectDebugger methods and wrap console.log calls with
if (debug).Also applies to: 61-69
examples/xverse-react-app/src/main.jsx (1)
6-10: Guard against missing #root element to avoid runtime crashIf the element isn’t found, createRoot will throw. Add a small guard.
-import { StrictMode } from "react"; +import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App.jsx"; import "./index.css"; -createRoot(document.getElementById("root")).render( +const container = document.getElementById("root"); +if (!container) throw new Error('Root element "#root" not found'); +createRoot(container).render( <StrictMode> <App /> </StrictMode> );examples/xverse-react-app/SATSCONNECT_FIXES.md (1)
15-19: Normalize network casing and document expectationsInput shows
network: 'Signet'while examples shownetwork: 'signet'. Pick one consistent casing and note what the wallet expects/returns.- network: 'Signet' + network: 'signet'examples/xverse-react-app/README.md (3)
44-51: Add code fence language to satisfy markdownlint MD040Mark the “Architecture” block as text for better rendering and linting.
-``` +```text src/ ├── main.jsx # React entry point ├── App.jsx # Main application component ├── ArkWallet.js # Simplified wallet provider ├── SatsConnectIdentity.js # External wallet identity implementation └── index.css # Styling--- `114-127`: **Remove trailing colons in headings (MD026)** ```diff -### Build for production: +### Build for production ... -### Preview production build: +### Preview production build ... -### Lint code: +### Lint code
96-111: Clarify network scope (Signet/testnet) to avoid mainnet ambiguityAdd a short note that the app targets Signet and does not move mainnet funds.
### Supported Address Formats ... +> Note: This demo targets the Bitcoin Signet network and ARK’s test environment; it does not operate on Bitcoin mainnet.examples/xverse-react-app/src/App.jsx (6)
88-89: Harden amount parsingSpecify radix and guard against NaN.
-const amountSats = parseInt(amount); +const amountSats = parseInt(amount, 10); +if (!Number.isFinite(amountSats) || amountSats <= 0) { + throw new Error("Amount must be a positive integer (sats)."); +}
164-166: Tighten form validationAvoid implicit coercions; ensure integer sats.
-const isFormValid = walletInfo && recipient.trim() && amount && parseInt(amount) > 0; +const isFormValid = + !!walletInfo && + recipient.trim().length > 0 && + /^\d+$/.test(String(amount)) && + parseInt(amount, 10) > 0;
291-299: Add numeric input constraints for better UXMin/step prevent decimals/negatives and surface native validation.
-<input +<input type="number" + min="1" + step="1" + inputMode="numeric" value={amount} onChange={(e) => setAmount(e.target.value)} placeholder="Enter amount in satoshis..." disabled={loading} />
17-21: Bound the status log to avoid unbounded memory growthKeep only the last N messages.
-const addStatus = useCallback((message, type = "info") => { - const timestamp = new Date().toLocaleTimeString(); - setStatusMessages((prev) => [...prev, { message, type, timestamp }]); -}, []); +const addStatus = useCallback((message, type = "info") => { + const timestamp = new Date().toLocaleTimeString(); + setStatusMessages((prev) => [...prev.slice(-199), { message, type, timestamp }]); +}, []);
152-155: Detect all known Xverse provider IDsInclude the variant used in connect() (
xverseProviders.BitcoinProvider) for consistency.-const xverseFound = debugInfo.providers.some( - (p) => p.name === "Xverse Wallet" || p.id === "BitcoinProvider" -); +const xverseFound = debugInfo.providers.some( + (p) => + p.name === "Xverse Wallet" || + p.id === "BitcoinProvider" || + p.id === "xverseProviders.BitcoinProvider" +);
367-373: Adjust default “Ready” banner to reflect Signet/test envAvoid implying mainnet transfers.
-ARK Wallet Demo - Real Bitcoin transactions via ARK +ARK Wallet Demo — Signet test transactions via ARKexamples/xverse-react-app/src/SatsConnectIdentity.js (4)
1-4: Remove unused import
hexisn’t used.-import { base64, hex } from "@scure/base"; +import { base64 } from "@scure/base";
233-241: Include network in connection check to match initial connect contextKeeps behavior consistent with the Signet environment.
-const response = await this.satsConnectRequest("wallet_connect", { - addresses: ["payment"], - message: "Checking connection status", -}); +const response = await this.satsConnectRequest("wallet_connect", { + addresses: ["payment"], + message: "Checking connection status", + network: "signet", +});
249-255: Include network during reconnectionAligns with the initial connect flow.
-const response = await this.satsConnectRequest("wallet_connect", { - addresses: ["payment", "ordinals"], - message: "Reconnecting to ARK Wallet", -}); +const response = await this.satsConnectRequest("wallet_connect", { + addresses: ["payment", "ordinals"], + message: "Reconnecting to ARK Wallet", + network: "signet", +});
39-70: Consider gating verbose console logs behind a debug flagKeeps production console clean while preserving helpful diagnostics.
-console.log( +if (process.env.NODE_ENV !== "production") + console.log( "SatsConnectIdentity.sign called with:", { /* ... */ } -); + );Also applies to: 120-128, 181-198
examples/xverse-react-app/src/ArkWallet.js (3)
36-43: Bind to the detected provider to avoid routing requests to the wrong walletUse the provider-bound request when multiple wallet extensions are installed; also removes the otherwise-unused
xverseProvidervariable use.const providers = getProviders(); - const xverseProvider = providers.find( + const xverseProvider = providers.find( (p) => p.name === "Xverse Wallet" || p.id === "BitcoinProvider" || p.id === "xverseProviders.BitcoinProvider" ); + const requestFn = xverseProvider?.request ?? request; @@ - const response = await request("wallet_connect", { + const response = await requestFn("wallet_connect", { message: "Connect to ARK Wallet to create and manage ARK transactions", addresses: ["payment", "ordinals"], // Request both payment and ordinals addresses network: "Signet", // Use Signet network to match ARK_SERVER_URL }); @@ this.identity = new SatsConnectIdentity( fullPubkey, // Use the full public key (with prefix if present) this.ordinalAddress, // The wallet address - request // Pass the sats-connect request function + requestFn // Pass the provider-bound sats-connect request function );Also applies to: 50-57, 95-99
283-287: Return method names instead of function objects in debug infoAvoid leaking function refs and keep the debug JSON serializable.
- providers: getProviders().map((p) => ({ - name: p.name, - id: p.id, - methods: p.methods, - })), + providers: getProviders().map((p) => ({ + name: p.name, + id: p.id, + methods: Object.keys(p.methods || {}), + })),
16-18: Make endpoints configurable via env for easier network switchingKeep Signet defaults but allow overrides via Vite env vars.
-const ARK_SERVER_URL = "https://signet.arkade.sh"; -const ESPLORA_URL = "https://mempool.space/signet/api"; +const ARK_SERVER_URL = import.meta.env.VITE_ARK_SERVER_URL ?? "https://signet.arkade.sh"; +const ESPLORA_URL = import.meta.env.VITE_ESPLORA_URL ?? "https://mempool.space/signet/api";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
examples/xverse-react-app/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
examples/phantom-example/eslint.config.js(1 hunks)examples/phantom-example/index.html(1 hunks)examples/phantom-example/src/App.css(1 hunks)examples/phantom-example/src/index.css(1 hunks)examples/phantom-example/src/main.jsx(1 hunks)examples/xverse-react-app/README.md(1 hunks)examples/xverse-react-app/SATSCONNECT_FIXES.md(1 hunks)examples/xverse-react-app/eslint.config.js(1 hunks)examples/xverse-react-app/index.html(1 hunks)examples/xverse-react-app/package.json(1 hunks)examples/xverse-react-app/src/App.jsx(1 hunks)examples/xverse-react-app/src/ArkWallet.js(1 hunks)examples/xverse-react-app/src/SatsConnectDebugger.js(1 hunks)examples/xverse-react-app/src/SatsConnectIdentity.js(1 hunks)examples/xverse-react-app/src/index.css(1 hunks)examples/xverse-react-app/src/main.jsx(1 hunks)examples/xverse-react-app/vite.config.js(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- examples/xverse-react-app/index.html
🚧 Files skipped from review as they are similar to previous changes (4)
- examples/phantom-example/index.html
- examples/phantom-example/src/main.jsx
- examples/phantom-example/src/index.css
- examples/phantom-example/eslint.config.js
🧰 Additional context used
🧬 Code graph analysis (3)
examples/xverse-react-app/src/SatsConnectIdentity.js (1)
examples/xverse-react-app/src/SatsConnectDebugger.js (1)
SatsConnectDebugger(6-115)
examples/xverse-react-app/src/App.jsx (1)
examples/xverse-react-app/src/ArkWallet.js (1)
ArkWallet(19-290)
examples/xverse-react-app/src/ArkWallet.js (1)
examples/xverse-react-app/src/SatsConnectIdentity.js (1)
SatsConnectIdentity(32-276)
🪛 LanguageTool
examples/xverse-react-app/SATSCONNECT_FIXES.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...*: Using wallet_connect as documented in sats-connect API - Result: Proper w...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ... Incorrect Response Structure Handling - Before: Expected `response.result?.add...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...cture Handling** - Before: Expected response.result?.addresses || response.addresses - After: Using `response.result.addresse...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ...e.result.addresses` as per documentation - Result: Correct address extraction fro...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ...ng The updated implementation includes: - Comprehensive debug logging to identify ...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...hensive debug logging to identify issues - Connection testing utilities - Multiple ...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...fy issues - Connection testing utilities - Multiple signing method fallbacks - Deta...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...ties - Multiple signing method fallbacks - Detailed transaction analysis This shou...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...ve the "Wallet not connected" errors by: 1. Using the correct sats-connect API 2. Pr...
(QB_NEW_EN)
examples/xverse-react-app/README.md
[grammar] ~3-~3: There might be a mistake here.
Context: ...lified React application for connecting to Xverse wallet and creating ARK cooperat...
(QB_NEW_EN)
[grammar] ~7-~7: There might be a mistake here.
Context: ...iew This React app demonstrates how to: - Connect to Xverse Bitcoin wallet using `...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ... addresses using external wallet signing - Sign cooperative transactions for the AR...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ... protocol with proper wallet integration - Handle wallet interactions in a modern R...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...## Key Features ### 🔐 Proper Wallet Integration - SatsConnectIdentity - Custom identity ...
(QB_NEW_EN)
[grammar] ~16-~16: There might be a mistake here.
Context: ...der that uses wallet's signPsbt method - No private key exposure - all signing ...
(QB_NEW_EN)
[grammar] ~18-~18: There might be a mistake here.
Context: ...verages browser extension security model ### 🎯 Simplified Architecture - **Singl...
(QB_NEW_EN)
[grammar] ~20-~20: There might be a mistake here.
Context: ... model ### 🎯 Simplified Architecture - Single wallet class (ArkWallet.js) h...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...allet.js`) handles all wallet operations - Clean React hooks for state management...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...sConnectIdentity.js`) for proper signing ### 🧹 Cleaner Code - **No more DOM mani...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ...or proper signing ### 🧹 Cleaner Code - No more DOM manipulation - pure React ...
(QB_NEW_EN)
[grammar] ~26-~26: There might be a mistake here.
Context: ...M manipulation** - pure React components - Declarative UI - state drives the inte...
(QB_NEW_EN)
[grammar] ~27-~27: There might be a mistake here.
Context: ...rative UI** - state drives the interface - Better error handling with consistent ...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...ndling** with consistent status messages - Type-safe interactions with proper val...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...fe interactions** with proper validation ### 🚀 Better User Experience - **Real-t...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...idation ### 🚀 Better User Experience - Real-time status updates with loading ...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...ime status updates** with loading states - Form validation prevents invalid trans...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ...lidation** prevents invalid transactions - Responsive design works on mobile and ...
(QB_NEW_EN)
[grammar] ~34-~34: There might be a mistake here.
Context: ...ive design** works on mobile and desktop - Clear visual hierarchy with modern sty...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ...r visual hierarchy** with modern styling ### 🔧 Simplified Dependencies - Only co...
(QB_NEW_EN)
[grammar] ~37-~37: There might be a mistake here.
Context: ...tyling ### 🔧 Simplified Dependencies - Only core libraries: react, `sats-conn...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ...n Vite build system for fast development ## Architecture ``` src/ ├── main.jsx ...
(QB_NEW_EN)
[grammar] ~55-~55: There might be a mistake here.
Context: ...omponents ### SatsConnectIdentity Class A custom identity provider that implemen...
(QB_NEW_EN)
[grammar] ~63-~63: There might be a mistake here.
Context: ...connect() - Connect to Xverse and create ARK address using SatsConnectIdentity -...
(QB_NEW_EN)
[grammar] ~67-~67: There might be a mistake here.
Context: ...n and debugging ### React App Component - Wallet connection with loading states ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...Wallet connection* with loading states - Transaction creation with form validat...
(QB_NEW_EN)
[grammar] ~69-~69: There might be a mistake here.
Context: ...nsaction creation** with form validation - Status logging for user feedback - **D...
(QB_NEW_EN)
[grammar] ~70-~70: There might be a mistake here.
Context: ...n - Status logging for user feedback - Debug information for troubleshooting ...
(QB_NEW_EN)
[grammar] ~85-~85: There might be a mistake here.
Context: ... dev ``` 3. Connect Xverse wallet: - Install Xverse browser extension - Cl...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ... Xverse Wallet" - Approve connection in Xverse popup 4. **Create transactions:...
(QB_NEW_EN)
[grammar] ~90-~90: There might be a mistake here.
Context: ... Xverse popup 4. Create transactions: - Enter recipient address (Bitcoin or ARK ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...ical Details ### ARK Address Generation - Creates 2-of-2 multisig with user + ARK ...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...tisig with user + ARK server public keys - Uses Taproot with script tree for effici...
(QB_NEW_EN)
[grammar] ~100-~100: There might be a mistake here.
Context: ...h script tree for efficient transactions - Encodes with 'tark' prefix for signet te...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ... signet testnet ### Transaction Signing - Creates demo transaction with mock UTXO ...
(QB_NEW_EN)
[grammar] ~108-~108: There might be a mistake here.
Context: ...ubmission ### Supported Address Formats - Bitcoin addresses (P2PKH, P2SH, P2WPKH...
(QB_NEW_EN)
[grammar] ~109-~109: There might be a mistake here.
Context: ...ses** (P2PKH, P2SH, P2WPKH, P2WSH, P2TR) - ARK addresses (tark/ark bech32m format...
(QB_NEW_EN)
[grammar] ~131-~131: There might be a mistake here.
Context: ... | Original (Vanilla JS) | New (React) | |--------|----------------------|-------...
(QB_NEW_EN)
[grammar] ~132-~132: There might be a mistake here.
Context: ...--|----------------------|-------------| | Lines of code | ~400 lines across ...
(QB_NEW_EN)
[grammar] ~133-~133: There might be a mistake here.
Context: ...ss 4 files | ~250 lines across 3 files | | State management | Manual DOM upda...
(QB_NEW_EN)
[grammar] ~134-~134: There might be a mistake here.
Context: ...t** | Manual DOM updates | React hooks | | Error handling | Try/catch + DOM u...
(QB_NEW_EN)
[grammar] ~135-~135: There might be a mistake here.
Context: ... + DOM updates | Unified status system | | UI updates | Direct DOM manipulati...
(QB_NEW_EN)
[grammar] ~136-~136: There might be a mistake here.
Context: ...t DOM manipulation | Declarative React | | Code organization | Multiple utili...
(QB_NEW_EN)
[grammar] ~137-~137: There might be a mistake here.
Context: ... utility classes | Single wallet class | | Type safety | None | Better valida...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...pe safety** | None | Better validation | | Development | Manual refresh | Hot...
(QB_NEW_EN)
[grammar] ~139-~139: There might be a mistake here.
Context: ...opment** | Manual refresh | Hot reload | | Build | No build step | Optimized ...
(QB_NEW_EN)
[grammar] ~144-~144: There might be a mistake here.
Context: ...- React 18 - Modern React with hooks - sats-connect 3.x - Xverse wallet integ...
(QB_NEW_EN)
[grammar] ~145-~145: There might be a mistake here.
Context: ...onnect 3.x** - Xverse wallet integration - @scure/btc-signer - Bitcoin transactio...
(QB_NEW_EN)
[grammar] ~146-~146: There might be a mistake here.
Context: ...-signer** - Bitcoin transaction creation - @scure/base - Encoding utilities - **V...
(QB_NEW_EN)
[grammar] ~147-~147: There might be a mistake here.
Context: ...n - @scure/base - Encoding utilities - Vite - Fast build tool and dev server ...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
examples/xverse-react-app/README.md
44-44: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
114-114: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
119-119: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
124-124: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (9)
examples/phantom-example/src/App.css (1)
30-34: Accessibility gating looks good.Animating only under
prefers-reduced-motion: no-preferenceis a solid default. Keep it.examples/xverse-react-app/eslint.config.js (1)
1-21: It looks like thepnpm -wworkspace flag isn’t recognized in this environment, so ESLint wasn’t actually run against your flat config. Could you please manually verify that the new flat-config is being picked up (and that thereact-refresh/only-export-componentsrule appears) by running from your project root:cd examples/xverse-react-app npx eslint --print-config src/main.jsx | rg -n "react-refresh/only-export-components"examples/xverse-react-app/src/main.jsx (1)
7-9: Heads-up: StrictMode double-invokes effects in devIf you see duplicate logs during development, it’s expected with React 18 StrictMode. No action needed unless it impacts wallet popups.
examples/xverse-react-app/SATSCONNECT_FIXES.md (1)
27-34: Confirm SatsConnect method names and response shapePlease double-check the SatsConnect reference docs for the exact RPC identifiers and payload format, then update the example accordingly.
– File:
examples/xverse-react-app/SATSCONNECT_FIXES.md(lines 27–34)
• Verify that the method to initiate a wallet connection is still namedwallet_connectand not, for example,connectWalletor similar.
• Confirm the exact response schema – whether it’s
js { result: { addresses: string[] } }
or something like
js { result: { addresses: Array<{ address: string; type: string }> } }
• Update both the RPC call and the code that readsresponse.result.addressesto match the current API.Once you’ve aligned the snippet with the August 2025 SatsConnect docs, this example will continue to work as intended.
examples/xverse-react-app/src/App.jsx (2)
112-121: Verify “offchain” vs “on-chain” wording for settlementCopy reads “offchain address” while “settle” often implies on-chain. Confirm terminology to prevent user confusion and adjust both messages accordingly.
Also applies to: 322-324
1-5: Import is correct: ArkWallet has a default exportThe scan of
examples/xverse-react-app/src/ArkWallet.jsshows:
export class ArkWallet { … }at line 19export default ArkWallet;at line 292Because there is a default export,
import ArkWallet from "./ArkWallet.js";is valid. You can ignore the suggested change.
Likely an incorrect or invalid review comment.
examples/xverse-react-app/src/SatsConnectIdentity.js (1)
105-118: Confirm and apply the correctsignPsbtresponse checksVerified that for Xverse via sats-connect the canonical method is
signPsbt, and it returns an object with:
status: "success"on successresultobject containing:
psbt(base64-encoded signed PSBT)- optional
txidwhenbroadcast: true(docs.xverse.app)Please update your code at the noted locations (lines 105–118, 129–139, 167–178) to ensure you:
- Check
response.status === "success"- Access the signed PSBT as
response.result.psbtExample diff:
- const response = await this.satsConnectRequest("signPsbt", requestParams); - // …no explicit checks… + const response = await this.satsConnectRequest("signPsbt", requestParams); + if (response.status === "success" && response.result?.psbt) { + const signedPsbt = response.result.psbt; + // proceed with signed PSBT + } else { + // handle error or user cancellation + }examples/xverse-react-app/src/ArkWallet.js (2)
90-107: Good call deferring signing to the external wallet via identityUsing
SatsConnectIdentityinstead ofSingleKeyprevents private key misuse and aligns with PSBT flows.
55-56: Confirm correctnetworkparameter for Signet in sats-connect
Thewallet_connectcall insats-connectmay require a specific format (e.g. lowercase"signet"or a constant object) rather than the capitalized"Signet". A mismatch here will silently break address resolution. Please verify against the sats-connect documentation or source (e.g. thewallet_connectsignature andnetworksexports) and update this value accordingly.• File:
examples/xverse-react-app/src/ArkWallet.js
• Lines: 55–56
• Suggested actions:
- Check the
wallet_connectfunction signature in the installedsats-connectpackage (or its docs) to see whether it expects a lowercase string or a network enum/object.- Replace
network: "Signet"with the exact identifier (for example,network: "signet"ornetwork: Networks.SIGNET) used by sats-connect.
| "devDependencies": { | ||
| "@types/react": "^18.2.66", | ||
| "@types/react-dom": "^18.2.22", | ||
| "@vitejs/plugin-react": "^4.2.1", | ||
| "eslint": "^8.57.0", | ||
| "eslint-plugin-react": "^7.34.1", | ||
| "eslint-plugin-react-hooks": "^4.6.0", | ||
| "eslint-plugin-react-refresh": "^0.4.6", | ||
| "vite": "^5.2.0" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add “globals” devDependency required by new flat config
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
+ "globals": "^13.24.0",
"vite": "^5.2.0"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "devDependencies": { | |
| "@types/react": "^18.2.66", | |
| "@types/react-dom": "^18.2.22", | |
| "@vitejs/plugin-react": "^4.2.1", | |
| "eslint": "^8.57.0", | |
| "eslint-plugin-react": "^7.34.1", | |
| "eslint-plugin-react-hooks": "^4.6.0", | |
| "eslint-plugin-react-refresh": "^0.4.6", | |
| "vite": "^5.2.0" | |
| } | |
| "devDependencies": { | |
| "@types/react": "^18.2.66", | |
| "@types/react-dom": "^18.2.22", | |
| "@vitejs/plugin-react": "^4.2.1", | |
| "eslint": "^8.57.0", | |
| "eslint-plugin-react": "^7.34.1", | |
| "eslint-plugin-react-hooks": "^4.6.0", | |
| "eslint-plugin-react-refresh": "^0.4.6", | |
| "globals": "^13.24.0", | |
| "vite": "^5.2.0" | |
| } |
🤖 Prompt for AI Agents
In examples/xverse-react-app/package.json around lines 21 to 30, the
devDependencies block is missing the "globals" package required by the new
ESLint flat config; add an entry for "globals" (use a current semver like
"^13.2.0" or the repo-standard version) to devDependencies and run npm/yarn
install so the flat config can resolve global definitions.
| // Validate recipient address | ||
| if (!this.isValidArkAddress(recipient)) { | ||
| throw new Error("Invalid ARK address format"); | ||
| } | ||
|
|
||
| // Use the wallet's sendBitcoin method | ||
| const arkTxid = await this.wallet.sendBitcoin({ | ||
| address: recipient, | ||
| amount: parseInt(amount, 10), | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dangerous amount parsing; replace parseInt with strict sats validation
parseInt accepts “1e6”→1, “0.01”→0, etc., risking wrong sends. Also guard against non-Error throws in the catch.
- // Validate recipient address
- if (!this.isValidArkAddress(recipient)) {
- throw new Error("Invalid ARK address format");
- }
-
- // Use the wallet's sendBitcoin method
- const arkTxid = await this.wallet.sendBitcoin({
- address: recipient,
- amount: parseInt(amount, 10),
- });
+ // Validate recipient address
+ if (!this.isValidArkAddress(recipient)) {
+ throw new Error("Invalid ARK address format");
+ }
+
+ // Strict sats parsing
+ const amountStr = String(amount).trim();
+ if (!/^[0-9]+$/.test(amountStr)) {
+ throw new Error("Amount must be a whole number of satoshis.");
+ }
+ const amountSats = Number(amountStr);
+ if (!Number.isSafeInteger(amountSats) || amountSats <= 0) {
+ throw new Error("Amount must be a positive integer within the safe range.");
+ }
+
+ const arkTxid = await this.wallet.sendBitcoin({
+ address: recipient,
+ amount: amountSats,
+ });
@@
- } catch (error) {
- // If it's a wallet connection error, provide helpful message
- if (
- error.message.includes("Wallet not connected") ||
- error.message.includes("not connected") ||
- error.message.includes("UNAUTHORIZED")
- ) {
- throw new Error(
- "Wallet connection lost. Please refresh the page and reconnect your wallet."
- );
- }
- throw new Error(`Transaction failed: ${error.message}`);
- }
+ } catch (error) {
+ const msg = (error && error.message) ? error.message : String(error);
+ // If it's a wallet connection error, provide helpful message
+ if (
+ msg.includes("Wallet not connected") ||
+ msg.includes("not connected") ||
+ msg.includes("UNAUTHORIZED")
+ ) {
+ throw new Error(
+ "Wallet connection lost. Please refresh the page and reconnect your wallet."
+ );
+ }
+ throw new Error(`Transaction failed: ${msg}`);
+ }Also applies to: 151-163
🤖 Prompt for AI Agents
In examples/xverse-react-app/src/ArkWallet.js around lines 139-149 and also
151-163, the code uses parseInt(amount, 10) which silently accepts
scientific/decimal formats (e.g. "1e6" or "0.01") and can produce incorrect
satoshi values, and the catch blocks don't guard against non-Error throws;
replace parseInt with a strict sats validation: require the amount to be a
string of only digits (no signs, decimals, or exponent notation), convert to an
integer safely (e.g., via Number or BigInt after validation), ensure the value
is >0 and within allowed limits, and throw a clear Error on invalid input;
update the catch handlers to normalize non-Error rejections into Error instances
(e.g., wrap unknown throw values with new Error(String(...))) before
logging/propagating.
| if (outputs) { | ||
| // Custom outputs provided | ||
| settleParams = { | ||
| outputs: outputs.map((output) => ({ | ||
| address: output.address, | ||
| amount: BigInt(output.amount), | ||
| })), | ||
| }; | ||
| } | ||
| // If no outputs provided, settle() will use default behavior (all to offchain) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate custom settle outputs (addresses and amounts) before broadcasting
Prevent invalid ARK addresses and non-integer/zero amounts; also guard catch on non-Error throws.
if (outputs) {
- // Custom outputs provided
- settleParams = {
- outputs: outputs.map((output) => ({
- address: output.address,
- amount: BigInt(output.amount),
- })),
- };
+ if (!Array.isArray(outputs) || outputs.length === 0) {
+ throw new Error("outputs must be a non-empty array");
+ }
+ outputs.forEach((o, i) => {
+ if (!o || typeof o.address !== "string" || !this.isValidArkAddress(o.address)) {
+ throw new Error(`Invalid ARK address at outputs[${i}]`);
+ }
+ const amtStr = String(o.amount).trim();
+ if (!/^[0-9]+$/.test(amtStr)) {
+ throw new Error(`Amount at outputs[${i}] must be a whole number of satoshis`);
+ }
+ if (amtStr === "0") {
+ throw new Error(`Amount at outputs[${i}] must be > 0`);
+ }
+ });
+ settleParams = {
+ outputs: outputs.map((o) => ({
+ address: o.address,
+ amount: BigInt(String(o.amount)),
+ })),
+ };
}
@@
- } catch (error) {
- // If it's a wallet connection error, provide helpful message
- if (
- error.message.includes("Wallet not connected") ||
- error.message.includes("not connected") ||
- error.message.includes("UNAUTHORIZED")
- ) {
- throw new Error(
- "Wallet connection lost. Please refresh the page and reconnect your wallet."
- );
- }
- throw new Error(`Settlement failed: ${error.message}`);
- }
+ } catch (error) {
+ const msg = (error && error.message) ? error.message : String(error);
+ // If it's a wallet connection error, provide helpful message
+ if (
+ msg.includes("Wallet not connected") ||
+ msg.includes("not connected") ||
+ msg.includes("UNAUTHORIZED")
+ ) {
+ throw new Error(
+ "Wallet connection lost. Please refresh the page and reconnect your wallet."
+ );
+ }
+ throw new Error(`Settlement failed: ${msg}`);
+ }Also applies to: 209-220
🤖 Prompt for AI Agents
In examples/xverse-react-app/src/ArkWallet.js around lines 184-193 and also
209-220, the code maps custom settle outputs directly without validating address
format or amount values and doesn't robustly handle non-Error throws; add
validation that each output has a defined address and amount, verify the address
is a valid ARK address (use existing validator or implement validation/regex),
ensure amount is a positive integer and not zero (reject decimals and negative
values) before converting to BigInt, and throw a descriptive Error for any
invalid output; additionally wrap settle/broadcast calls in try/catch that
normalizes non-Error throws (e.g., rethrow as new Error(String(err))) and log
the error message so invalid inputs and unexpected throw types are handled
safely.
| const addressResponse = await satsConnectRequest("wallet_connect", { | ||
| addresses: ["payment", "ordinals"], | ||
| message: "Testing wallet connection", | ||
| }); | ||
| console.log("wallet_connect response:", addressResponse); | ||
|
|
||
| return { | ||
| success: addressResponse.status === "success", | ||
| addresses: addressResponse.result?.addresses || null, | ||
| error: addressResponse.error?.message || null, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Include network and make success detection tolerant to provider variants
Some wallets return ok: true instead of status: "success".
- const addressResponse = await satsConnectRequest("wallet_connect", {
- addresses: ["payment", "ordinals"],
- message: "Testing wallet connection",
- });
+ const addressResponse = await satsConnectRequest("wallet_connect", {
+ addresses: ["payment", "ordinals"],
+ message: "Testing wallet connection",
+ network: "signet",
+ });
console.log("wallet_connect response:", addressResponse);
return {
- success: addressResponse.status === "success",
- addresses: addressResponse.result?.addresses || null,
- error: addressResponse.error?.message || null,
+ success:
+ addressResponse?.status === "success" ||
+ addressResponse?.ok === true,
+ addresses: addressResponse?.result?.addresses ?? null,
+ error: addressResponse?.error?.message ?? null,
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const addressResponse = await satsConnectRequest("wallet_connect", { | |
| addresses: ["payment", "ordinals"], | |
| message: "Testing wallet connection", | |
| }); | |
| console.log("wallet_connect response:", addressResponse); | |
| return { | |
| success: addressResponse.status === "success", | |
| addresses: addressResponse.result?.addresses || null, | |
| error: addressResponse.error?.message || null, | |
| }; | |
| const addressResponse = await satsConnectRequest("wallet_connect", { | |
| addresses: ["payment", "ordinals"], | |
| message: "Testing wallet connection", | |
| network: "signet", | |
| }); | |
| console.log("wallet_connect response:", addressResponse); | |
| return { | |
| success: | |
| addressResponse?.status === "success" || | |
| addressResponse?.ok === true, | |
| addresses: addressResponse?.result?.addresses ?? null, | |
| error: addressResponse?.error?.message ?? null, | |
| }; |
🤖 Prompt for AI Agents
In examples/xverse-react-app/src/SatsConnectDebugger.js around lines 13 to 23,
the response handling should be made tolerant to provider variants and include
network info: change the success check to accept either addressResponse.status
=== "success" or addressResponse.ok === true (or Boolean(addressResponse.ok)),
and populate addresses from result?.addresses (falling back to
addressResponse.addresses) and include network from result?.network or
addressResponse.network (or null if absent); keep error as result of
addressResponse.error?.message || null. Ensure the returned object uses these
tolerant checks and fields.
| const methods = ["signPsbt", "sign_psbt", "signTransaction", "sign"]; | ||
| const results = {}; | ||
|
|
||
| for (const method of methods) { | ||
| try { | ||
| console.log(`Testing method: ${method}`); | ||
|
|
||
| const response = await satsConnectRequest(method, { | ||
| psbt: testPsbt, | ||
| signInputs: { | ||
| [testAddress]: [0], | ||
| }, | ||
| broadcast: false, | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Pass network and verify method responses rather than assuming try/catch implies success
Providers may resolve with a non-success status without throwing.
const methods = ["signPsbt", "sign_psbt", "signTransaction", "sign"];
const results = {};
for (const method of methods) {
try {
console.log(`Testing method: ${method}`);
- const response = await satsConnectRequest(method, {
- psbt: testPsbt,
- signInputs: {
- [testAddress]: [0],
- },
- broadcast: false,
- });
+ const response = await satsConnectRequest(method, {
+ psbt: testPsbt,
+ signInputs: { [testAddress]: [0] },
+ broadcast: false,
+ network: "signet",
+ });
- results[method] = {
- success: true,
- response: response,
- };
+ const ok =
+ response?.status === "success" || response?.ok === true;
+ results[method] = ok
+ ? { success: true, response }
+ : {
+ success: false,
+ error:
+ response?.error?.message ??
+ "Unknown non-success response",
+ response,
+ };
console.log(`${method} succeeded:`, response);
} catch (error) {
results[method] = {
success: false,
error: error.message,
};Also applies to: 56-66
🤖 Prompt for AI Agents
In examples/xverse-react-app/src/SatsConnectDebugger.js around lines 41-55 (and
similarly for 56-66), the current loop treats any resolved response as success
because it relies on try/catch only; update the logic to inspect the resolved
response object (e.g., check response.status, response.success, or an error
field) and verify the method returned a successful outcome before marking it as
passed; if the response indicates failure, record/log the error details and
treat it as a failed result; ensure the results map stores explicit
success/failure and any returned data or error message for each method.
| signerSession() { | ||
| // For external wallets, we can't control the signing session | ||
| // Return a random session as a placeholder | ||
| // This would need to be imported from the SDK if needed | ||
| throw new Error("signerSession not implemented for external wallets"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Unimplemented signerSession may break SDK integrations
If the SDK ever calls signerSession(), this will throw. Confirm it’s not used, or implement a benign session provider.
🏁 Script executed:
#!/bin/bash
# Search for any usage of signerSession in the repo
rg -nP -C2 '\bsignerSession\s*\('Length of output: 1561
Implement signerSession in SatsConnectIdentity to avoid runtime errors
The example identity’s signerSession() currently throws unconditionally. However, the SDK invokes this method when preparing off-chain outputs:
- In
src/wallet/wallet.tsat line 586, the code callsthis.identity.signerSession()before usingsession.getPublicKey().
As written, running the xverse-react-app example will result in an uncaught Error at runtime. You should either:
- Provide a benign default implementation (e.g. return
TreeSignerSession.random()), or - Clearly document that this example doesn’t support signing and guard against calling into the SDK’s signing path.
Suggested change in examples/xverse-react-app/src/SatsConnectIdentity.js (around lines 216–221):
signerSession() {
- // For external wallets, we can't control the signing session
- // Return a random session as a placeholder
- // This would need to be imported from the SDK if needed
- throw new Error("signerSession not implemented for external wallets");
+ // Return a random session so the SDK’s signing flow won’t crash.
+ // Note: for production, replace with a proper session provider.
+ return TreeSignerSession.random();
}• Import TreeSignerSession from the SDK at the top of the file:
import { TreeSignerSession } from "@xverse/connect-sdk";• Or, if you intend this example to be read-only, wrap SDK calls to avoid invoking signerSession().
🤖 Prompt for AI Agents
In examples/xverse-react-app/src/SatsConnectIdentity.js around lines 216-221,
the signerSession() method currently throws an Error which causes runtime
failures when the SDK calls it; change it to return a benign default session
(e.g., return TreeSignerSession.random()) and add the required import at the top
of the file (import { TreeSignerSession } from "@xverse/connect-sdk"), or
alternatively make the identity explicitly read-only by guarding SDK calls so
signerSession() is never invoked; implement one of these fixes so
signerSession() no longer throws.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
examples/xverse-react-app/README.md (6)
172-181: Add missing sats-connect import and verify network enum/casingMake the snippet runnable and flag the network value for confirmation against sats-connect API.
```javascript + import { request } from 'sats-connect'; import { SatsConnectIdentity } from './SatsConnectIdentity'; - import { Wallet } from '@arkosdao/ts-sdk'; + import { Wallet } from '@arkade-os/ts-sdk'; // Connect wallet const response = await request('wallet_connect', { addresses: ['payment', 'ordinals'], - network: 'Signet' + network: 'signet' // verify expected casing/value per sats-connect docs }); ```
88-97: Tighten SatsConnectIdentity example for clarityConsider showing minimal, concrete code to help users implement the sign() method and imports.
```javascript - // Implements ARK SDK Identity interface - class SatsConnectIdentity { - async sign(tx, inputIndexes) { - // Converts transaction to PSBT - // Uses sats-connect to request wallet signature - // Returns signed transaction - } - } + import { request } from 'sats-connect'; + // Implements ARK SDK Identity interface + class SatsConnectIdentity { + constructor(address, publicKey, requestFn = request) { + this.address = address; + this.publicKey = publicKey; + this.request = requestFn; + } + async sign(psbtBase64, inputIndexes) { + // Convert/prepare PSBT if needed, then delegate to sats-connect + const res = await this.request('sign_psbt', { + psbt: psbtBase64, + signAtIndex: inputIndexes, + network: 'signet' // verify per your app config + }); + return res?.psbt || psbtBase64; + } + } ```
129-136: Add a production-safety note to Debug ModePSBTs and verbose logs can leak sensitive transaction details.
- The app includes comprehensive logging. Check browser console for: + The app includes comprehensive logging. Check the browser console for: - - Wallet connection status - - Transaction details - - PSBT data - - ARK server responses - - Signing attempts + - Wallet connection status. + - Transaction details. + - PSBT data. + - ARK server responses. + - Signing attempts. + +Note: PSBTs and debug logs may contain sensitive information. Disable verbose logging in production.
111-123: Be explicit about where to change network settingsPoint readers to concrete keys/locations so they don’t hunt through code.
To change networks, update: -1. `ArkWallet.js` - ARK server URL -2. `ArkWallet.js` - sats-connect network parameter +1. `ArkWallet.js` — ARK coordinator URL (e.g., `const COORDINATOR_URL = '...'` or `walletConfig.baseUrl`). +2. `ArkWallet.js` — sats-connect network parameter (e.g., `{ network: 'signet' }`). 3. Switch your Xverse wallet to the same networkIf you prefer, add a short code snippet showing these exact lines from ArkWallet.js.
9-12: Minor grammar/punctuation touch-ups in feature listImproves readability and consistency.
- - Connect to Xverse browser extension wallet - - Use external wallet signing instead of in-memory private keys - - Send Bitcoin transactions through ARK's collaborative transaction protocol - - Handle wallet connection states and errors + - Connect to the Xverse browser extension wallet. + - Use external wallet signing instead of in-memory private keys. + - Send Bitcoin transactions via ARK's collaborative transaction protocol. + - Handle wallet-connection states and errors.
49-66: Note on double-signing prompts may vary by wallet versionPrompt sequencing can differ with Xverse versions; add a brief caveat to reduce user confusion if they see one combined prompt.
-4. **First time**: Xverse will prompt to sign the funding transaction -5. **Second prompt**: Xverse will prompt to sign the ARK round transaction +4. You may see two prompts: one to sign the funding transaction, then one to sign the ARK round transaction (prompt sequencing can vary by Xverse version).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
examples/xverse-react-app/README.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
examples/xverse-react-app/README.md
[grammar] ~9-~9: There might be a mistake here.
Context: ...This React app shows how to: - Connect to Xverse browser extension wallet - Use e...
(QB_NEW_EN)
[grammar] ~122-~122: There might be a mistake here.
Context: ...let.js` - sats-connect network parameter 3. Switch your Xverse wallet to the same ne...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...The app includes comprehensive logging. Check browser console for: - Wallet connecti...
(QB_NEW_EN)
[grammar] ~131-~131: There might be a mistake here.
Context: ...console for: - Wallet connection status - Transaction details - PSBT data - ARK se...
(QB_NEW_EN)
[grammar] ~132-~132: There might be a mistake here.
Context: ... connection status - Transaction details - PSBT data - ARK server responses - Signi...
(QB_NEW_EN)
[grammar] ~133-~133: There might be a mistake here.
Context: ...status - Transaction details - PSBT data - ARK server responses - Signing attempts ...
(QB_NEW_EN)
[grammar] ~134-~134: There might be a mistake here.
Context: ...tails - PSBT data - ARK server responses - Signing attempts ### Testing Different ...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (2)
examples/xverse-react-app/README.md (2)
111-118: Confirm coordinator URL and naming consistency (ARK vs Arkade)Ensure the README and code consistently use the product/package naming and that the coordinator URL for Signet is correct and reachable.
Would you like me to add a small .env example (COORDINATOR_URL, NETWORK) and reference it in ArkWallet.js?
1-4: Overall: Solid, practical READMEClear architecture, steps, and code pointers. Once the package scope fix lands, this should be copy-paste friendly.
| npm install sats-connect @arkosdao/ts-sdk | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix SDK package name in install command
The npm instruction points to a non-existent/org-mismatched package; the repository is arkade-os/ts-sdk.
- npm install sats-connect @arkosdao/ts-sdk
+ npm install sats-connect @arkade-os/ts-sdkCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/xverse-react-app/README.md around lines 167 to 168, the npm install
command references the wrong SDK package name; update the install instruction to
use the correct package name from the arkade-os/ts-sdk repository (replace
@arkosdao/ts-sdk with the actual package name published by arkade-os, e.g.,
@arkade-os/ts-sdk) so the example uses the accurate package for npm installation
and matches the repository ownership.
| import { Wallet } from '@arkosdao/ts-sdk'; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct Wallet import path
Align the import with the published package scope.
- import { Wallet } from '@arkosdao/ts-sdk';
+ import { Wallet } from '@arkade-os/ts-sdk';Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/xverse-react-app/README.md around lines 174 to 175, the Wallet is
imported from the package root but the published package exposes the Wallet
through its scoped subpath; change the import to the package's correct export
path (for example import { Wallet } from '@arkosdao/ts-sdk/wallet') so the
README uses the actual published import location.
- Implemented core functionality for deriving private keys and signing PSBTs. - Created configuration files for Snap including snap.config.js and snap.manifest.json. - Added TypeScript configuration for the Snap project. - Integrated user confirmation dialog for signing transactions. - Included error handling for PSBT parsing and signing processes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
examples/metamask-react-app/src/ArkWallet.js (1)
1-309: Add missing ArkAddress import and re-run Prettier
In examples/metamask-react-app/src/ArkWallet.js, insertimport { ArkAddress } from "@arkade-os/sdk";after your other imports (exactly once), then re-run Prettier to clear the CI formatting error.
🧹 Nitpick comments (12)
snap/snap.config.js (1)
12-14: Comment contradicts config.The comment says the snap “will use Node.js built-ins,” but
ignore: ['fs']disables bundlingfs. Reword.- // This snap will use Node.js built-ins. + // Do not bundle Node.js built-ins (fs is not available in snaps).examples/metamask-react-app/eslint.config.js (2)
10-10: Update ignore pattern to match the actual config file extensionThe ignore pattern references
.eslintrc.cjsbut this file is namedeslint.config.js. This mismatch could cause issues if there's an actual.eslintrc.cjsfile that should be ignored.- ignorePatterns: ["dist", ".eslintrc.cjs"], + ignorePatterns: ["dist", "eslint.config.js"],
12-12: Consider using automatic React version detectionHardcoding the React version to "18.2" could become outdated. Consider using automatic detection for better maintainability.
- settings: { react: { version: "18.2" } }, + settings: { react: { version: "detect" } },examples/metamask-react-app/README.md (1)
25-32: Consider using workspace commands for better developer experienceThe installation instructions require manual navigation between directories. Since this is a monorepo with pnpm, consider leveraging workspace commands or scripts for a smoother setup experience.
Would you like me to suggest a root-level script configuration that could simplify the setup process for developers?
snap/src/index.ts (2)
52-52: Preserve the original error for better debuggingWhen re-throwing errors, preserve the original error information for better debugging.
- throw new Error(`Invalid PSBT: ${error.message}`); + throw new Error(`Invalid PSBT: ${error instanceof Error ? error.message : String(error)}`);
88-101: Consider more robust error handling for signing failuresCurrently, signing failures are only logged as warnings. Consider collecting these failures and reporting them back to the caller for better transparency.
Would you like me to suggest an implementation that tracks and reports which inputs were successfully signed vs. which failed?
examples/metamask-react-app/src/index.css (1)
7-13: Consider dark-mode and contrastThe gradient background plus white card works, but text colors may miss WCAG contrast on some displays. Consider supporting prefers-color-scheme and increasing contrast slightly.
Also applies to: 32-37
examples/metamask-react-app/src/App.jsx (2)
114-121: Clarify “settle” semantics (on-chain vs offchain) and align copyFunction is named settleToOnchain, but status/copy say “offchain.” Align user-facing text with actual behavior to avoid confusion.
- addStatus("Settling all funds to offchain address...", "info"); + addStatus("Settling all funds to on-chain address...", "info"); @@ - Settle all your funds (boarding UTXOs + VTXOs) to - your offchain Arkade address. + Settle all your funds (boarding UTXOs + VTXOs) to + your on-chain address.Also applies to: 300-312
156-164: PR description vs. implementation mismatchHeader and code target MetaMask Snap, while PR objectives mention Xverse via sats-connect. Please confirm scope or adjust PR title/description accordingly.
examples/metamask-react-app/src/MetaMaskSnapIdentity.js (1)
59-66: Improve defensive checks around tx shapeIf tx.inputsLength is missing, this can break. Prefer deriving input count from PSBT or a safe fallback.
- console.log("MetaMaskSnapIdentity.sign called with:", { - inputIndexes, - txInputsLength: tx.inputsLength, - address: this.address, - }); + const inputsLen = typeof tx.inputsLength === "number" ? tx.inputsLength : (tx.inputs?.length ?? 0); + console.log("MetaMaskSnapIdentity.sign called with:", { inputIndexes, txInputsLength: inputsLen, address: this.address }); @@ - } else { - signInputs = Array.from({ length: tx.inputsLength }, (_, i) => i); + } else { + signInputs = Array.from({ length: inputsLen }, (_, i) => i); }Also applies to: 100-108
examples/metamask-react-app/src/ArkWallet.js (2)
5-7: Make endpoints configurableHard-coding ARK_SERVER_URL and ESPLORA_URL limits reuse. Consider using Vite env vars (import.meta.env) with sensible defaults.
-const ARK_SERVER_URL = "https://signet.arkade.sh"; -const ESPLORA_URL = "https://mempool.space/signet/api"; +const ARK_SERVER_URL = import.meta.env.VITE_ARK_SERVER_URL ?? "https://signet.arkade.sh"; +const ESPLORA_URL = import.meta.env.VITE_ESPLORA_URL ?? "https://mempool.space/signet/api";
281-288: Validate Ark address decode errors explicitlyCatching all exceptions hides useful diagnostics. Narrow the catch and surface decode errors.
isValidArkAddress(address) { - try { - ArkAddress.decode(address); - return true; - } catch { - return false; - } + try { + ArkAddress.decode(address); + return true; + } catch (e) { + console.debug("Ark address decode failed:", e); + return false; + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
examples/metamask-react-app/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsnap/images/icon.svgis excluded by!**/*.svgsnap/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
examples/metamask-react-app/README.md(1 hunks)examples/metamask-react-app/eslint.config.js(1 hunks)examples/metamask-react-app/index.html(1 hunks)examples/metamask-react-app/package.json(1 hunks)examples/metamask-react-app/src/App.jsx(1 hunks)examples/metamask-react-app/src/ArkWallet.js(1 hunks)examples/metamask-react-app/src/MetaMaskSnapIdentity.js(1 hunks)examples/metamask-react-app/src/index.css(1 hunks)examples/metamask-react-app/src/main.jsx(1 hunks)examples/metamask-react-app/vite.config.js(1 hunks)snap/.eslintrc.js(1 hunks)snap/.gitignore(1 hunks)snap/README.md(1 hunks)snap/package.json(1 hunks)snap/snap.config.js(1 hunks)snap/snap.manifest.json(1 hunks)snap/src/index.ts(1 hunks)snap/tsconfig.json(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- examples/metamask-react-app/index.html
- snap/snap.manifest.json
- snap/.gitignore
🧰 Additional context used
🧬 Code graph analysis (3)
examples/metamask-react-app/src/MetaMaskSnapIdentity.js (1)
examples/spillman.js (1)
signedTx(276-276)
examples/metamask-react-app/src/App.jsx (1)
examples/metamask-react-app/src/ArkWallet.js (1)
ArkWallet(8-306)
examples/metamask-react-app/src/ArkWallet.js (1)
examples/metamask-react-app/src/MetaMaskSnapIdentity.js (1)
MetaMaskSnapIdentity(8-128)
🪛 GitHub Actions: CI
examples/metamask-react-app/package.json
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
examples/metamask-react-app/src/MetaMaskSnapIdentity.js
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
examples/metamask-react-app/src/App.jsx
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
examples/metamask-react-app/src/ArkWallet.js
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
🪛 LanguageTool
examples/metamask-react-app/README.md
[grammar] ~9-~9: There might be a mistake here.
Context: ...ask Snap integration for Bitcoin signing - Taproot address generation and managemen...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...aproot address generation and management - ARK protocol transactions (send, settle,...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...tions (send, settle, balance management) - Secure signing without exposing private ...
(QB_NEW_EN)
[grammar] ~16-~16: There might be a mistake here.
Context: ...requisites - MetaMask browser extension - Node.js 18+ - pnpm package manager ## Q...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...MetaMask browser extension - Node.js 18+ - pnpm package manager ## Quick Start ##...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...tic key derivation from MetaMask entropy - PSBT signing for Taproot transactions - ...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ... - PSBT signing for Taproot transactions - RPC interface for dApp communication ##...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ... replacement for other wallet identities - ArkWallet: Simplified wallet management ...
(QB_NEW_EN)
[grammar] ~80-~80: There might be a mistake here.
Context: ...: Simplified wallet management interface - Real-time balance and transaction update...
(QB_NEW_EN)
[grammar] ~93-~93: There might be a mistake here.
Context: ... - Bitcoin Network: Signet (testnet) - ARK Server: https://signet.arkade.sh...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...k**: Signet (testnet) - ARK Server: https://signet.arkade.sh - Block Explorer: `https://mempool.space...
(QB_NEW_EN)
snap/README.md
[grammar] ~9-~9: There might be a mistake here.
Context: ...ddress generation using MetaMask entropy - PSBT signing for ARK protocol transactio...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...BT signing for ARK protocol transactions - Secure key management within MetaMask sa...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...e key management within MetaMask sandbox - User confirmation for all signing operat...
(QB_NEW_EN)
[grammar] ~18-~18: There might be a mistake here.
Context: ...s - MetaMask Flask or development build - Node.js 18+ - pnpm package manager ### ...
(QB_NEW_EN)
[grammar] ~19-~19: There might be a mistake here.
Context: ...Flask or development build - Node.js 18+ - pnpm package manager ### Setup ```bash...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ... 1. Install MetaMask Flask 2. Navigate to snap development server 3. Connect from...
(QB_NEW_EN)
[grammar] ~78-~78: There might be a mistake here.
Context: ...`` ## Architecture - Entry Point: src/index.ts - Key Management: Deterministic derivati...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ...inistic derivation from MetaMask entropy - Security: All operations require user ...
(QB_NEW_EN)
[grammar] ~80-~80: There might be a mistake here.
Context: ...**: All operations require user approval - Network: Bitcoin Signet for safe testi...
(QB_NEW_EN)
🔇 Additional comments (8)
snap/tsconfig.json (1)
1-9: LGTM — aligns with snaps-cli expectations.Root/out directories look correct for
mm-snap. Ensure ESLint’sparserOptions.projectpoints here (see .eslintrc.js change).snap/snap.config.js (1)
9-15: Ignore this suggestion; snaps-cli supports stats.buffer and stats.builtIns.ignore These options are valid in snap.config.js for mm-snap to ignore built-ins likefs.Likely an incorrect or invalid review comment.
examples/metamask-react-app/vite.config.js (1)
1-12: Configuration looks good for React + crypto librariesThe Vite configuration correctly sets up React with necessary polyfills and optimizations for cryptographic libraries. The
globalpolyfill and pre-bundling of@scureand@noblelibraries will ensure smooth operation in the browser environment.snap/README.md (1)
1-89: Well-structured documentation with clear setup instructionsThe README provides comprehensive documentation for the MetaMask Snap, including clear prerequisites, setup instructions, API examples, and security model details. The architecture and security sections appropriately highlight the key security features.
snap/src/index.ts (2)
156-160: Good error code handling for user rejectionThe implementation correctly uses error code 4001 for user rejection, which aligns with the EIP-1193 standard for user-rejected requests.
30-32: Ignore this review: slicing from index 1 to 33 correctly removes the 0x04 uncompressed-key prefix to yield the 32-byte X-coordinate.Likely an incorrect or invalid review comment.
examples/metamask-react-app/src/main.jsx (1)
6-10: Entry point looks goodStandard React 18 root bootstrapping with StrictMode. No issues spotted.
examples/metamask-react-app/src/App.jsx (1)
156-179: CI: Prettier formatting fixed Example app files now comply with Prettier code style.
| { | ||
| "name": "arkade-metamask-react-app", | ||
| "private": true, | ||
| "version": "0.1.0", | ||
| "description": "ARK MetaMask Snap demo application", | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "preview": "vite preview" | ||
| }, | ||
| "packageManager": "pnpm@10.15.0", | ||
| "dependencies": { | ||
| "@arkade-os/sdk": "link:../..", | ||
| "@scure/base": "^1.1.3", | ||
| "@scure/btc-signer": "^1.3.1", | ||
| "@noble/curves": "^1.2.0", | ||
| "react": "^18.2.0", | ||
| "react-dom": "^18.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@vitejs/plugin-react": "^4.2.1", | ||
| "vite": "^5.2.0" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Fix Prettier CI failure.
Run repository formatting; if you want, add a local script for this example.
🏁 Script executed:
#!/bin/bash
# Format and re-check at repo root (uses workspace).
pnpm -w exec prettier --write .
pnpm -w exec prettier --check .Length of output: 189
Fix Prettier commands for example CI
- Remove the
-wflag and run formatting locally inexamples/metamask-react-app:cd examples/metamask-react-app pnpm exec prettier --write . pnpm exec prettier --check .
- Optionally add these scripts to
examples/metamask-react-app/package.json:"scripts": { "format": "prettier --write .", "format:check": "prettier --check ." }
🧰 Tools
🪛 GitHub Actions: CI
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
🛠️ Refactor suggestion
Declare Node engine to match Vite 5 (Node ≥18).
Prevent “unsupported engine” CI/dev surprises by pinning a minimum Node.
"packageManager": "pnpm@10.15.0",
+ "engines": {
+ "node": ">=18.0.0"
+ },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| "name": "arkade-metamask-react-app", | |
| "private": true, | |
| "version": "0.1.0", | |
| "description": "ARK MetaMask Snap demo application", | |
| "type": "module", | |
| "scripts": { | |
| "dev": "vite", | |
| "build": "vite build", | |
| "preview": "vite preview" | |
| }, | |
| "packageManager": "pnpm@10.15.0", | |
| "dependencies": { | |
| "@arkade-os/sdk": "link:../..", | |
| "@scure/base": "^1.1.3", | |
| "@scure/btc-signer": "^1.3.1", | |
| "@noble/curves": "^1.2.0", | |
| "react": "^18.2.0", | |
| "react-dom": "^18.2.0" | |
| }, | |
| "devDependencies": { | |
| "@vitejs/plugin-react": "^4.2.1", | |
| "vite": "^5.2.0" | |
| } | |
| } | |
| { | |
| "name": "arkade-metamask-react-app", | |
| "private": true, | |
| "version": "0.1.0", | |
| "description": "ARK MetaMask Snap demo application", | |
| "type": "module", | |
| "scripts": { | |
| "dev": "vite", | |
| "build": "vite build", | |
| "preview": "vite preview" | |
| }, | |
| "packageManager": "pnpm@10.15.0", | |
| "engines": { | |
| "node": ">=18.0.0" | |
| }, | |
| "dependencies": { | |
| "@arkade-os/sdk": "link:../..", | |
| "@scure/base": "^1.1.3", | |
| "@scure/btc-signer": "^1.3.1", | |
| "@noble/curves": "^1.2.0", | |
| "react": "^18.2.0", | |
| "react-dom": "^18.2.0" | |
| }, | |
| "devDependencies": { | |
| "@vitejs/plugin-react": "^4.2.1", | |
| "vite": "^5.2.0" | |
| } | |
| } |
🧰 Tools
🪛 GitHub Actions: CI
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
🤖 Prompt for AI Agents
examples/metamask-react-app/package.json lines 1-25: the package.json lacks an
"engines" field declaring the minimum Node version required by Vite 5; add an
"engines" entry (e.g. "engines": { "node": ">=18" }) to the top-level of
package.json (near other metadata fields) so CI and developers receive a clear
minimum Node requirement and avoid unsupported-engine failures.
| const amountSats = parseInt(amount); | ||
|
|
||
| addStatus("Submitting transaction to Arkade server...", "info"); | ||
| const arkTxid = await wallet.sendBitcoin(recipient, amountSats); | ||
|
|
||
| setTransactionResult(arkTxid); | ||
| addStatus("Transaction submitted successfully!", "success"); | ||
| addStatus(`Arkade Transaction ID: ${arkTxid}`, "success"); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Harden amount parsing and input constraints
parseInt without radix and loose numeric checks allow inputs like "1e2" or empty -> NaN. Use base 10, validate finite positive integers, and constrain the input element.
- const amountSats = parseInt(amount);
+ const amountSats = parseInt(amount, 10);
+ if (!Number.isFinite(amountSats) || amountSats <= 0) {
+ throw new Error("Amount must be a positive integer (sats).");
+ }
@@
- const isFormValid =
- walletInfo && recipient.trim() && amount && parseInt(amount) > 0;
+ const isFormValid =
+ !!walletInfo &&
+ recipient.trim().length > 0 &&
+ Number.isFinite(parseInt(amount, 10)) &&
+ parseInt(amount, 10) > 0;
@@
- <input
- type="number"
+ <input
+ type="number"
+ inputMode="numeric"
+ pattern="\d*"
+ min={1}
+ step={1}
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Enter amount in satoshis..."
disabled={loading}
/>Also applies to: 153-155, 279-286
🤖 Prompt for AI Agents
In examples/metamask-react-app/src/App.jsx around lines 88 to 96 (and also apply
the same fixes at lines 153-155 and 279-286): the amount parsing and validation
is too loose (parseInt without radix and no checks) so replace parseInt(amount)
with parseInt(amount, 10) and then validate that the parsed value is a finite
integer and > 0; if validation fails, prevent submission, surface a user-facing
error status and return early. Additionally, constrain the input element to
accept only whole positive numbers by using input attributes (type="number",
min="1", step="1", and optional pattern) and/or sanitize onChange to strip
non-numeric characters so the component never passes empty, "1e2", decimals or
NaN to wallet.sendBitcoin. Ensure equivalent fixes are applied at the other
referenced line ranges.
| import * as scureBase from "@scure/base"; | ||
| import { Wallet } from "@arkade-os/sdk"; | ||
| import { MetaMaskSnapIdentity } from "./MetaMaskSnapIdentity.js"; | ||
|
|
||
| const ARK_SERVER_URL = "https://signet.arkade.sh"; | ||
| const ESPLORA_URL = "https://mempool.space/signet/api"; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing ArkAddress import causes runtime error
isValidArkAddress uses ArkAddress but it’s not imported. This will throw ReferenceError at runtime.
import * as scureBase from "@scure/base";
-import { Wallet } from "@arkade-os/sdk";
+import { Wallet, ArkAddress } from "@arkade-os/sdk";
import { MetaMaskSnapIdentity } from "./MetaMaskSnapIdentity.js";
const ARK_SERVER_URL = "https://signet.arkade.sh";
const ESPLORA_URL = "https://mempool.space/signet/api";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import * as scureBase from "@scure/base"; | |
| import { Wallet } from "@arkade-os/sdk"; | |
| import { MetaMaskSnapIdentity } from "./MetaMaskSnapIdentity.js"; | |
| const ARK_SERVER_URL = "https://signet.arkade.sh"; | |
| const ESPLORA_URL = "https://mempool.space/signet/api"; | |
| import * as scureBase from "@scure/base"; | |
| import { Wallet, ArkAddress } from "@arkade-os/sdk"; | |
| import { MetaMaskSnapIdentity } from "./MetaMaskSnapIdentity.js"; | |
| const ARK_SERVER_URL = "https://signet.arkade.sh"; | |
| const ESPLORA_URL = "https://mempool.space/signet/api"; |
🧰 Tools
🪛 GitHub Actions: CI
[error] 1-1: Prettier formatting issue detected. Run 'prettier --write' to fix code style.
🤖 Prompt for AI Agents
In examples/metamask-react-app/src/ArkWallet.js around lines 1 to 7, the helper
isValidArkAddress references ArkAddress but ArkAddress is not imported which
causes a runtime ReferenceError; fix by importing ArkAddress from the SDK (add
ArkAddress to the existing import from "@arkade-os/sdk" or add a separate import
statement) so ArkAddress is defined before use.
| loadCachedConnection() { | ||
| try { | ||
| const cached = localStorage.getItem('ark-wallet-cache'); | ||
| if (cached) { | ||
| const data = JSON.parse(cached); | ||
| if (Date.now() - data.timestamp < 5 * 60 * 1000) { | ||
| this.taprootAddress = data.taprootAddress; | ||
| this.arkAddress = data.arkAddress; | ||
| this.userPubKey = data.userPubKey; | ||
| console.log("Loaded cached wallet data:", data); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| console.log("No cached connection or error loading:", error); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix cache type mismatch for userPubKey
You store userPubKey as hex string but load it back without decoding, breaking callers expecting Uint8Array (e.g., getDebugInfo encoding).
loadCachedConnection() {
try {
const cached = localStorage.getItem('ark-wallet-cache');
if (cached) {
const data = JSON.parse(cached);
if (Date.now() - data.timestamp < 5 * 60 * 1000) {
this.taprootAddress = data.taprootAddress;
this.arkAddress = data.arkAddress;
- this.userPubKey = data.userPubKey;
+ this.userPubKey = data.userPubKey ? scureBase.hex.decode(data.userPubKey) : null;
console.log("Loaded cached wallet data:", data);
}
}
} catch (error) {
console.log("No cached connection or error loading:", error);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| loadCachedConnection() { | |
| try { | |
| const cached = localStorage.getItem('ark-wallet-cache'); | |
| if (cached) { | |
| const data = JSON.parse(cached); | |
| if (Date.now() - data.timestamp < 5 * 60 * 1000) { | |
| this.taprootAddress = data.taprootAddress; | |
| this.arkAddress = data.arkAddress; | |
| this.userPubKey = data.userPubKey; | |
| console.log("Loaded cached wallet data:", data); | |
| } | |
| } | |
| } catch (error) { | |
| console.log("No cached connection or error loading:", error); | |
| } | |
| } | |
| loadCachedConnection() { | |
| try { | |
| const cached = localStorage.getItem('ark-wallet-cache'); | |
| if (cached) { | |
| const data = JSON.parse(cached); | |
| if (Date.now() - data.timestamp < 5 * 60 * 1000) { | |
| this.taprootAddress = data.taprootAddress; | |
| this.arkAddress = data.arkAddress; | |
| this.userPubKey = data.userPubKey | |
| ? scureBase.hex.decode(data.userPubKey) | |
| : null; | |
| console.log("Loaded cached wallet data:", data); | |
| } | |
| } | |
| } catch (error) { | |
| console.log("No cached connection or error loading:", error); | |
| } | |
| } |
🤖 Prompt for AI Agents
In examples/metamask-react-app/src/ArkWallet.js around lines 25 to 40, the cache
loader restores userPubKey as a hex string but callers expect a Uint8Array;
update loadCachedConnection to detect if data.userPubKey is a hex string and
convert it to a Uint8Array (e.g., decode hex to bytes) before assigning
this.userPubKey, leaving other fields unchanged and preserving the timestamp
check and error handling.
| try { | ||
| const installedSnaps = await window.ethereum.request({ | ||
| method: 'wallet_getSnaps', | ||
| }); | ||
|
|
||
| const isSnapInstalled = installedSnaps && installedSnaps[this.snapId]; | ||
|
|
||
| if (!isSnapInstalled) { | ||
| await window.ethereum.request({ | ||
| method: 'wallet_requestSnaps', | ||
| params: { | ||
| [this.snapId]: { version: "^1.0.0" }, | ||
| }, | ||
| }); | ||
| } | ||
| } catch (checkError) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make snap installation check robust
wallet_getSnaps returns an object keyed by snap IDs; guard against shape differences and use id matching.
- const installedSnaps = await window.ethereum.request({
+ const installedSnaps = await window.ethereum.request({
method: 'wallet_getSnaps',
});
-
- const isSnapInstalled = installedSnaps && installedSnaps[this.snapId];
+ const isSnapInstalled =
+ installedSnaps &&
+ (installedSnaps[this.snapId] ||
+ Object.values(installedSnaps).some((s) => s?.id === this.snapId));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| const installedSnaps = await window.ethereum.request({ | |
| method: 'wallet_getSnaps', | |
| }); | |
| const isSnapInstalled = installedSnaps && installedSnaps[this.snapId]; | |
| if (!isSnapInstalled) { | |
| await window.ethereum.request({ | |
| method: 'wallet_requestSnaps', | |
| params: { | |
| [this.snapId]: { version: "^1.0.0" }, | |
| }, | |
| }); | |
| } | |
| } catch (checkError) { | |
| try { | |
| const installedSnaps = await window.ethereum.request({ | |
| method: 'wallet_getSnaps', | |
| }); | |
| const isSnapInstalled = | |
| installedSnaps && | |
| (installedSnaps[this.snapId] || | |
| Object.values(installedSnaps).some((s) => s?.id === this.snapId)); | |
| if (!isSnapInstalled) { | |
| await window.ethereum.request({ | |
| method: 'wallet_requestSnaps', | |
| params: { | |
| [this.snapId]: { version: "^1.0.0" }, | |
| }, | |
| }); | |
| } | |
| } catch (checkError) { |
🤖 Prompt for AI Agents
In examples/metamask-react-app/src/ArkWallet.js around lines 59 to 74, the code
assumes wallet_getSnaps returns an object keyed by snap IDs and directly indexes
installedSnaps[this.snapId]; make the check robust by first confirming
installedSnaps is an object (not null/array) and then verify presence using
Object.prototype.hasOwnProperty.call(installedSnaps, this.snapId). Additionally
handle alternative shapes: if installedSnaps is an array, search for an entry
where entry.id === this.snapId (or entry.snapId), and treat any
non-object/unexpected response as "not installed" so the wallet_requestSnaps
flow runs safely.
| "scripts": { | ||
| "build": "mm-snap build", | ||
| "serve": "mm-snap serve", | ||
| "start": "mm-snap watch", | ||
| "clean": "rimraf dist" | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rimraf script without dependency.
"clean": "rimraf dist" will fail unless rimraf is installed. Either add it or use a portable rm.
Option A — add dep:
"scripts": {
"build": "mm-snap build",
"serve": "mm-snap serve",
"start": "mm-snap watch",
- "clean": "rimraf dist"
+ "clean": "rimraf dist"
},
...
"devDependencies": {
+ "rimraf": "^5.0.0",Option B — no dep:
- "clean": "rimraf dist"
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "scripts": { | |
| "build": "mm-snap build", | |
| "serve": "mm-snap serve", | |
| "start": "mm-snap watch", | |
| "clean": "rimraf dist" | |
| }, | |
| "scripts": { | |
| "build": "mm-snap build", | |
| "serve": "mm-snap serve", | |
| "start": "mm-snap watch", | |
| "clean": "rimraf dist" | |
| }, | |
| // … | |
| "devDependencies": { | |
| "rimraf": "^5.0.0", | |
| // …other devDependencies | |
| } |
| "scripts": { | |
| "build": "mm-snap build", | |
| "serve": "mm-snap serve", | |
| "start": "mm-snap watch", | |
| "clean": "rimraf dist" | |
| }, | |
| "scripts": { | |
| "build": "mm-snap build", | |
| "serve": "mm-snap serve", | |
| "start": "mm-snap watch", | |
| "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"" | |
| }, | |
| // …rest of package.json… |
🤖 Prompt for AI Agents
In snap/package.json around lines 9 to 14 the "clean": "rimraf dist" script
assumes the rimraf package is installed which will fail in environments without
that devDependency; either add rimraf to devDependencies (and run npm/yarn/pnpm
install) or replace the script with a portable no-dependency command (e.g. use a
Node builtin or cross-platform shell command like a conditional rm -rf or an npm
script that runs a small Node script to delete the dist folder) so the clean
script works consistently across environments.
| "devDependencies": { | ||
| "@metamask/snaps-cli": "latest", | ||
| "@types/node": "^20.0.0", | ||
| "typescript": "^5.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Pin snaps-cli; avoid "latest" to keep builds reproducible.
Lock to a tested @metamask/snaps-cli version; config keys can change across releases.
- "@metamask/snaps-cli": "latest",
+ "@metamask/snaps-cli": "^6.24.0",🌐 Web query:
What is the latest stable @metamask/snaps-cli version compatible with the current config fields (`polyfills.buffer`, `server.port`), as of August 2025?
💡 Result:
The latest stable @metamask/snaps-cli that supports the config fields polyfills.buffer and server.port (per the CLI docs) is v6.5.1. [1][2]
Sources:
- MetaMask Snaps CLI options (shows polyfills, stats.buffer, server.port). [1]
- MetaMask/snaps GitHub releases (shows @metamask/snaps-cli v6.5.1 release). [2]
Pin @metamask/snaps-cli to ^6.5.1
Locking to v6.5.1 ensures polyfills.buffer and server.port remain supported.
- "@metamask/snaps-cli": "latest",
+ "@metamask/snaps-cli": "^6.5.1",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "devDependencies": { | |
| "@metamask/snaps-cli": "latest", | |
| "@types/node": "^20.0.0", | |
| "typescript": "^5.0.0" | |
| "devDependencies": { | |
| "@metamask/snaps-cli": "^6.5.1", | |
| "@types/node": "^20.0.0", | |
| "typescript": "^5.0.0" | |
| } |
🤖 Prompt for AI Agents
In snap/package.json around lines 22 to 25, update the devDependency entry for
"@metamask/snaps-cli" from "latest" to the pinned version "^6.5.1" so
polyfills.buffer and server.port remain supported; after changing the version
string, run your package manager (npm install or yarn install) to update the
lockfile (package-lock.json or yarn.lock) and verify tests/builds pass.
| import { hex, base64 } from '@scure/base'; | ||
| import { networks } from '@arkade-os/sdk'; | ||
|
|
||
| declare const snap: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Replace any type with proper Snap type definition
Using any for the snap declaration bypasses TypeScript's type checking. Consider using the proper type from MetaMask's SDK.
-declare const snap: any;
+import type { SnapsGlobalObject } from '@metamask/snaps-sdk';
+declare const snap: SnapsGlobalObject;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| declare const snap: any; | |
| // At the top of snap/src/index.ts, replace: | |
| - declare const snap: any; | |
| // With: | |
| import type { SnapsGlobalObject } from '@metamask/snaps-sdk'; | |
| declare const snap: SnapsGlobalObject; |
🤖 Prompt for AI Agents
In snap/src/index.ts at line 7 replace the loose "declare const snap: any;" with
the proper Snap typing from MetaMask's snaps types: import the appropriate type
(e.g., Snap or SnapsGlobal) from the '@metamask/snaps-types' package (add it to
devDependencies if missing) and declare the global using that type (e.g.,
"declare const snap: <ImportedType>;"). This ensures TypeScript enforces the
correct Snap API surface instead of bypassing checks with any.
| }, | ||
| }); | ||
|
|
||
| return hex.decode(entropy.slice(2, 66)); // Remove 0x prefix and take first 32 bytes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation for entropy length
The code assumes the entropy string has at least 66 characters (after removing '0x'). Add validation to prevent potential runtime errors.
async function derivePrivateKey(): Promise<Uint8Array> {
const entropy = await snap.request({
method: 'snap_getEntropy',
params: {
version: 1,
salt: 'bitcoin-arkade-snap',
},
});
+ if (!entropy || entropy.length < 66) {
+ throw new Error('Insufficient entropy received from MetaMask');
+ }
+
return hex.decode(entropy.slice(2, 66)); // Remove 0x prefix and take first 32 bytes
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return hex.decode(entropy.slice(2, 66)); // Remove 0x prefix and take first 32 bytes | |
| async function derivePrivateKey(): Promise<Uint8Array> { | |
| const entropy = await snap.request({ | |
| method: 'snap_getEntropy', | |
| params: { | |
| version: 1, | |
| salt: 'bitcoin-arkade-snap', | |
| }, | |
| }); | |
| if (!entropy || entropy.length < 66) { | |
| throw new Error('Insufficient entropy received from MetaMask'); | |
| } | |
| return hex.decode(entropy.slice(2, 66)); // Remove 0x prefix and take first 32 bytes | |
| } |
🤖 Prompt for AI Agents
In snap/src/index.ts around line 21, the function currently slices
entropy.slice(2, 66) without validating the input; add explicit validation that
the entropy is a string, starts with '0x' (or handle absence explicitly), and
has a minimum length of 66 characters before slicing, otherwise throw a clear
Error (or return a controlled failure) indicating invalid entropy length/format;
only call hex.decode(entropy.slice(2,66)) after these checks.
| const { psbt, inputIndexes } = request.params as { | ||
| psbt: string; | ||
| inputIndexes?: number[]; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add type safety for request parameters
The type assertion could hide type mismatches. Consider defining proper types for the request parameters.
+interface SignPsbtParams {
+ psbt: string;
+ inputIndexes?: number[];
+}
+
case 'bitcoin_signPsbt': {
- const { psbt, inputIndexes } = request.params as {
- psbt: string;
- inputIndexes?: number[];
- };
+ const { psbt, inputIndexes } = request.params as SignPsbtParams;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { psbt, inputIndexes } = request.params as { | |
| psbt: string; | |
| inputIndexes?: number[]; | |
| }; | |
| // Define a dedicated params interface for better type safety | |
| interface SignPsbtParams { | |
| psbt: string; | |
| inputIndexes?: number[]; | |
| } | |
| case 'bitcoin_signPsbt': { | |
| const { psbt, inputIndexes } = request.params as SignPsbtParams; | |
| // …rest of handler… | |
| } |
🤖 Prompt for AI Agents
In snap/src/index.ts around lines 143 to 146, replace the broad type assertion
for request.params with a proper typed interface/typedef for the RPC params
(e.g., interface RpcParams { psbt: string; inputIndexes?: number[] }) and use it
in the function signature or when extracting params so TypeScript validates
shape at compile time; also narrow the runtime check by validating required
fields (psbt exists and is a string, inputIndexes is an array of numbers if
present) and throw a clear error if validation fails.
🔌 Add Xverse Wallet Integration Example
Overview
This PR introduces a complete example demonstrating how to integrate the Arkade SDK with external Bitcoin wallets, specifically Xverse wallet using sats-connect.
To test it locally, download Xverse, create the wallet, switch to Signet and run the web app:
cd examples/xverse-react-app pnpm devUse Arkade Wallet on signet operator
Summary by CodeRabbit