feat: add example website and improve SDK widget UI/UX & validation flow#105
feat: add example website and improve SDK widget UI/UX & validation flow#105DeveloperAmrit wants to merge 7 commits into
Conversation
📝 WalkthroughWalkthroughThis PR implements a unified payment flow and robust error handling for the StablePay widget, addressing friction in the two-step Prepare/Send transaction process. It adds a complete example application demonstrating SDK integration, improves transaction error classification using JSON-RPC codes, refactors the payment state machine into a single Pay action, and redesigns the payment dialog styling. ChangesStablePay Payment UX and Example Application
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as TransactionReview
participant WalletClient
participant RPC as Network/RPC
User->>UI: Click Pay
UI->>UI: interactionState = CONFIRMING
User->>UI: Confirm
UI->>UI: interactionState = PENDING
UI->>UI: ensureCorrectNetwork()
UI->>WalletClient: sendTransaction()
WalletClient->>RPC: eth_sendTransaction
RPC-->>WalletClient: txHash
WalletClient-->>UI: txHash
UI->>UI: Set txHash, interactionState = IDLE
UI->>User: Show transaction link + success
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
stablepay-sdk/src/widget/Dialog.jsx (1)
13-13: ⚡ Quick winKeep semantic heading for dialog title.
Line 13 swaps
<h2>to<div>, which drops document/assistive heading semantics. Prefer keeping a heading tag here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stablepay-sdk/src/widget/Dialog.jsx` at line 13, In the Dialog component replace the non-semantic <div className={styles.stablepayTitle}>StablePay</div> with a proper heading element (e.g. <h2 className={styles.stablepayTitle}>StablePay</h2>) so the title retains document/assistive heading semantics; keep the same className (styles.stablepayTitle) and styling, and ensure the Dialog component still meets any aria-labelledby usage if present.stablepay-sdk/example/index.html (1)
7-7: 💤 Low valueUpdate page title to be more descriptive.
The generic title "example" should be updated to reflect the purpose of this demonstration application.
💡 Suggested improvement
- <title>example</title> + <title>StablePay SDK Example</title>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stablepay-sdk/example/index.html` at line 7, Replace the generic HTML title tag content (<title>example</title>) with a more descriptive string that reflects the page purpose (e.g., "StablePay SDK Demo" or "StablePay SDK Example - Payment Integration Demo"); update the <title> element in the document head so it clearly identifies this demonstration app for users and search engines.stablepay-sdk/example/README.md (1)
1-17: ⚡ Quick winEnhance README with example-specific documentation.
This README contains only generic Vite+React template boilerplate and doesn't explain:
- The purpose of this example (demonstrating StablePay SDK integration)
- How to install dependencies and run the example (
npm install,npm run dev)- What the example demonstrates (wallet connection, network/token selection, transaction flow)
- How to use the SDK in your own project
📝 Suggested documentation structure
# StablePay SDK Example This example demonstrates how to integrate the StablePay SDK into a React application using Vite. ## Features Demonstrated - Wallet connection and management - Network and token selection - Transaction preparation and execution - Success/error handling ## Getting Started ```bash # Install dependencies npm install # Run development server npm run devUsage
The example shows how to:
- Import and configure the StablePay widget
- Handle transaction completion callbacks
- Manage wallet states
See
src/App.jsxfor the integration code.Additional Notes
This template uses
@vitejs/plugin-reactwith HMR and ESLint rules.</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@stablepay-sdk/example/README.mdaround lines 1 - 17, The README currently
contains only generic Vite+React boilerplate; update it to describe this example
as the "StablePay SDK Example", briefly state its purpose (demonstrating
StablePay SDK integration), list features demonstrated (wallet
connection/management, network & token selection, transaction
preparation/execution, success/error handling), add Getting Started steps (npm
install, npm run dev) and Usage notes that point to the integration code in
src/App.jsx (importing/configuring the StablePay widget, handling transaction
callbacks, managing wallet state); keep the existing note about the React
compiler and plugin links but replace template-specific text with these
example-specific instructions and a short Features/Usage section.</details> <!-- cr-comment:v1:bd7ab29875e4640f237f5211 --> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.Inline comments:
In@stablepay-sdk/example/eslint.config.js:
- Around line 11-15: The ESLint flat config currently uses an unsupported
"extends" array; replace that usage by directly spreading the config objects
into the exported array — locate the extends block referencing
js.configs.recommended, reactHooks.configs.flat.recommended, and
reactRefresh.configs.vite and change it to spread those config objects at the
top level of the flat config export so the configs are merged correctly.In
@stablepay-sdk/example/package.json:
- Around line 12-18: Remove the unused ethers dependency from the example app by
deleting the "ethers" entry in stablepay-sdk/example/package.json and then
regenerate the lockfile (package-lock.json) by running the package manager
(e.g., npm install) inside stablepay-sdk/example; ensure no code in
stablepay-sdk/example/src imports "ethers" (the widget uses viem, see
stablepay-sdk/src/widget/TransactionReview.jsx) before committing the updated
package.json and package-lock.json.In
@stablepay-sdk/example/src/App.jsx:
- Around line 8-19: The header's local wallet flow is uncoordinated with the SDK
widget: remove the header "Connect Wallet" button and the related local state
and function (remove the header button JSX, the account state variable and the
connectWallet function) so the app relies solely on
StablePay.Widget/WalletProvider for wallet connections; ensure any imports or
variables only used by connectWallet are also cleaned up and add a short comment
near StablePay.Widget/WalletProvider noting it now owns wallet handling.- Around line 33-35: The alert callback handleTransactionComplete currently
takes a string hash but TransactionReview calls onTransactionComplete with an
object payload; change handleTransactionComplete to accept and destructure the
payload (e.g., ({ txHash, network, token }) or payload) and use txHash in the
alert (with a fallback if missing) so the actual transaction hash is shown;
update any references to handleTransactionComplete to match the new signature
and keep onTransactionComplete usage in TransactionReview unchanged.In
@stablepay-sdk/src/core/Transaction.js:
- Around line 56-57: The isConnectionError check in Transaction.js incorrectly
treats error.code === 4001 as an RPC connection failure; remove 4001 from the
isConnectionError condition in the isConnectionError declaration inside the
Transaction handling logic and instead handle error.code === 4001 as a
user-rejection case (consistent with WalletContext.jsx and
TransactionReview.jsx), e.g., branch separately to return/throw a user-rejection
error or status so 4001 is no longer classified as a connection error.- Around line 33-34: The revert detection in Transaction.init is too loose:
replace the current check that treats any truthy contractError.data as a revert
by narrowing to explicit revert signatures or types (e.g., check if
contractError is an instance of ContractFunctionRevertedError or if
contractError.data exists and contractError.data.message (or contractError.data
&& contractError.data.message) contains a revert indicator like "execution
reverted" or other contract revert text); update the isReverted computation in
Transaction.init to use these stricter conditions (reference the contractError
variable and Transaction.init function) so provider/RPC errors with generic data
are not misclassified as contract reverts.In
@stablepay-sdk/src/widget/TransactionReview.jsx:
- Around line 101-104: The button's onClick uses window.open(url, '_blank')
which is vulnerable to tabnabbing; update the click handler in
TransactionReview.jsx to call window.open('https://metamask.io/download/',
'_blank', 'noopener,noreferrer') (or alternatively set the returned window's
opener to null) so the opened page cannot access window.opener.
Nitpick comments:
In@stablepay-sdk/example/index.html:<title> element in the document head so it clearly identifies this demonstration app for users and search engines.
- Line 7: Replace the generic HTML title tag content (<title>example</title>)
with a more descriptive string that reflects the page purpose (e.g., "StablePay
SDK Demo" or "StablePay SDK Example - Payment Integration Demo"); update theIn
@stablepay-sdk/example/README.md:
- Around line 1-17: The README currently contains only generic Vite+React
boilerplate; update it to describe this example as the "StablePay SDK Example",
briefly state its purpose (demonstrating StablePay SDK integration), list
features demonstrated (wallet connection/management, network & token selection,
transaction preparation/execution, success/error handling), add Getting Started
steps (npm install, npm run dev) and Usage notes that point to the integration
code in src/App.jsx (importing/configuring the StablePay widget, handling
transaction callbacks, managing wallet state); keep the existing note about the
React compiler and plugin links but replace template-specific text with these
example-specific instructions and a short Features/Usage section.In
@stablepay-sdk/src/widget/Dialog.jsx:
- Line 13: In the Dialog component replace the non-semantic
StablePaywith a proper heading element
(e.g.StablePay
) so the title retains
document/assistive heading semantics; keep the same className
(styles.stablepayTitle) and styling, and ensure the Dialog component still meets
any aria-labelledby usage if present.</details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `67180db3-9253-4741-bfa4-8fc1e58945d4` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between a254fa62d912f352b543ba2811a03f3fdf213b1a and 4a3df35a6d15313a8009d388a64ac228322c1ff0. </details> <details> <summary>⛔ Files ignored due to path filters (12)</summary> * `stablepay-sdk/dist/esm/index.js` is excluded by `!**/dist/**` * `stablepay-sdk/dist/esm/styles.css` is excluded by `!**/dist/**` * `stablepay-sdk/dist/umd/index.js` is excluded by `!**/dist/**` * `stablepay-sdk/dist/umd/index.js.map` is excluded by `!**/dist/**`, `!**/*.map` * `stablepay-sdk/dist/umd/styles.css` is excluded by `!**/dist/**` * `stablepay-sdk/example/package-lock.json` is excluded by `!**/package-lock.json` * `stablepay-sdk/example/public/favicon.svg` is excluded by `!**/*.svg` * `stablepay-sdk/example/public/icons.svg` is excluded by `!**/*.svg` * `stablepay-sdk/example/src/assets/hero.png` is excluded by `!**/*.png` * `stablepay-sdk/example/src/assets/react.svg` is excluded by `!**/*.svg` * `stablepay-sdk/example/src/assets/vite.svg` is excluded by `!**/*.svg` * `stablepay-sdk/package-lock.json` is excluded by `!**/package-lock.json` </details> <details> <summary>📒 Files selected for processing (16)</summary> * `stablepay-sdk/example/.gitignore` * `stablepay-sdk/example/README.md` * `stablepay-sdk/example/eslint.config.js` * `stablepay-sdk/example/index.html` * `stablepay-sdk/example/package.json` * `stablepay-sdk/example/src/App.css` * `stablepay-sdk/example/src/App.jsx` * `stablepay-sdk/example/src/index.css` * `stablepay-sdk/example/src/main.jsx` * `stablepay-sdk/example/vite.config.js` * `stablepay-sdk/examples/index.txt` * `stablepay-sdk/src/core/Transaction.js` * `stablepay-sdk/src/styles/PricingCard.css` * `stablepay-sdk/src/widget/Dialog.jsx` * `stablepay-sdk/src/widget/TokenDropdown.jsx` * `stablepay-sdk/src/widget/TransactionReview.jsx` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * stablepay-sdk/examples/index.txt </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| "dependencies": { | ||
| "ethers": "^6.16.0", | ||
| "react": "^19.2.6", | ||
| "react-dom": "^19.2.6", | ||
| "stablepay-sdk": "file:..", | ||
| "viem": "^2.52.2" | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if ethers is actually imported/used in the example app source
rg -n --type=js --type=jsx -g 'stablepay-sdk/example/src/**' "from ['\"]ethers['\"]|require\(['\"]ethers['\"]"Repository: DjedAlliance/StablePay
Length of output: 94
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate example source (and fall back safely if path differs)
if [ -d "stablepay-sdk/example/src" ]; then
SRC_DIR="stablepay-sdk/example/src"
else
echo "ERROR: stablepay-sdk/example/src not found" >&2
exit 1
fi
# Search for any direct import/require usage of ethers in the example source
rg -n --hidden --no-ignore -S \
--glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
"from ['\"]ethers['\"]|require\(\s*['\"]ethers['\"]\s*\)|\bethers\b" \
"$SRC_DIR" || trueRepository: DjedAlliance/StablePay
Length of output: 48
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect whether stablepay-sdk declares ethers as a (peer) dependency that the example should provide
if [ -f "stablepay-sdk/package.json" ]; then
echo "=== stablepay-sdk/package.json (relevant fields) ==="
node -e "
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('stablepay-sdk/package.json','utf8'));
const pick=(obj)=>obj&&obj.ethers?{ethers:obj.ethers}:undefined;
console.log('dependencies:', pick(p.dependencies));
console.log('peerDependencies:', pick(p.peerDependencies));
console.log('devDependencies:', pick(p.devDependencies));
"
else
echo "stablepay-sdk/package.json not found" >&2
fi
echo "=== stablepay-sdk/example/package.json (dependencies) ==="
node -e "
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('stablepay-sdk/example/package.json','utf8'));
console.log(p.dependencies||{});
"Repository: DjedAlliance/StablePay
Length of output: 375
🏁 Script executed:
#!/bin/bash
set -euo pipefail
SRC_DIR="stablepay-sdk/example/src"
if [ ! -d "$SRC_DIR" ]; then
echo "ERROR: $SRC_DIR not found" >&2
exit 1
fi
echo "=== ethers usage in example/src ==="
MATCHES="$(rg -n --hidden --no-ignore -S \
--glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
"from ['\"]ethers['\"]|require\(\s*['\"]ethers['\"]\s*\)|\bethers\b" \
"$SRC_DIR" || true)"
if [ -z "$MATCHES" ]; then
echo "(no direct ethers imports/requires/usages found)"
else
echo "$MATCHES"
fi
echo
echo "=== ethers usage in stablepay-sdk library (best-effort) ==="
# Find likely source roots for the SDK within stablepay-sdk
# - prefer 'src' if exists; otherwise search JS/TS files under stablepay-sdk excluding example/
if [ -d "stablepay-sdk/src" ]; then
LIB_DIR="stablepay-sdk/src"
else
LIB_DIR="stablepay-sdk"
fi
MATCHES2="$(rg -n --hidden --no-ignore -S \
--glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
"\bethers\b" \
--glob '!stablepay-sdk/example/**' \
"$LIB_DIR" || true)"
if [ -z "$MATCHES2" ]; then
echo "(no ethers word found in stablepay-sdk library sources scanned)"
else
echo "$MATCHES2" | head -n 80
echo "… (truncated)"
fiRepository: DjedAlliance/StablePay
Length of output: 276
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== All occurrences of word 'ethers' under stablepay-sdk (excluding node_modules), first 80 lines ==="
rg -n --hidden --no-ignore -S "\bethers\b" stablepay-sdk \
--glob '!**/node_modules/**' \
--glob '!**/dist/**' \
--glob '!**/build/**' \
--glob '!**/.next/**' \
| head -n 80 || true
echo
echo "=== package.json files under stablepay-sdk that mention ethers (showing full matching lines) ==="
rg -n --hidden --no-ignore -S "ethers" stablepay-sdk \
--glob '**/package.json' \
--glob '!**/node_modules/**' \
--glob '!**/dist/**' \
--glob '!**/build/**' \
|| trueRepository: DjedAlliance/StablePay
Length of output: 1053
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== All occurrences of word 'ethers' under stablepay-sdk (including dist/build if present), excluding node_modules, first 80 lines ==="
rg -n --hidden --no-ignore -S "\bethers\b" stablepay-sdk \
--glob '!**/node_modules/**' \
| head -n 80 || trueRepository: DjedAlliance/StablePay
Length of output: 925
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for `@ethersproject` usage under stablepay-sdk (excluding node_modules) ==="
rg -n --hidden --no-ignore -S "`@ethersproject`" stablepay-sdk \
--glob '!**/node_modules/**' \
|| true
echo
echo "=== Search for common ethers-style symbols under stablepay-sdk (excluding node_modules) ==="
rg -n --hidden --no-ignore -S "\bJsonRpcProvider\b|\bWeb3Provider\b|\bBrowserProvider\b|\bContract\b" stablepay-sdk \
--glob '!**/node_modules/**' \
|| trueRepository: DjedAlliance/StablePay
Length of output: 9850
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== viem usage in example/src (imports/usages) ==="
rg -n --hidden --no-ignore -S \
--glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx' \
"\bviem\b|from ['\"]viem['\"]|require\(\s*['\"]viem['\"]\s*\)" \
stablepay-sdk/example/src || true
echo
echo "=== Non-example package.json files under stablepay-sdk (look for ethers/peerDeps) ==="
# List package.json (excluding example)
find stablepay-sdk -name package.json -not -path "*/example/*" | sort > /tmp/pkgjsons.txt
cat /tmp/pkgjsons.txt | sed -n '1,50p'
echo
echo "=== package.json lines containing ethers/peerDependencies for non-example packages ==="
# Search for ethers/peerDependencies only in those package.json files
if [ -s /tmp/pkgjsons.txt ]; then
rg -n --hidden --no-ignore -S "ethers|peerDependencies" -f /tmp/pkgjsons.txt --no-mmap || true
fiRepository: DjedAlliance/StablePay
Length of output: 401
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== stablepay-sdk/package.json (full) ==="
cat stablepay-sdk/package.json
echo
echo "=== viem usage in stablepay-sdk (excluding example/, node_modules), first 80 matches ==="
rg -n --hidden --no-ignore -S "\bviem\b|from ['\"]viem['\"]|require\(\s*['\"]viem['\"]\s*\)" stablepay-sdk \
--glob '!stablepay-sdk/example/**' \
--glob '!**/node_modules/**' \
| head -n 80 || true
echo
echo "=== ethers usage in stablepay-sdk (excluding example/, node_modules), first 80 matches ==="
rg -n --hidden --no-ignore -S "\bethers\b|from ['\"]ethers['\"]|require\(\s*['\"]ethers['\"]\s*\)" stablepay-sdk \
--glob '!stablepay-sdk/example/**' \
--glob '!**/node_modules/**' \
| head -n 80 || true
echo
echo "=== List all package.json under stablepay-sdk excluding example, and show ethers/viem-related dependency keys ==="
find stablepay-sdk -name package.json -not -path "*/example/*" -print | sort > /tmp/pkgjsons.txt
if [ -s /tmp/pkgjsons.txt ]; then
while IFS= read -r f; do
echo "--- $f ---"
node -e "
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('$f','utf8'));
const keys=['dependencies','peerDependencies','devDependencies','optionalDependencies'];
for (const k of keys){
if (p[k] && (p[k].ethers || p[k].viem)){
console.log(k, {ethers:p[k].ethers, viem:p[k].viem});
}
}
"
done < /tmp/pkgjsons.txt
else
echo "(no non-example package.json found)"
fiRepository: DjedAlliance/StablePay
Length of output: 50379
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== ethers usage anywhere in stablepay-sdk/example (excluding node_modules & package-lock.json) ==="
rg -n --hidden --no-ignore -S "\bethers\b|from ['\"]ethers['\"]|require\(\s*['\"]ethers['\"]\s*\)" stablepay-sdk/example \
--glob '!**/node_modules/**' \
--glob '!**/package-lock.json' \
|| true
echo
echo "=== viem usage anywhere in stablepay-sdk/example (excluding node_modules & package-lock.json), first 40 matches ==="
rg -n --hidden --no-ignore -S "\bviem\b|from ['\"]viem['\"]|require\(\s*['\"]viem['\"]\s*\)" stablepay-sdk/example \
--glob '!**/node_modules/**' \
--glob '!**/package-lock.json' \
| head -n 40 || trueRepository: DjedAlliance/StablePay
Length of output: 406
Remove unused ethers dependency from stablepay-sdk/example [minor]
stablepay-sdk/example/package.jsondeclaresethers, but there are noethersimports/usages instablepay-sdk/example/src(it only appears in the example manifest/lockfile).- The SDK/widget code uses
viemfor wallet interactions (e.g.,stablepay-sdk/src/widget/TransactionReview.jsx), so the example app shouldn’t needethersdirectly. - Drop
ethersfromstablepay-sdk/example/package.json(and updatepackage-lock.json) to avoid dependency bloat/confusion.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/example/package.json` around lines 12 - 18, Remove the unused
ethers dependency from the example app by deleting the "ethers" entry in
stablepay-sdk/example/package.json and then regenerate the lockfile
(package-lock.json) by running the package manager (e.g., npm install) inside
stablepay-sdk/example; ensure no code in stablepay-sdk/example/src imports
"ethers" (the widget uses viem, see
stablepay-sdk/src/widget/TransactionReview.jsx) before committing the updated
package.json and package-lock.json.
| const isConnectionError = error.code === -32603 || error.code === 4001 || error.code === -32005 || (error.message && (error.message.includes('CONNECTION ERROR') || error.message.includes('ERR_NAME_NOT_RESOLVED'))); | ||
| if (isConnectionError) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the exact lines in Transaction.js
sed -n '1,120p' stablepay-sdk/src/core/Transaction.js | nl -ba | sed -n '40,95p'
# Show the rest around the error handling (if present)
rg -n "isConnectionError|error\.code === 4001|CONNECTION ERROR|ERR_NAME_NOT_RESOLVED" stablepay-sdk/src/core/Transaction.js
# Find all occurrences of "=== 4001" across the repo
rg -n "code === 4001" stablepay-sdk/src | head -n 50
# Inspect the referenced wallet/transaction UI files where 4001 is likely handled
ls -la stablepay-sdk/src/contexts || true
ls -la stablepay-sdk/src/widget || true
sed -n '1,220p' stablepay-sdk/src/contexts/WalletContext.jsx | nl -ba | sed -n '1,220p'
sed -n '200,340p' stablepay-sdk/src/widget/TransactionReview.jsx | nl -ba | sed -n '200,340p'
# Search for any docs/mapping of error codes for web3/eth provider
rg -n "4001|UserRejectedRequestError|User denied" stablepay-sdk/src stablepay-sdk/package.json package.json || trueRepository: DjedAlliance/StablePay
Length of output: 109
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect Transaction.js around the relevant lines (no nl)
echo "=== Transaction.js around line 56-57 ==="
sed -n '45,80p' stablepay-sdk/src/core/Transaction.js | cat -n
echo "=== Exact occurrences of 4001 / connection-error markers in Transaction.js ==="
rg -n "isConnectionError|error\.code\s*===\s*4001|CONNECTION ERROR|ERR_NAME_NOT_RESOLVED" stablepay-sdk/src/core/Transaction.js
# 2) Find all uses of code === 4001 in the repo (for context consistency)
echo "=== All occurrences of \"code === 4001\" in stablepay-sdk/src ==="
rg -n "code\s*===\s*4001" stablepay-sdk/src | head -n 50
# 3) Inspect the referenced wallet/transaction UI files for how 4001 is classified
echo "=== WalletContext.jsx around switchToNetwork / sendTransaction error handling ==="
sed -n '1,260p' stablepay-sdk/src/contexts/WalletContext.jsx | cat -n
echo "=== TransactionReview.jsx around error handling area (near ~246) ==="
sed -n '200,340p' stablepay-sdk/src/widget/TransactionReview.jsx | cat -n
# 4) Check any error-code mapping/docs (quick grep)
echo "=== Mentions of 4001/UserRejectedRequestError in repo ==="
rg -n "4001|UserRejectedRequestError|User denied|rejected" stablepay-sdk/src | head -n 100Repository: DjedAlliance/StablePay
Length of output: 20925
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Transaction.js (top portion) ==="
sed -n '1,120p' stablepay-sdk/src/core/Transaction.js | cat -n
echo "=== Check if Transaction.js references window.ethereum ==="
rg -n "window\.ethereum|eth_requestAccounts|wallet_switchEthereumChain|wallet_addEthereumChain|UserRejectedRequestError" stablepay-sdk/src/core/Transaction.js || true
echo "=== Check what calls are inside the init try block (look for web3 init / provider calls) ==="
# Print the section from the start of the likely init/constructor around where the catch we saw is located
sed -n '120,220p' stablepay-sdk/src/core/Transaction.js | cat -n
echo "=== Search for 'RPC endpoint' wording to find any message templates ==="
rg -n "Failed to connect to .* RPC endpoint" stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 6398
Fix inconsistent handling of error code 4001
stablepay-sdk/src/core/Transaction.js includes error.code === 4001 in isConnectionError, which throws an RPC-connection failure message. In the rest of the SDK, 4001 is treated as user rejection (stablepay-sdk/src/contexts/WalletContext.jsx, stablepay-sdk/src/widget/TransactionReview.jsx), so this classification is inconsistent and can mislead if 4001 ever propagates here. Remove 4001 from the connection-error bucket (or handle it separately as user rejection).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/src/core/Transaction.js` around lines 56 - 57, The
isConnectionError check in Transaction.js incorrectly treats error.code === 4001
as an RPC connection failure; remove 4001 from the isConnectionError condition
in the isConnectionError declaration inside the Transaction handling logic and
instead handle error.code === 4001 as a user-rejection case (consistent with
WalletContext.jsx and TransactionReview.jsx), e.g., branch separately to
return/throw a user-rejection error or status so 4001 is no longer classified as
a connection error.
| <button | ||
| className={styles.walletButton} | ||
| onClick={() => window.open('https://metamask.io/download/', '_blank')} | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="stablepay-sdk/src/widget/TransactionReview.jsx"
# Basic sanity: ensure file exists and show relevant lines around 90-120
if [ ! -f "$file" ]; then
echo "Missing file: $file" >&2
exit 1
fi
echo "---- Context ($file:80-130) ----"
nl -ba "$file" | sed -n '80,130p'
echo
echo "---- Search for window.open( in repo ----"
rg -n "window\.open\(" stablepay-sdk/src/widget/TransactionReview.jsx stablepay-sdk/src || trueRepository: DjedAlliance/StablePay
Length of output: 202
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="stablepay-sdk/src/widget/TransactionReview.jsx"
# Basic sanity: ensure file exists and show relevant lines around 90-120
if [ ! -f "$file" ]; then
echo "Missing file: $file" >&2
exit 1
fi
echo "---- Context ($file:80-130) ----"
nl -ba "$file" | sed -n '80,130p'
echo
echo "---- Search for window.open( in repo ----"
rg -n "window\.open\(" stablepay-sdk/src || true
echo
echo "---- Search for rel/noopener patterns in JSX anchor tags ----"
rg -n "rel\s*=\s*{?.*noopener|noopener|noreferrer" stablepay-sdk/src || trueRepository: DjedAlliance/StablePay
Length of output: 202
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="stablepay-sdk/src/widget/TransactionReview.jsx"
if [ ! -f "$file" ]; then
echo "Missing file: $file" >&2
exit 1
fi
echo "---- Context ($file:95-110) ----"
sed -n '95,110p' "$file" | awk '{printf "%d:%s\n", NR+94, $0}'
echo
echo "---- Search for window.open( in TransactionReview.jsx ----"
rg -n "window\.open\(" "$file" || true
echo
echo "---- Search for window.open( in stablepay-sdk/src ----"
rg -n "window\.open\(" stablepay-sdk/src || true
echo
echo "---- Search for noopener/noreferrer usage in stablepay-sdk/src ----"
rg -n "noopener|noreferrer|rel\s*=\s*['\"]noopener|rel\s*=\s*['\"]noreferrer|noopener,noreferrer" stablepay-sdk/src || trueRepository: DjedAlliance/StablePay
Length of output: 1252
Harden external link opening against tabnabbing.
window.open(..., '_blank') at the click handler lacks noopener/noreferrer, allowing the opened page to control window.opener. Add the flags via the third argument.
Suggested patch
- onClick={() => window.open('https://metamask.io/download/', '_blank')}
+ onClick={() => window.open('https://metamask.io/download/', '_blank', 'noopener,noreferrer')}📝 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.
| <button | |
| className={styles.walletButton} | |
| onClick={() => window.open('https://metamask.io/download/', '_blank')} | |
| > | |
| <button | |
| className={styles.walletButton} | |
| onClick={() => window.open('https://metamask.io/download/', '_blank', 'noopener,noreferrer')} | |
| > |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/src/widget/TransactionReview.jsx` around lines 101 - 104, The
button's onClick uses window.open(url, '_blank') which is vulnerable to
tabnabbing; update the click handler in TransactionReview.jsx to call
window.open('https://metamask.io/download/', '_blank', 'noopener,noreferrer')
(or alternatively set the returned window's opener to null) so the opened page
cannot access window.opener.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
stablepay-sdk/src/styles/main.css (1)
20-29: ⚡ Quick winAdd explicit keyboard focus styling for the primary Pay CTA.
Line 20 defines hover/active states, but there’s no matching
:focus-visibletreatment. Adding one improves keyboard navigation clarity for the main action button.Suggested patch
+.stablePayButton:focus-visible { + outline: 2px solid `#f6941c`; + outline-offset: 2px; +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stablepay-sdk/src/styles/main.css` around lines 20 - 29, The .stablePayButton class has hover and active states defined but lacks explicit keyboard focus styling. Add a :focus-visible pseudo-class rule for .stablePayButton that provides clear visual feedback when the button receives keyboard focus. This styling should be distinct enough to indicate focus state for keyboard users, potentially using a combination of box-shadow, outline, or border styling similar to the hover state but tailored for accessibility.stablepay-sdk/src/styles/PricingCard.css (1)
1-10: ⚡ Quick winAdd
prefers-reduced-motionhandling for dialog and message animations.Line 1 and Line 347 introduce entrance animations, but there’s no reduced-motion fallback. Add a media query to disable non-essential animation/transition for motion-sensitive users.
Suggested patch
+@media (prefers-reduced-motion: reduce) { + .dialogOverlay, + .messageBox, + .walletButton, + .secondaryButton, + .stablePayButton { + animation: none !important; + transition: none !important; + transform: none !important; + } +}Also applies to: 347-368
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stablepay-sdk/src/styles/PricingCard.css` around lines 1 - 10, The fadeInScale keyframes animation and other entrance animations in the file lack accessibility support for users who prefer reduced motion. Add a prefers-reduced-motion media query to disable non-essential animations for motion-sensitive users. Wrap the keyframe definitions and their corresponding animation/transition properties with `@media` (prefers-reduced-motion: reduce) rules that set animation and transition to none, ensuring the animations are only applied when users have not indicated a preference for reduced motion. Apply this same pattern to both the fadeInScale animation and any other entrance animations referenced in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@stablepay-sdk/src/core/Transaction.js`:
- Around line 33-37: The isReverted check in Transaction.js is too broad on line
37. The condition checking typeof contractError?.data?.message === 'string' will
match any error with a message field, including generic network/provider
failures like timeouts or connection errors, not just actual contract reverts.
This causes users to receive misleading "invalid Djed contract" guidance when
the real issue is a network or provider error. Replace this generic message
check with a more specific condition that only matches actual contract revert
error signatures—focus on checking for specific error codes or error names that
definitively indicate a contract function revert, rather than any error that
happens to have a message string.
In `@stablepay-sdk/src/styles/main.css`:
- Line 7: Remove the quotes around the font name "Inter" in the font-family
property on line 7 of the CSS file. Change `font-family: "Inter", sans-serif;`
to `font-family: Inter, sans-serif;` to comply with Stylelint's quoting rules
for font families.
In `@stablepay-sdk/src/styles/PricingCard.css`:
- Line 107: Remove the double quotes around the font family name throughout the
PricingCard.css file to fix Stylelint violations. Change all instances of
font-family: "Inter", sans-serif; to font-family: Inter, sans-serif; (without
quotes around Inter). This issue appears at lines 107, 133, 151, 210, 218, 243,
301, 329, 341, 353, and 410. Font family names that are single words or contain
only alphanumeric characters do not require quotes according to CSS standards
and Stylelint rules, so removing the quotes will resolve all violations.
---
Nitpick comments:
In `@stablepay-sdk/src/styles/main.css`:
- Around line 20-29: The .stablePayButton class has hover and active states
defined but lacks explicit keyboard focus styling. Add a :focus-visible
pseudo-class rule for .stablePayButton that provides clear visual feedback when
the button receives keyboard focus. This styling should be distinct enough to
indicate focus state for keyboard users, potentially using a combination of
box-shadow, outline, or border styling similar to the hover state but tailored
for accessibility.
In `@stablepay-sdk/src/styles/PricingCard.css`:
- Around line 1-10: The fadeInScale keyframes animation and other entrance
animations in the file lack accessibility support for users who prefer reduced
motion. Add a prefers-reduced-motion media query to disable non-essential
animations for motion-sensitive users. Wrap the keyframe definitions and their
corresponding animation/transition properties with `@media`
(prefers-reduced-motion: reduce) rules that set animation and transition to
none, ensuring the animations are only applied when users have not indicated a
preference for reduced motion. Apply this same pattern to both the fadeInScale
animation and any other entrance animations referenced in the file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 794af35f-e93d-4e77-9a7a-f64589318181
⛔ Files ignored due to path filters (8)
.DS_Storeis excluded by!**/.DS_Storestablepay-sdk/dist/assets/stablepay-logo.svgis excluded by!**/dist/**,!**/*.svgstablepay-sdk/dist/esm/index.jsis excluded by!**/dist/**stablepay-sdk/dist/esm/styles.cssis excluded by!**/dist/**stablepay-sdk/dist/umd/index.jsis excluded by!**/dist/**stablepay-sdk/dist/umd/index.js.mapis excluded by!**/dist/**,!**/*.mapstablepay-sdk/dist/umd/styles.cssis excluded by!**/dist/**stablepay-sdk/src/assets/stablepay-logo.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
stablepay-sdk/example/eslint.config.jsstablepay-sdk/example/src/App.jsxstablepay-sdk/rollup.config.mjsstablepay-sdk/src/core/Transaction.jsstablepay-sdk/src/styles/PricingCard.cssstablepay-sdk/src/styles/main.cssstablepay-sdk/src/widget/TransactionReview.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
- stablepay-sdk/example/eslint.config.js
- stablepay-sdk/src/widget/TransactionReview.jsx
| const isReverted = | ||
| contractError?.code === -32000 || | ||
| contractError?.code === 3 || | ||
| contractError?.name === 'ContractFunctionRevertedError' || | ||
| typeof contractError?.data?.message === 'string'; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For viem v2.21.x and common EIP-1193 providers, which error fields/codes reliably indicate a contract execution revert vs generic RPC/provider failure?
💡 Result:
In viem v2.21.x, distinguishing between a contract execution revert and a generic RPC/provider failure should not be done by manually checking raw RPC error codes (which vary significantly between providers). Instead, you should rely on viem's structured error hierarchy and the.walk method, which traverses the error chain to identify the root cause [1]. To reliably determine if a contract reverted, catch the error, verify it is a BaseError, and use.walk to check if the error chain includes a ContractFunctionRevertedError [1][2]. Example implementation: import { BaseError, ContractFunctionRevertedError } from 'viem'; try { // Your contract call (e.g., simulateContract, readContract, writeContract) await publicClient.simulateContract({... }); } catch (err) { if (err instanceof BaseError) { // Walk the error chain to find the specific revert error const revertError = err.walk((e) => e instanceof ContractFunctionRevertedError); if (revertError instanceof ContractFunctionRevertedError) { // Contract execution reverted. // If ABI is provided, you can access decoded data: console.log('Error Name:', revertError.data?.errorName); console.log('Args:', revertError.data?.args); } else { // Generic RPC/provider failure (e.g., network issues, unauthorized, etc.) console.log('Generic RPC failure:', err.shortMessage); } } } Key concepts: 1. Structured Errors: Viem wraps all errors in BaseError classes [1]. These classes preserve the original error cause, allowing you to drill down into the chain [1][3]. 2. The.walk Method: This is the recommended way to inspect error chains [1][4]. It allows you to check for specific error types, such as ContractFunctionRevertedError (for reverts) or InsufficientFundsError (for pre-transaction validation errors) [1]. 3. EIP-1193 Context: While EIP-1193 defines standard provider errors (e.g., 4001 for User Rejected Request, 4100 for Unauthorized) [5][6], contract reverts are typically returned as RPC-level exceptions (often code -32000 or -32603) with data payloads [4][7]. Viem abstracts these inconsistencies by normalizing them into its own internal error hierarchy, making manual checks of RPC codes unnecessary and error-prone [1]. For decoding custom Solidity errors within the revert, ensure you pass the contract ABI to your call; viem will then automatically populate the errorName and args fields if the revert data matches a defined ABI error [8][1][2].
Citations:
- 1: Structured errors for programmatic error handling wevm/viem#4281
- 2: https://viem-iyn6kgwt-wxqdoit.4everland.app/docs/contract/simulateContract.html
- 3: wevm/viem@60f701f
- 4: Improve error messages wevm/viem#581
- 5: https://eips.ethereum.org/EIPS/eip-1193
- 6: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md
- 7: https://github.com/MetaMask/eth-json-rpc-errors
- 8: https://viem.sh/docs/contract/decodeErrorResult
🏁 Script executed:
find . -name "Transaction.js" -type f | head -5Repository: DjedAlliance/StablePay
Length of output: 106
🏁 Script executed:
sed -n '20,50p' stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 1709
🏁 Script executed:
head -20 stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 834
🏁 Script executed:
cat stablepay-sdk/package.jsonRepository: DjedAlliance/StablePay
Length of output: 1061
🏁 Script executed:
grep -r "import.*viem" stablepay-sdk/src --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"Repository: DjedAlliance/StablePay
Length of output: 452
🏁 Script executed:
grep -B5 -A10 "contractError" stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 2196
🏁 Script executed:
grep -r "djed-sdk" . --include="package.json" -A5 -B5 | head -30Repository: DjedAlliance/StablePay
Length of output: 909
🏁 Script executed:
cat djed-sdk/package.jsonRepository: DjedAlliance/StablePay
Length of output: 609
🏁 Script executed:
cat djed-sdk/src/index.js | head -50Repository: DjedAlliance/StablePay
Length of output: 308
🏁 Script executed:
cat djed-sdk/src/web3.jsRepository: DjedAlliance/StablePay
Length of output: 402
🏁 Script executed:
grep -r "ContractFunctionRevertedError\|BaseError" stablepay-sdk/src --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"Repository: DjedAlliance/StablePay
Length of output: 319
🏁 Script executed:
sed -n '33,50p' stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 1134
🏁 Script executed:
sed -n '20,65p' stablepay-sdk/src/core/Transaction.jsRepository: DjedAlliance/StablePay
Length of output: 2577
🏁 Script executed:
cat stablepay-sdk/src/widget/TransactionReview.jsx | grep -A10 -B10 "ContractFunctionRevertedError"Repository: DjedAlliance/StablePay
Length of output: 863
Narrow revert detection on Line 37 to actual revert signatures.
The check typeof contractError?.data?.message === 'string' is overly broad. Any error with a message field—including generic RPC/provider failures (network timeouts, connection issues, provider rejections)—will trigger the revert-handling path, causing misleading "invalid Djed contract" guidance to users when the real issue is a network or provider error.
Suggested patch
- const isReverted =
- contractError?.code === -32000 ||
- contractError?.code === 3 ||
- contractError?.name === 'ContractFunctionRevertedError' ||
- typeof contractError?.data?.message === 'string';
+ const message = contractError?.data?.message || contractError?.shortMessage || contractError?.message || '';
+ const isReverted =
+ contractError?.code === 3 ||
+ contractError?.name === 'ContractFunctionRevertedError' ||
+ /\brevert(ed)?\b|execution reverted/i.test(String(message));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/src/core/Transaction.js` around lines 33 - 37, The isReverted
check in Transaction.js is too broad on line 37. The condition checking typeof
contractError?.data?.message === 'string' will match any error with a message
field, including generic network/provider failures like timeouts or connection
errors, not just actual contract reverts. This causes users to receive
misleading "invalid Djed contract" guidance when the real issue is a network or
provider error. Replace this generic message check with a more specific
condition that only matches actual contract revert error signatures—focus on
checking for specific error codes or error names that definitively indicate a
contract function revert, rather than any error that happens to have a message
string.
| border: 1px solid #e6ebf1; | ||
| border-radius: 12px; | ||
| color: #1a1f36; | ||
| font-family: "Inter", sans-serif; |
There was a problem hiding this comment.
Resolve Stylelint error on font-family quoting.
Line 7 uses "Inter" with quotes and is flagged by Stylelint in this change set.
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 7-7: Expected no quotes around "Inter" (font-family-name-quotes)
(font-family-name-quotes)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/src/styles/main.css` at line 7, Remove the quotes around the
font name "Inter" in the font-family property on line 7 of the CSS file. Change
`font-family: "Inter", sans-serif;` to `font-family: Inter, sans-serif;` to
comply with Stylelint's quoting rules for font families.
Source: Linters/SAST tools
| } | ||
|
|
||
| .stablepayTitle { | ||
| font-family: "Inter", sans-serif; |
There was a problem hiding this comment.
Fix repeated Stylelint violations for quoted font family names.
Line 107 (and repeated at Lines 133, 151, 210, 218, 243, 301, 329, 341, 353, 410) uses "Inter" with quotes, which is currently failing lint in this PR context.
Also applies to: 133-133, 151-151, 210-210, 218-218, 243-243, 301-301, 329-329, 341-341, 353-353, 410-410
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 107-107: Expected no quotes around "Inter" (font-family-name-quotes)
(font-family-name-quotes)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stablepay-sdk/src/styles/PricingCard.css` at line 107, Remove the double
quotes around the font family name throughout the PricingCard.css file to fix
Stylelint violations. Change all instances of font-family: "Inter", sans-serif;
to font-family: Inter, sans-serif; (without quotes around Inter). This issue
appears at lines 107, 133, 151, 210, 218, 243, 301, 329, 341, 353, and 410. Font
family names that are single words or contain only alphanumeric characters do
not require quotes according to CSS standards and Stylelint rules, so removing
the quotes will resolve all violations.
Source: Linters/SAST tools
Addressed Issues:
Fixes #60, #61 and #62
Screenshots/Recordings:
Case 1: User has a wallet
has_wallet.mp4
Case 2: User has no wallet
no_wallet.mp4
Additional Notes:
Description
This Pull Request introduces a comprehensive example application to demonstrate SDK integration and significantly improves the UI/UX and validation flow of the StablePay SDK widgets.
Why a new example website?
A fully fledged example website repo exists (https://github.com/DjedAlliance/StablePay-MerchantWebsiteDemo), but it does not update stable-sdk in real time. We need to update stablepay-sdk on npm to get the latest build in that example website. I have made this example website within the SDK to show widgets and a user flow that updates automatically with every local SDK build.
Changes made:
Checklist
AI Usage Disclosure
Check one of the checkboxes below:
Summary by CodeRabbit