invoice request link when visitor has no wallet (fixes #63)#87
invoice request link when visitor has no wallet (fixes #63)#87kumawatkaran523 merged 3 commits intoStabilityNexus:mainfrom
Conversation
…Nexus#63) We now use the chain from the URL for the token list and for verification (via public RPC), so the create-invoice form works in incognito or without a wallet. Amount and description from the link are pre-filled when the first line is empty.
📝 WalkthroughWalkthroughAdds a dual-path token verification flow: if a public RPC exists for a target chainId use a JsonRpcProvider; otherwise fall back to the wallet BrowserProvider. Introduces Changes
Sequence DiagramsequenceDiagram
participant UI as CreateInvoice UI
participant Comp as CreateInvoice Component
participant Selector as RPC Selector
participant Public as JsonRpcProvider
participant Wallet as BrowserProvider
participant Token as ERC20 Contract
UI->>Comp: request verifyToken(address, chainIdForRpc)
Comp->>Selector: lookup CHAIN_ID_TO_PUBLIC_RPC(chainIdForRpc)
alt public RPC found
Selector->>Public: instantiate JsonRpcProvider(rpcUrl)
Public->>Token: call name/symbol/decimals
Token-->>Public: token metadata
Public-->>Comp: metadata
else no public RPC
Selector->>Wallet: use BrowserProvider(wallet)
Wallet->>Token: call name/symbol/decimals
Token-->>Wallet: token metadata
Wallet-->>Comp: metadata
end
Comp->>Comp: validate & update token state
Comp-->>UI: render token details / errors
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/page/CreateInvoice.jsx (1)
894-902: MissingchainIdForRpcparameter breaks custom token verification without wallet.When users manually enter a custom token address,
verifyToken(address)is called without the chain ID parameter. This means visitors without a connected wallet cannot verify manually-entered tokens, as the function will fall through to the error state.🐛 Proposed fix
onChange={(e) => { const address = e.target.value; setCustomTokenAddress(address); if (!address || !ethers.isAddress(address)) { setTokenVerificationState("idle"); setVerifiedToken(null); } else if (ethers.isAddress(address)) { - verifyToken(address); + verifyToken(address, chainIdForTokens); } }}
🤖 Fix all issues with AI agents
In `@frontend/src/page/CreateInvoice.jsx`:
- Around line 119-138: The issue is that pre-filled item values are not shown
because the Input components are uncontrolled—add controlled value props bound
to itemData so inputs reflect state; update all item inputs (description, qty,
unitPrice, discount, tax) in both mobile and desktop layouts to set value from
itemData[index].<field> (e.g., value derived from itemData[index].description,
.qty, .unitPrice, .discount, .tax) and keep existing onChange handlers
(handleItemData) and the setItemData logic intact so the pre-filled state is
visible and editable.
- Around line 289-296: The current Promise.all with .catch fallbacks can mark
non-ERC20 addresses as verified; change the logic in the token verification
block so you first attempt to call contract.symbol() (and await it without
swallowing errors) and only if symbol resolves to a non-empty string proceed to
fetch name() and decimals() and call setVerifiedToken({ address, symbol, name,
decimals }) and setTokenVerificationState("success"); if symbol throws or
returns an empty/whitespace string, setTokenVerificationState("error" or
"invalid") and do not call setVerifiedToken. Use the existing contract.symbol(),
contract.name(), contract.decimals(), setVerifiedToken and
setTokenVerificationState identifiers to locate and update the code.
🧹 Nitpick comments (1)
frontend/src/page/CreateInvoice.jsx (1)
53-61: Public RPC endpoints may have availability/rate-limit constraints.The hardcoded RPCs are reasonable for read-only verification, but consider:
- These endpoints may have rate limits or downtime
- Missing coverage for some popular chains (Arbitrum, Optimism, Avalanche)
For improved resilience, consider adding fallback RPCs or using a public RPC aggregator service in the future.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/page/CreateInvoice.jsx (1)
901-913: Pass chainIdForTokens to custom-token verification.Manual custom-token entry currently calls
verifyToken(address)without the chainId, which forces a wallet-based path and breaks the no-wallet flow. This prevents verification in incognito/no-wallet scenarios.✅ Proposed fix
} else if (ethers.isAddress(address)) { - verifyToken(address); + verifyToken(address, chainIdForTokens); }
🤖 Fix all issues with AI agents
In `@frontend/src/page/CreateInvoice.jsx`:
- Around line 112-161: In verifyToken, clear any previously verified token
immediately when beginning a new check and also on failure: call
setVerifiedToken(null) right after setTokenVerificationState("verifying") to
avoid stale data, and ensure setVerifiedToken(null) is also invoked in the error
paths (both the catch block and the branches that
setTokenVerificationState("error")) before returning; reference the verifyToken
function and the state setters setTokenVerificationState and setVerifiedToken
when applying this change.
🧹 Nitpick comments (1)
frontend/src/page/CreateInvoice.jsx (1)
53-61: Consider centralizing public RPC endpoints.Embedding RPC URLs in this component makes updates harder and requires a frontend deploy for endpoint changes. Consider moving
CHAIN_ID_TO_PUBLIC_RPCto a shared config/module (or env-backed config) to improve maintainability.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/page/CreateInvoice.jsx (1)
892-904: MissingchainIdForTokensargument in manual token verification.When a user manually enters a custom token address,
verifyToken(address)is called without thechainIdForTokensparameter. This means public RPC won't be used, and verification will fail for users without a connected wallet. Other call sites (lines 186, 223, 229) correctly passchainIdForTokens.🐛 Proposed fix
} else if (ethers.isAddress(address)) { - verifyToken(address); + verifyToken(address, chainIdForTokens); }
🧹 Nitpick comments (3)
frontend/src/page/CreateInvoice.jsx (3)
53-61: Consider resilience for public RPC endpoints.Hardcoded public RPC URLs may become unavailable or rate-limited. Consider adding fallback endpoints or graceful degradation when the primary RPC fails.
197-207: Consider using public RPC for decimals fetch.This code path uses
BrowserProviderdirectly to fetch missing decimals, which won't work for wallet-less visitors. While there's a fallback toverifyToken(which uses public RPC), using public RPC here would be more consistent with the PR's goal.♻️ Suggested improvement
if (decimals === undefined || decimals === null) { try { - if (typeof window !== "undefined" && window.ethereum) { - const provider = new BrowserProvider(window.ethereum); - const contract = new ethers.Contract(urlTokenAddress, ERC20_ABI, provider); - decimals = await contract.decimals(); - } + const rpcUrl = CHAIN_ID_TO_PUBLIC_RPC[chainIdForTokens]; + let provider; + if (rpcUrl) { + provider = new JsonRpcProvider(rpcUrl); + } else if (typeof window !== "undefined" && window.ethereum) { + provider = new BrowserProvider(window.ethereum); + } + if (provider) { + const contract = new ethers.Contract(urlTokenAddress, ERC20_ABI, provider); + decimals = await contract.decimals(); + } } catch (err) { console.warn("Failed to fetch decimals for preselected token:", err); } }
1122-1127: Consider using pre-computedamountfrom state.The disabled amount field recalculates the value inline using
parseFloat, which may produce different results than thehandleItemDatacalculation (which usesparseUnitsfor precision). For consistency, use the pre-computeditemData[index].amount.♻️ Suggested change
<Input type="text" placeholder="0.00" className="w-full bg-gray-100 border-gray-300 text-gray-700 font-semibold" name="amount" disabled - value={String( - (parseFloat(itemData[index]?.qty) || 0) * - (parseFloat(itemData[index]?.unitPrice) || 0) - - (parseFloat(itemData[index]?.discount) || 0) + - (parseFloat(itemData[index]?.tax) || 0) - )} + value={itemData[index]?.amount ?? "0"} />Apply the same change to the desktop layout amount field (lines 1219-1224).
When a user opens a “Generate Invoice Request” link without a connected wallet , the Create Invoice page now uses the chain from the URL for the token list and for token verification via public RPC, so the form loads and verifies the token correctly.
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.