Feat/starknet clean#474
Conversation
…main - Restore Starknet routes (create-wallet, get-public-key, transfer, create-order), context, lib, and STRK logo - Wire balance, transfer, transaction flows, network UI, and Privy wallet API helpers - Cast EVM viem Chain where migration/network lists still union with custom Starknet chain id type Made-with: Cursor
…d improved error handling - Added validation for the AGGREGATOR_URL to ensure it is set and properly formatted. - Improved error logging to provide more context on network issues when fetching tokens from the API. - Updated the API request to use a constructed URL for better clarity and maintainability.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds end-to-end Starknet support: new env vars, Starknet RPC/paymaster config, Privy raw-sign + Ready-account utilities, server APIs for wallet/public-key/transfer/create-order, Starknet React context/hooks, unified balance plumbing, UI wiring, and package/test updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client/Browser
participant UI as TransactionPreview
participant API as /api/starknet/create-order
participant Privy as Privy Wallet API
participant RPC as Starknet RPC / Paymaster
participant SC as Starknet Smart Contracts
rect rgba(200,200,255,0.5)
Client->>UI: Initiate order (JWT + params)
UI->>API: POST /api/starknet/create-order
API->>API: verifyJWT -> extract userId
API->>Privy: getStarknetWallet(walletId) -> publicKey
API->>RPC: getClassHashAt / check deployed
API->>RPC: setupPaymaster()
API->>RPC: buildReadyAccount(publicKey, classHash, paymaster)
API->>RPC: prepare calls (approve, create_order)
alt account not deployed
API->>RPC: deployReadyAccount (exec + deploy)
else deployed
API->>RPC: account.executePaymasterTransaction(calls)
end
RPC->>SC: execute approve & create_order
SC-->>RPC: txHash + events
API->>RPC: waitForTransaction/scan events -> extract orderId
API-->>Client: { success, transactionHash, orderId }
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.env.example (1)
110-111:⚠️ Potential issue | 🟡 MinorDuplicate
BREVO_LIST_IDentry.Line 111 duplicates line 110. This appears to be an unintentional copy-paste error.
🧹 Proposed fix
BREVO_LIST_ID= -BREVO_LIST_ID= # Brevo Conversations (Chat Widget)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.example around lines 110 - 111, There is a duplicate environment variable entry for BREVO_LIST_ID (appears twice); remove the extra line so only one BREVO_LIST_ID= entry remains in the .env.example and verify there are no other accidental duplicate environment keys in the file.
🧹 Nitpick comments (16)
app/context/TokensContext.tsx (1)
56-62: Misleading comment: Starknet is not handled "above" in this function.The comment states "Skip Starknet since we already handled it above" but there's no Starknet handling above in
refreshTokens(). Per the context snippets, Starknet tokens are intentionally excluded here because they're handled separately bygetNetworkTokens()inBalanceContext.📝 Suggested comment fix
// Merge fallback tokens for any networks missing from API response Object.keys(FALLBACK_TOKENS).forEach((networkName) => { - // Skip Starknet since we already handled it above + // Skip Starknet - its tokens are handled separately via getNetworkTokens() in BalanceContext if (networkName === "Starknet") return;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/context/TokensContext.tsx` around lines 56 - 62, Update the misleading inline comment in refreshTokens(): instead of saying "Skip Starknet since we already handled it above", change it to explain that Starknet tokens are intentionally excluded here because Starknet is handled separately by getNetworkTokens() in BalanceContext; reference FALLBACK_TOKENS and the networkName === "Starknet" check so future readers know why the branch skips Starknet and where to find its handling.app/hooks/useCNGNRate.ts (1)
197-204: Misleading comment: Starknet mainnet is also excluded.The comment states "testnet, not supported by aggregator" but based on the PR objectives adding Starknet mainnet support, this exclusion applies to all Starknet networks. The comment should reflect the actual reason for exclusion.
📝 Suggested comment fix
- // Skip rate fetching for Starknet networks (testnet, not supported by aggregator) + // Skip rate fetching for Starknet networks (not yet supported by aggregator) if (network.toLowerCase().includes("starknet")) { - console.log("Skipping rate fetch for Starknet network:", network);Also consider removing or reducing the log level of the
console.logstatement, as it will fire on every render for Starknet users.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/hooks/useCNGNRate.ts` around lines 197 - 204, Update the misleading comment to state that all Starknet networks (including mainnet) are excluded because the aggregator doesn’t support Starknet, and keep the existing early-return behavior in the useCNGNRate hook (the network variable check that calls setRate(null), setError(null), and setIsLoading(false) then returns). Replace the noisy console.log with a lower-level log (e.g., console.debug) or remove it entirely so it doesn’t fire on every render for Starknet users, or gate it behind a dev/verbose flag.app/pages/TransactionForm.tsx (1)
160-164: Consider centralizing Starknet network detection.The inline
"Starknet"string check works, but a shared helper/constant would reduce drift across files (TransactionForm,TransactionStatus, etc.).🧹 Minimal cleanup option
+const isStarknetNetwork = selectedNetwork.chain.name === "Starknet"; + const activeBalance = isInjectedWallet ? injectedWalletBalance - : selectedNetwork.chain.name === "Starknet" + : isStarknetNetwork ? starknetWalletBalance : shouldUseEOA ? externalWalletBalance : smartWalletBalance;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/pages/TransactionForm.tsx` around lines 160 - 164, Centralize the Starknet detection by replacing the literal string check in TransactionForm (selectedNetwork.chain.name === "Starknet") with a shared helper/constant (e.g., isStarknetNetwork(network) or NETWORKS.STARKNET) used across TransactionForm, TransactionStatus, etc.; add the helper to a common utilities/constants module, update usages to call isStarknetNetwork(selectedNetwork) (or compare against NETWORKS.STARKNET) and export it for reuse so all components rely on the single source of truth.app/components/WalletTransferApprovalModal.tsx (1)
39-42: Prefer guarded EVM-chain narrowing over blanketas Chaincasts.The current casts work today but remove compile-time/runtime safeguards. A small guard makes this path safer if
migrationChecklistNetworksever changes.♻️ Proposed hardening
-const CHAIN_MAP = Object.fromEntries( - migrationChecklistNetworks.map((n) => [n.chain.name, n.chain]), -) as Record<string, Chain>; +const asEvmChain = (chain: unknown): Chain | null => { + const id = (chain as { id?: unknown } | undefined)?.id; + return typeof id === "number" ? (chain as Chain) : null; +}; + +const CHAIN_MAP: Record<string, Chain> = Object.fromEntries( + migrationChecklistNetworks + .map((n) => [n.chain.name, asEvmChain(n.chain)] as const) + .filter(([, chain]) => chain !== null) + .map(([name, chain]) => [name, chain as Chain]), +); ... -const evmChain = network.chain as Chain; +const evmChain = asEvmChain(network.chain); +if (!evmChain) continue;Also applies to: 99-103
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/WalletTransferApprovalModal.tsx` around lines 39 - 42, The CHAIN_MAP construction bluntly casts entries to Chain; replace it with a guarded narrow: add a type-guard (e.g., isValidChain or isEvmChain) that checks the properties you expect on n.chain, then build CHAIN_MAP from migrationChecklistNetworks.filter(n => isValidChain(n.chain)).map(n => [n.chain.name, n.chain]) and pass the result to Object.fromEntries so only validated Chain objects are included; update the same pattern used at the other block (around the 99-103 area) to use the same type-guard + filter approach and avoid unchecked `as Chain` casts on migrationChecklistNetworks and CHAIN_MAP.app/api/starknet/create-wallet/route.ts (1)
32-39: Redundant userId validation.The
userIdis directly assigned fromauthUserIdon line 32, which was already validated as truthy on lines 25-30. The subsequent check on lines 34-39 will never trigger sinceauthUserIdbeing falsy would have already returned a 401 response.🧹 Remove redundant check
- const userId = authUserId; - - if (!userId) { - return NextResponse.json( - { error: "No user ID available" }, - { status: 400 }, - ); - } + const userId = authUserId; // First, check if user already has a Starknet wallet🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-wallet/route.ts` around lines 32 - 39, Remove the redundant userId validation: since authUserId is already checked earlier (the 25-30 block returns 401 if falsy), delete the assignment/guard that sets const userId = authUserId and the subsequent if (!userId) { return NextResponse.json(...) } block in route.ts; instead use authUserId directly where userId was used (or keep a simple alias const userId = authUserId without re-checking) so there is no unreachable validation code.app/api/starknet/get-public-key/route.ts (1)
49-51: Ownership verification could be more specific about wallet chain type.The current check only verifies
account.type === "wallet"andaccount.id === walletId. For consistency withcreate-wallet/route.ts(which checks forchainType === "starknet"), consider also verifying the wallet is a Starknet wallet.However, since the walletId is unique and the endpoint is specifically for Starknet public keys, this is a minor consideration rather than a security issue.
💡 Optional: Add chain type verification
const ownsWallet = linkedAccounts.find( (account: any) => account.id === walletId && account.type === "wallet" + && (account.chainType === "starknet" || account.chain_type === "starknet") );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/get-public-key/route.ts` around lines 49 - 51, The ownership check using linkedAccounts.find(...) currently only matches account.id === walletId and account.type === "wallet"; update this to also verify account.chainType === "starknet" (or the same chainType string used in create-wallet's check) so ownsWallet only matches Starknet wallets; modify the predicate passed to linkedAccounts.find to include the chainType check and keep the existing id and type checks (symbols: linkedAccounts, ownsWallet, walletId, account.chainType).app/utils.ts (2)
289-290: Starknet RPC URL returnsundefinedif env var is not set.Unlike other networks which construct URLs with optional
rpcUrlKey, the Starknet case directly returns the environment variable which could beundefined. Callers should handle this case.The
createStarknetRpcProviderinapp/lib/starknetRpc.tsacceptsstring | undefined, so this is handled, but explicit validation infetchStarknetBalancewould be more defensive.💡 Add explicit RPC URL validation in fetchStarknetBalance
export async function fetchStarknetBalance( address: string, tokens: Token[], ): Promise<{ total: number; balances: Record<string, number>; balancesUsd: Record<string, number>; }> { if (!address || !tokens || tokens.length === 0) { return { total: 0, balances: {}, balancesUsd: {} }; } try { const { createStarknetRpcProvider } = await import("./lib/starknetRpc"); const rpcUrl = process.env.NEXT_PUBLIC_STARKNET_RPC_URL; + if (!rpcUrl) { + console.warn("NEXT_PUBLIC_STARKNET_RPC_URL is not configured"); + return { total: 0, balances: {}, balancesUsd: {} }; + } const provider = createStarknetRpcProvider(rpcUrl);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/utils.ts` around lines 289 - 290, The Starknet RPC URL can be undefined because app/utils.ts returns process.env.NEXT_PUBLIC_STARKNET_RPC_URL directly; add an explicit validation in fetchStarknetBalance (in app/lib/starknetRpc.ts) to check the rpcUrl before using it: if rpcUrl is undefined or empty, return a controlled error/early failure (or throw) with a clear message mentioning NEXT_PUBLIC_STARKNET_RPC_URL, otherwise proceed to call createStarknetRpcProvider and continue as normal; this makes the caller-side behavior deterministic even though createStarknetRpcProvider accepts string | undefined.
628-665: CoinGecko price fetching may hit rate limits.The
fetchTokenPricefunction calls CoinGecko's public API without any rate limiting or caching. InfetchStarknetBalance, this is called for each token in parallel, which could lead to rate limiting errors (CoinGecko's public API has strict limits).Consider:
- Batching price requests using CoinGecko's multi-coin endpoint
- Adding a simple in-memory cache for prices
- Handling 429 (rate limit) responses specifically
💡 Batch price fetching to reduce API calls
async function fetchTokenPrices(tokenSymbols: string[]): Promise<Record<string, number | null>> { const coinGeckoIds: Record<string, string> = { ETH: "ethereum", WETH: "ethereum", USDC: "usd-coin", USDT: "tether", DAI: "dai", WBTC: "wrapped-bitcoin", }; const ids = tokenSymbols .map(s => coinGeckoIds[s.toUpperCase()]) .filter(Boolean); if (ids.length === 0) return {}; try { const response = await fetch( `https://api.coingecko.com/api/v3/simple/price?ids=${ids.join(',')}&vs_currencies=usd` ); if (!response.ok) return {}; const data = await response.json(); const result: Record<string, number | null> = {}; for (const symbol of tokenSymbols) { const id = coinGeckoIds[symbol.toUpperCase()]; result[symbol] = id ? (data[id]?.usd ?? null) : null; } return result; } catch { return {}; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/utils.ts` around lines 628 - 665, The current fetchTokenPrice function can trigger CoinGecko rate limits; replace per-token calls with a batched multi-coin fetch and add an in-memory cache + TTL and 429 handling: implement a new fetchTokenPrices(tokenSymbols: string[]) that maps symbols to CoinGecko IDs, queries /simple/price with ids=comma-separated, stores results in a simple in-memory map keyed by symbol with an expiry (e.g., 1–5 minutes), and returns a Record<string, number|null>; modify fetchStarknetBalance to call fetchTokenPrices once for all tokens instead of calling fetchTokenPrice per token; in fetchTokenPrices specifically detect response.status === 429 and perform an exponential backoff retry (or return cached values if available) and ensure any network or non-200 responses return nulls for those tokens.app/context/StarknetContext.tsx (1)
134-208: Return type mismatch betweencreateWalletimplementation and interface.The
createWalletfunction returnsPromise<string>(line 134), but theStarknetContextTypeinterface declarescreateWallet: () => Promise<void>(line 22). ThecreateWalletWrapper(lines 282-284) exists to bridge this gap, but this creates a confusing internal API.Consider aligning the interface with the implementation or documenting why the internal function returns the walletId while the exposed function doesn't.
💡 Option 1: Update interface to reflect actual behavior
interface StarknetContextType extends StarknetWalletState { - createWallet: () => Promise<void>; + createWallet: () => Promise<string | null>; resetError: () => void; refreshWalletState: () => Promise<void>; ensureWalletExists: () => Promise<void>; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/context/StarknetContext.tsx` around lines 134 - 208, createWallet currently returns Promise<string> but StarknetContextType declares createWallet: () => Promise<void>, causing a type mismatch; update the API to be consistent by either (A) changing StarknetContextType.createWallet to () => Promise<string> so the context type reflects the actual returned walletId (reference: StarknetContextType, createWallet), or (B) change createWallet to return Promise<void> and move walletId return into createWalletWrapper so callers only get void (reference: createWalletWrapper). Pick one option and update all usages/signatures accordingly to eliminate the mismatch.app/hooks/useSmartWalletTransfer.ts (1)
145-152: Best-effort wallet deployment with hardcoded delay.The 1-second delay after
ensureWalletExists()is a timing workaround to allow deployment propagation. While the comment acknowledges this is best-effort, this pattern is fragile:
- The delay may not be sufficient in all network conditions
- The empty catch block silently swallows all errors
Consider documenting this behavior more explicitly or implementing a retry/polling mechanism in the future.
💭 Future improvement: polling for deployment status
A more robust approach would poll the deployment status rather than using a fixed delay. However, the current implementation is acceptable for initial integration since the API handles undeployed accounts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/hooks/useSmartWalletTransfer.ts` around lines 145 - 152, The hardcoded 1s delay after ensureWalletExists() is fragile and the empty catch swallows errors; replace the static sleep with a short polling loop that checks the deployment flag (starknetWallet.deployed) or a provider/deployment-status helper until either deployed or a max timeout is reached (e.g., 5–10s) with a small interval/backoff between checks, and in the catch block log or surface the error instead of silently ignoring it; update ensureWalletExists()/the surrounding block to implement this polling and a bounded timeout so transfers won’t wait indefinitely.app/api/starknet/create-order/route.ts (4)
256-262: Magic string for event key matching.The hardcoded event key
"0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3"should be extracted to a named constant with documentation explaining what event it represents (likelyOrderCreatedevent selector).Proposed fix
+// Selector for the OrderCreated event from the gateway contract +const ORDER_CREATED_EVENT_KEY = "0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3"; // ... later in the code ... rawEvents.forEach((event) => { if ( Object.values(event.keys).includes( - "0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3", + ORDER_CREATED_EVENT_KEY, ) ) { orderId = event.data[2]; } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-order/route.ts` around lines 256 - 262, Replace the hardcoded event selector string used in the event key check with a named constant (e.g., ORDER_CREATED_SELECTOR) documented to state that it is the StarkNet selector for the OrderCreated event; update the check that uses Object.values(event.keys).includes(...) to compare against ORDER_CREATED_SELECTOR and ensure the constant is exported/defined near the top of route.ts with a brief comment describing its meaning and source (OrderCreated event selector) so references like orderId = event.data[2] remain unchanged but now use the named constant.
114-114: Redundant public key fetch.The
publicKeyis destructured from the request body (line 40), but thengetStarknetWalletis called to fetchwalletPublicKey(line 114) which is used instead. Either remove the unusedpublicKeyfrom the request body validation, or use the provided one and only fetch from Privy as a fallback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-order/route.ts` at line 114, The request body validation currently destructures publicKey but the code always overwrites it by calling getStarknetWallet(walletId) to obtain walletPublicKey; either remove publicKey from the request body schema/validation and stop expecting it from the client, or change the logic in route.ts to prefer the client-supplied publicKey (use the destructured publicKey) and only call getStarknetWallet(walletId) to populate walletPublicKey as a fallback when publicKey is missing/empty; update any references to walletPublicKey/publicKey accordingly (e.g., the const { publicKey: walletPublicKey } = await getStarknetWallet(walletId) usage) so there is a single source of truth.
246-248: Remove hardcoded constant.
const wait = truefollowed byif (wait)is effectively dead code structure. The condition always evaluates to true.Proposed fix
let orderId; // Wait for transaction confirmation - const wait = true; - if (wait) { - try { + try { const txReceipt = await account.waitForTransaction( result.transaction_hash, ); // ... rest of logic - } catch (error) { - console.log( - "[API] Warning: Could not confirm transaction, but it may still succeed", - ); - } + } catch (error) { + console.log( + "[API] Warning: Could not confirm transaction, but it may still succeed", + ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-order/route.ts` around lines 246 - 248, Remove the hardcoded "const wait = true" and the always-true "if (wait)" branch: read a real boolean (e.g., from the incoming request body/query or a config/feature-flag) and use that value in the existing conditional where "wait" is referenced so the code path becomes conditional in practice; update any variable name if needed (e.g., request.wait or options.wait) and validate/coerce it to a boolean before use so the "Wait for transaction confirmation" block only runs when intended.
190-195: Avoidanytypes for paymaster configuration.Using
anyforpaymasterDetailsandmaxFeeloses type safety. Consider defining proper types or using the types from the starknet library.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-order/route.ts` around lines 190 - 195, Replace the use of any for paymasterDetails and maxFee by importing and using the appropriate StarkNet types: type the paymasterDetails variable with the library's PaymasterDetails/PaymasterInfo type (or the exact exported interface for feeMode) and type maxFee as the proper fee type (e.g., BigNumberish | bigint | number | MaxFee type exported by starknet). Update the variable declarations for paymasterDetails and maxFee to use those specific types and adjust the assigned objects/values to match the typed shape (retain the conditional logic around isSponsored and gasToken), importing the types from the starknet package and removing the any annotations.app/lib/starknet.ts (1)
29-59: RawSigner base class methods all throw - consider making abstract.The
RawSignerclass has methods that only throw errors. While this works, using TypeScript abstract methods would provide compile-time enforcement that subclasses implement the required methods.Proposed refactor using abstract class
-class RawSigner implements SignerInterface { - async getPubKey(): Promise<string> { - throw new Error("getPubKey not implemented"); - } - - async signMessage( - _typedData: any, - _accountAddress: string, - ): Promise<Signature> { - throw new Error("signMessage not implemented"); - } - // ... other methods -} +abstract class RawSigner implements SignerInterface { + abstract getPubKey(): Promise<string>; + abstract signMessage(typedData: any, accountAddress: string): Promise<Signature>; + abstract signTransaction(transactions: any[], transactionsDetail: any): Promise<Signature>; + abstract signDeployAccountTransaction(transaction: any): Promise<Signature>; + abstract signDeclareTransaction(transaction: any): Promise<Signature>; + abstract signRaw(messageHash: string): Promise<Signature>; +}Note: This would require updating the anonymous class in
buildReadyAccountto implement all abstract methods.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/lib/starknet.ts` around lines 29 - 59, Convert RawSigner into a TypeScript abstract class and change its no-op throwing methods (getPubKey, signMessage, signTransaction, signDeployAccountTransaction, signDeclareTransaction, signRaw) to abstract method signatures so implementations are enforced at compile time; then update the anonymous subclass used in buildReadyAccount (the class that currently extends RawSigner) to implement all the newly abstract methods (provide concrete implementations for getPubKey, signMessage, signTransaction, signDeployAccountTransaction, signDeclareTransaction, signRaw) to satisfy the compiler.app/api/starknet/transfer/route.ts (1)
13-231: Significant code duplication with create-order route.Both
transfer/route.tsandcreate-order/route.tsshare nearly identical patterns for:
- JWT extraction and verification (lines 16-34)
- Deployment check (lines 49-55)
- Paymaster setup and configuration (lines 95-115)
- Fee estimation with 1.5x margin (lines 148-168)
- Transaction execution with deploy fallback (lines 170-196)
Consider extracting shared utilities to reduce duplication and maintenance burden.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/transfer/route.ts` around lines 13 - 231, The POST handler duplicates auth, deployment check, paymaster setup, fee estimation, and execute-with-deploy-fallback logic; extract these into shared utilities and call them from transfer/route.ts (and create-order/route.ts). Create helpers like verifyAuthFromRequest(request) to run the JWT extraction/verifyJWT and return { token, authUserId }, isWalletDeployed(provider, walletAddress) that wraps provider.getClassHashAt, initPaymasterConfig() that wraps setupPaymaster and returns { paymasterRpc,isSponsored,gasToken,usePaymaster }, estimateFeeWithMargin(account, calls, paymasterDetails) encapsulating account.estimatePaymasterTransactionFee plus the 1.5x margin logic, and executeOrDeployTransaction({account,walletId,publicKey,classHash,token,authUserId,origin,calls,isDeployed}) that chooses between deployReadyAccount and account.executePaymasterTransaction and returns the result; replace the inline blocks in POST with calls to these helpers and reuse them from create-order/route.ts to eliminate duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/starknet/create-order/route.ts`:
- Around line 49-60: The deployment check using
provider.getClassHashAt(WalletAddress) runs before input validation and can
throw unclear errors if WalletAddress is missing/invalid; move that try/catch
block so it executes after you validate the request body and confirm
WalletAddress exists and is well-formed (the same validation logic currently
around lines validating body properties), initialize isDeployed only after
validation, and keep the try/catch around provider.getClassHashAt in the route
handler (referencing WalletAddress, provider.getClassHashAt, and isDeployed) so
invalid inputs return a 400 before any RPC call.
In `@app/api/starknet/transfer/route.ts`:
- Around line 117-126: The comment above the buildReadyAccount call is
misleading: it states the account is built without a paymaster while the code
passes paymasterRpc into buildReadyAccount (variables account and address).
Update the comment to accurately describe the behavior (e.g., note that
buildReadyAccount is invoked with paymaster support when paymasterRpc is
provided) or remove the incorrect parenthetical; alternatively, if the original
intent was to exclude paymaster, remove paymasterRpc from the buildReadyAccount
call instead—make the change around the buildReadyAccount(...) invocation.
- Around line 198-214: The endpoint inconsistently treats a failed txReceipt
(using txReceipt.isSuccess()) as an error but swallows exceptions from
account.waitForTransaction; change the catch block for
account.waitForTransaction to handle failures like the isSuccess branch by
returning an error response (NextResponse.json with a 500 status) and include
the caught error message for diagnostics, while optionally logging it; update
references in the catch to account.waitForTransaction, txReceipt.isSuccess(),
and NextResponse.json so the endpoint consistently reports transaction
confirmation failures.
In `@app/context/StarknetContext.tsx`:
- Around line 210-239: The refreshWalletState function is dead/pointing to a
missing GET /api/starknet/wallet-state endpoint; either delete
refreshWalletState (and any exports referencing it) and remove calls to
saveToLocalStorage if only localStorage/linkedAccounts are used, or implement
the missing backend endpoint to return { walletId, address, publicKey, deployed
} for a given userId and ensure refreshWalletState uses getAccessToken, then
sets state via setWalletId, setAddress, setPublicKey, setDeployed and calls
saveToLocalStorage; update any tests or imports that reference
refreshWalletState accordingly.
In `@app/hooks/useSmartWalletTransfer.ts`:
- Around line 199-204: The transaction save currently calls
saveTransferTransaction(...) using getEmbeddedWalletAddress(), which returns an
EVM-style address and causes Starknet transfers to be recorded with the wrong
wallet; update the saveTransferTransaction call (in useSmartWalletTransfer) to
pass starknetWallet.address when selectedNetwork.chain.name indicates Starknet
(or otherwise derive the proper wallet address based on
selectedNetwork.chain.name), falling back to getEmbeddedWalletAddress() for EVM
networks so the recorded txHash/recipientAddress/amount/token use the actual
wallet that executed the transfer.
In `@app/hooks/useWalletAddress.ts`:
- Around line 23-29: The Starknet branch in the useWalletAddress hook calls
normalizeStarknetAddress(starknetAddress) directly which can throw on malformed
input and crash render; wrap the call in a try/catch (or validate the format
first) so any exceptions return undefined (or fallback to starknetAddress)
instead of propagating, e.g. catch errors around normalizeStarknetAddress and
return undefined on failure; keep references to selectedNetwork,
starknetAddress, and normalizeStarknetAddress when locating the code.
In `@app/lib/authorization.ts`:
- Around line 48-49: The current return of signature ?? "" silently allows an
empty bearer token; change this to fail fast by throwing an explicit error when
signature is null/undefined/empty (e.g., if (!signature) throw new
Error("Authorization signature generation failed")) instead of returning "", so
callers of the function that computes `signature` will get an immediate,
descriptive failure; update any call sites to handle the thrown error as needed.
- Around line 19-33: The cache uses a shared literal "unknown" when userId is
missing which causes different users to collide; update the logic that builds
cacheKey (the cacheKey variable used with userSignerCache) to avoid a global
"unknown" key by deriving a unique fallback such as a stable fingerprint of the
request (e.g., a hash of userJwt) or a per-request random id combined with
userJwt, then continue to call getPrivyClient() and
privy.walletApi.generateUserSigner(...) as before and store the result in
userSignerCache with that unique cacheKey to prevent cross-user reuse of
authorizationKey.
In `@app/lib/starknet.ts`:
- Around line 242-243: The signature-splitting code assumes sig has a "0x"
prefix and exactly 128 hex chars (body length 128) before slicing into two
felts, which can produce malformed values if Privy returns a different length;
update the logic around the sig and body variables to validate that sig is a hex
string (startsWith("0x") or matches /^[0-9a-fA-F]+$/) and that body.length ===
128 before doing body.slice(0,64) and body.slice(64), and if the check fails
throw a clear error or return a validated failure value; apply the same
validation/failure behavior to the other occurrences that use
body.slice(0,64)/slice(64) (the other two places noted).
- Around line 285-289: The getRpcProvider function currently falls back to an
empty string which yields an invalid RpcProvider and cryptic errors; change
getRpcProvider to validate NEXT_PUBLIC_STARKNET_RPC_URL and fail fast by
throwing a clear error if the env var is missing or empty before constructing
RpcProvider (referencing getRpcProvider and RpcProvider), so callers get an
explicit message instead of creating a provider with an invalid nodeUrl.
- Around line 119-190: In rawSign, simplify the signature extraction to use only
the documented response path (data.data.signature) and remove the redundant
fallback checks (data.signature, data.result.signature,
data.result.data.signature, and treating the whole body as a string); update the
logic that assigns sig to read only data?.data?.signature, validate it is a
string, and return it prefixed with "0x" if missing so
buildAuthorizationSignature, getUserAuthorizationKey and the existing error
handling remain unchanged.
In `@app/pages/TransactionStatus.tsx`:
- Around line 511-515: The analytics wallet-type label is still set to "Smart
wallet" for Starknet flows; update the logic that determines the walletType used
in the analytics event to mirror the balance branches: check isInjectedWallet
first (use "Injected wallet"), then if selectedNetwork.chain.name === "Starknet"
set walletType to "Starknet wallet", otherwise set it to "Smart wallet"
(matching the smartWalletBalance branch); ensure this walletType is used when
emitting the analytics event so Starknet transactions are labeled correctly
(refer to the balance calculation and the analytics emission site in
TransactionStatus.tsx, and the variables isInjectedWallet,
injectedWalletBalance, selectedNetwork.chain.name, starknetWalletBalance,
smartWalletBalance).
---
Outside diff comments:
In @.env.example:
- Around line 110-111: There is a duplicate environment variable entry for
BREVO_LIST_ID (appears twice); remove the extra line so only one BREVO_LIST_ID=
entry remains in the .env.example and verify there are no other accidental
duplicate environment keys in the file.
---
Nitpick comments:
In `@app/api/starknet/create-order/route.ts`:
- Around line 256-262: Replace the hardcoded event selector string used in the
event key check with a named constant (e.g., ORDER_CREATED_SELECTOR) documented
to state that it is the StarkNet selector for the OrderCreated event; update the
check that uses Object.values(event.keys).includes(...) to compare against
ORDER_CREATED_SELECTOR and ensure the constant is exported/defined near the top
of route.ts with a brief comment describing its meaning and source (OrderCreated
event selector) so references like orderId = event.data[2] remain unchanged but
now use the named constant.
- Line 114: The request body validation currently destructures publicKey but the
code always overwrites it by calling getStarknetWallet(walletId) to obtain
walletPublicKey; either remove publicKey from the request body schema/validation
and stop expecting it from the client, or change the logic in route.ts to prefer
the client-supplied publicKey (use the destructured publicKey) and only call
getStarknetWallet(walletId) to populate walletPublicKey as a fallback when
publicKey is missing/empty; update any references to walletPublicKey/publicKey
accordingly (e.g., the const { publicKey: walletPublicKey } = await
getStarknetWallet(walletId) usage) so there is a single source of truth.
- Around line 246-248: Remove the hardcoded "const wait = true" and the
always-true "if (wait)" branch: read a real boolean (e.g., from the incoming
request body/query or a config/feature-flag) and use that value in the existing
conditional where "wait" is referenced so the code path becomes conditional in
practice; update any variable name if needed (e.g., request.wait or
options.wait) and validate/coerce it to a boolean before use so the "Wait for
transaction confirmation" block only runs when intended.
- Around line 190-195: Replace the use of any for paymasterDetails and maxFee by
importing and using the appropriate StarkNet types: type the paymasterDetails
variable with the library's PaymasterDetails/PaymasterInfo type (or the exact
exported interface for feeMode) and type maxFee as the proper fee type (e.g.,
BigNumberish | bigint | number | MaxFee type exported by starknet). Update the
variable declarations for paymasterDetails and maxFee to use those specific
types and adjust the assigned objects/values to match the typed shape (retain
the conditional logic around isSponsored and gasToken), importing the types from
the starknet package and removing the any annotations.
In `@app/api/starknet/create-wallet/route.ts`:
- Around line 32-39: Remove the redundant userId validation: since authUserId is
already checked earlier (the 25-30 block returns 401 if falsy), delete the
assignment/guard that sets const userId = authUserId and the subsequent if
(!userId) { return NextResponse.json(...) } block in route.ts; instead use
authUserId directly where userId was used (or keep a simple alias const userId =
authUserId without re-checking) so there is no unreachable validation code.
In `@app/api/starknet/get-public-key/route.ts`:
- Around line 49-51: The ownership check using linkedAccounts.find(...)
currently only matches account.id === walletId and account.type === "wallet";
update this to also verify account.chainType === "starknet" (or the same
chainType string used in create-wallet's check) so ownsWallet only matches
Starknet wallets; modify the predicate passed to linkedAccounts.find to include
the chainType check and keep the existing id and type checks (symbols:
linkedAccounts, ownsWallet, walletId, account.chainType).
In `@app/api/starknet/transfer/route.ts`:
- Around line 13-231: The POST handler duplicates auth, deployment check,
paymaster setup, fee estimation, and execute-with-deploy-fallback logic; extract
these into shared utilities and call them from transfer/route.ts (and
create-order/route.ts). Create helpers like verifyAuthFromRequest(request) to
run the JWT extraction/verifyJWT and return { token, authUserId },
isWalletDeployed(provider, walletAddress) that wraps provider.getClassHashAt,
initPaymasterConfig() that wraps setupPaymaster and returns {
paymasterRpc,isSponsored,gasToken,usePaymaster }, estimateFeeWithMargin(account,
calls, paymasterDetails) encapsulating account.estimatePaymasterTransactionFee
plus the 1.5x margin logic, and
executeOrDeployTransaction({account,walletId,publicKey,classHash,token,authUserId,origin,calls,isDeployed})
that chooses between deployReadyAccount and account.executePaymasterTransaction
and returns the result; replace the inline blocks in POST with calls to these
helpers and reuse them from create-order/route.ts to eliminate duplication.
In `@app/components/WalletTransferApprovalModal.tsx`:
- Around line 39-42: The CHAIN_MAP construction bluntly casts entries to Chain;
replace it with a guarded narrow: add a type-guard (e.g., isValidChain or
isEvmChain) that checks the properties you expect on n.chain, then build
CHAIN_MAP from migrationChecklistNetworks.filter(n =>
isValidChain(n.chain)).map(n => [n.chain.name, n.chain]) and pass the result to
Object.fromEntries so only validated Chain objects are included; update the same
pattern used at the other block (around the 99-103 area) to use the same
type-guard + filter approach and avoid unchecked `as Chain` casts on
migrationChecklistNetworks and CHAIN_MAP.
In `@app/context/StarknetContext.tsx`:
- Around line 134-208: createWallet currently returns Promise<string> but
StarknetContextType declares createWallet: () => Promise<void>, causing a type
mismatch; update the API to be consistent by either (A) changing
StarknetContextType.createWallet to () => Promise<string> so the context type
reflects the actual returned walletId (reference: StarknetContextType,
createWallet), or (B) change createWallet to return Promise<void> and move
walletId return into createWalletWrapper so callers only get void (reference:
createWalletWrapper). Pick one option and update all usages/signatures
accordingly to eliminate the mismatch.
In `@app/context/TokensContext.tsx`:
- Around line 56-62: Update the misleading inline comment in refreshTokens():
instead of saying "Skip Starknet since we already handled it above", change it
to explain that Starknet tokens are intentionally excluded here because Starknet
is handled separately by getNetworkTokens() in BalanceContext; reference
FALLBACK_TOKENS and the networkName === "Starknet" check so future readers know
why the branch skips Starknet and where to find its handling.
In `@app/hooks/useCNGNRate.ts`:
- Around line 197-204: Update the misleading comment to state that all Starknet
networks (including mainnet) are excluded because the aggregator doesn’t support
Starknet, and keep the existing early-return behavior in the useCNGNRate hook
(the network variable check that calls setRate(null), setError(null), and
setIsLoading(false) then returns). Replace the noisy console.log with a
lower-level log (e.g., console.debug) or remove it entirely so it doesn’t fire
on every render for Starknet users, or gate it behind a dev/verbose flag.
In `@app/hooks/useSmartWalletTransfer.ts`:
- Around line 145-152: The hardcoded 1s delay after ensureWalletExists() is
fragile and the empty catch swallows errors; replace the static sleep with a
short polling loop that checks the deployment flag (starknetWallet.deployed) or
a provider/deployment-status helper until either deployed or a max timeout is
reached (e.g., 5–10s) with a small interval/backoff between checks, and in the
catch block log or surface the error instead of silently ignoring it; update
ensureWalletExists()/the surrounding block to implement this polling and a
bounded timeout so transfers won’t wait indefinitely.
In `@app/lib/starknet.ts`:
- Around line 29-59: Convert RawSigner into a TypeScript abstract class and
change its no-op throwing methods (getPubKey, signMessage, signTransaction,
signDeployAccountTransaction, signDeclareTransaction, signRaw) to abstract
method signatures so implementations are enforced at compile time; then update
the anonymous subclass used in buildReadyAccount (the class that currently
extends RawSigner) to implement all the newly abstract methods (provide concrete
implementations for getPubKey, signMessage, signTransaction,
signDeployAccountTransaction, signDeclareTransaction, signRaw) to satisfy the
compiler.
In `@app/pages/TransactionForm.tsx`:
- Around line 160-164: Centralize the Starknet detection by replacing the
literal string check in TransactionForm (selectedNetwork.chain.name ===
"Starknet") with a shared helper/constant (e.g., isStarknetNetwork(network) or
NETWORKS.STARKNET) used across TransactionForm, TransactionStatus, etc.; add the
helper to a common utilities/constants module, update usages to call
isStarknetNetwork(selectedNetwork) (or compare against NETWORKS.STARKNET) and
export it for reuse so all components rely on the single source of truth.
In `@app/utils.ts`:
- Around line 289-290: The Starknet RPC URL can be undefined because
app/utils.ts returns process.env.NEXT_PUBLIC_STARKNET_RPC_URL directly; add an
explicit validation in fetchStarknetBalance (in app/lib/starknetRpc.ts) to check
the rpcUrl before using it: if rpcUrl is undefined or empty, return a controlled
error/early failure (or throw) with a clear message mentioning
NEXT_PUBLIC_STARKNET_RPC_URL, otherwise proceed to call
createStarknetRpcProvider and continue as normal; this makes the caller-side
behavior deterministic even though createStarknetRpcProvider accepts string |
undefined.
- Around line 628-665: The current fetchTokenPrice function can trigger
CoinGecko rate limits; replace per-token calls with a batched multi-coin fetch
and add an in-memory cache + TTL and 429 handling: implement a new
fetchTokenPrices(tokenSymbols: string[]) that maps symbols to CoinGecko IDs,
queries /simple/price with ids=comma-separated, stores results in a simple
in-memory map keyed by symbol with an expiry (e.g., 1–5 minutes), and returns a
Record<string, number|null>; modify fetchStarknetBalance to call
fetchTokenPrices once for all tokens instead of calling fetchTokenPrice per
token; in fetchTokenPrices specifically detect response.status === 429 and
perform an exponential backoff retry (or return cached values if available) and
ensure any network or non-200 responses return nulls for those tokens.
🪄 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: 68ee37fd-9bc7-4bbf-b2a3-645515b7437a
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/logos/strk-logo.svgis excluded by!**/*.svg
📒 Files selected for processing (35)
.env.exampleapp/api/aggregator.tsapp/api/starknet/create-order/route.tsapp/api/starknet/create-wallet/route.tsapp/api/starknet/get-public-key/route.tsapp/api/starknet/transfer/route.tsapp/api/track-logout/route.tsapp/components/MainPageContent.tsxapp/components/MobileDropdown.tsxapp/components/Navbar.tsxapp/components/NetworkSelectionModal.tsxapp/components/NetworksDropdown.tsxapp/components/SettingsDropdown.tsxapp/components/TransferForm.tsxapp/components/WalletDetails.tsxapp/components/WalletTransferApprovalModal.tsxapp/context/BalanceContext.tsxapp/context/StarknetContext.tsxapp/context/TokensContext.tsxapp/context/index.tsapp/hooks/useCNGNRate.tsapp/hooks/useSmartWalletTransfer.tsapp/hooks/useWalletAddress.tsapp/lib/authorization.tsapp/lib/privy.tsapp/lib/starknet.tsapp/lib/starknetRpc.tsapp/mocks.tsapp/pages/TransactionForm.tsxapp/pages/TransactionPreview.tsxapp/pages/TransactionStatus.tsxapp/providers.tsxapp/utils.tsjest.setup.jspackage.json
…tion and error handling - Added validation for the wallet address format in the create-order API, ensuring it is a valid string. - Implemented error responses for missing or invalid wallet addresses to improve user feedback. - Refactored the logic to check if the wallet is deployed using the normalized wallet address. - Removed redundant code related to wallet deployment checks, streamlining the function.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
app/lib/starknet.ts (1)
237-238:⚠️ Potential issue | 🟡 MinorSignature parsing assumes fixed length without validation.
The signature splitting logic (
body.slice(0, 64)andbody.slice(64)) assumes the signature is exactly 128 hex characters (64 bytes). If Privy returns a signature of different length, this will silently produce malformed felt values. The same issue exists at lines 257-258 and 267-268.Proposed fix: add validation helper
+function splitSignature(sig: string): [string, string] { + const body = sig.startsWith("0x") ? sig.slice(2) : sig; + if (body.length !== 128) { + throw new Error(`Invalid signature length: expected 128 hex chars, got ${body.length}`); + } + return [`0x${body.slice(0, 64)}`, `0x${body.slice(64)}`]; +} // Then in signMessage, signTransaction, signRaw: - const body = sig.slice(2); - return [`0x${body.slice(0, 64)}`, `0x${body.slice(64)}`]; + return splitSignature(sig);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/lib/starknet.ts` around lines 237 - 238, The signature splitting assumes sig has exactly 128 hex chars after the "0x" prefix and doesn't validate input; add a small helper (e.g., validateAndNormalizeSignature(sig)) that checks the prefix "0x", verifies the body length is 128 characters, ensures all chars are hex, and returns the normalized body (or throws a descriptive error) and then use that helper before performing the slices (`const body = sig.slice(2)` / `body.slice(0, 64)` / `body.slice(64)`); apply the same validation helper at the other identical locations where you slice `sig` (the other two occurrences using `body.slice(0, 64)` and `body.slice(64)`) so malformed or differently-sized signatures fail fast with clear messages instead of producing bad felt values.
🧹 Nitpick comments (1)
app/api/starknet/create-order/route.ts (1)
266-292: Consider adding documentation for the hardcoded event key.The event key
0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3identifies the order creation event, andevent.data[2]extracts theorderId. While this pattern is standard for Starknet event parsing, a comment documenting what this key represents (e.g.,// keccak("OrderCreated")or similar) would improve maintainability.Additionally,
const wait = trueon line 269 is always true, so theif (wait)block always executes. This appears to be a development artifact that could be removed.Suggested documentation
let orderId; // Wait for transaction confirmation - const wait = true; - if (wait) { - try { - const txReceipt = await account.waitForTransaction( - result.transaction_hash, - ); - if (txReceipt.isSuccess()) { - const rawEvents = txReceipt.value.events; - rawEvents.forEach((event) => { - if ( - Object.values(event.keys).includes( - "0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3", - ) - ) { - orderId = event.data[2]; - } - }); - } - } catch (error) { - console.log( - "[API] Warning: Could not confirm transaction, but it may still succeed", - ); - } + try { + const txReceipt = await account.waitForTransaction( + result.transaction_hash, + ); + if (txReceipt.isSuccess()) { + const rawEvents = txReceipt.value.events; + // Gateway OrderCreated event selector (keccak of event name) + const ORDER_CREATED_SELECTOR = "0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3"; + rawEvents.forEach((event) => { + if (Object.values(event.keys).includes(ORDER_CREATED_SELECTOR)) { + // OrderCreated event data layout: [sender, token, orderId, ...] + orderId = event.data[2]; + } + }); + } + } catch (error) { + console.log( + "[API] Warning: Could not confirm transaction, but it may still succeed", + ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/create-order/route.ts` around lines 266 - 292, Document the hardcoded event key and remove the dev artifact: add a concise comment next to the literal "0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3" explaining what it represents (e.g., the keccak/selector for the OrderCreated event) and that event.data[2] is the orderId extracted from that event; also remove or replace the always-true const wait = true (used around account.waitForTransaction and txReceipt handling) with a real flag/parameter or just execute the block directly so the if (wait) is not redundant (refer to account.waitForTransaction, txReceipt, rawEvents, and orderId to locate the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/starknet/transfer/route.ts`:
- Around line 49-56: The deployment check is calling
provider.getClassHashAt(WalletAddress) before WalletAddress is validated,
causing unclear errors; update the route to validate WalletAddress (presence and
format) before calling getClassHashAt: check the WalletAddress request field
(same way as create-order/route.ts does) and return a 400 if missing/invalid,
then only call getClassHashAt and set isDeployed based on that call; reference
the WalletAddress variable, provider.getClassHashAt and the isDeployed
assignment to locate and fix the logic.
In `@app/context/StarknetContext.tsx`:
- Around line 45-72: The effect in StarknetContext.tsx only sets
walletId/address/publicKey when a Starknet linked account exists, leaving stale
values otherwise; update the useEffect (the effect that reads
user.linkedAccounts) to add an else branch that calls setWalletId(null),
setAddress(null), setPublicKey(null) and calls saveToLocalStorage with
null/empty values (or removes the keys) when no starknetWallet is found, and do
the same for the other place noted (the storage-read logic) so that both the
in-memory state setters (setWalletId, setAddress, setPublicKey) and persisted
storage via saveToLocalStorage are cleared when no wallet data exists.
- Around line 133-167: The createWallet function's signature promises
Promise<string> but newWalletId can be null; update the function to satisfy the
contract by either changing the return type to Promise<string | null> or
(preferred) validate and enforce a non-null wallet id: after parsing wallet
(variable wallet and newWalletId) check if newWalletId is truthy and if not
throw a descriptive Error (e.g., "Created wallet missing id"), ensuring
createWallet always resolves to a string; adjust any callers accordingly if you
change the return type.
---
Duplicate comments:
In `@app/lib/starknet.ts`:
- Around line 237-238: The signature splitting assumes sig has exactly 128 hex
chars after the "0x" prefix and doesn't validate input; add a small helper
(e.g., validateAndNormalizeSignature(sig)) that checks the prefix "0x", verifies
the body length is 128 characters, ensures all chars are hex, and returns the
normalized body (or throws a descriptive error) and then use that helper before
performing the slices (`const body = sig.slice(2)` / `body.slice(0, 64)` /
`body.slice(64)`); apply the same validation helper at the other identical
locations where you slice `sig` (the other two occurrences using `body.slice(0,
64)` and `body.slice(64)`) so malformed or differently-sized signatures fail
fast with clear messages instead of producing bad felt values.
---
Nitpick comments:
In `@app/api/starknet/create-order/route.ts`:
- Around line 266-292: Document the hardcoded event key and remove the dev
artifact: add a concise comment next to the literal
"0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3" explaining
what it represents (e.g., the keccak/selector for the OrderCreated event) and
that event.data[2] is the orderId extracted from that event; also remove or
replace the always-true const wait = true (used around
account.waitForTransaction and txReceipt handling) with a real flag/parameter or
just execute the block directly so the if (wait) is not redundant (refer to
account.waitForTransaction, txReceipt, rawEvents, and orderId to locate the
code).
🪄 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: e94dc224-7882-47a6-b506-cf79c7b876b5
📒 Files selected for processing (6)
app/api/starknet/create-order/route.tsapp/api/starknet/transfer/route.tsapp/context/StarknetContext.tsxapp/hooks/useSmartWalletTransfer.tsapp/hooks/useWalletAddress.tsapp/lib/starknet.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- app/hooks/useWalletAddress.ts
- app/hooks/useSmartWalletTransfer.ts
…in Starknet context - Added validation for wallet address format in the Starknet API, ensuring it is a valid string before processing. - Implemented error responses for missing or invalid wallet addresses to enhance user feedback. - Refactored state management in StarknetProvider to handle null values for wallet ID, address, and public key more effectively. - Streamlined the logic for checking if a wallet is deployed using the normalized wallet address, improving code clarity.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/api/starknet/transfer/route.ts (1)
48-62: Consider removingpublicKeyfrom required fields.The validation at line 49 requires
publicKeyfrom the request body, but line 115 ignores it and fetches the authoritativewalletPublicKeydirectly from Privy viagetStarknetWallet(walletId). This is likely intentional for security (don't trust client-provided keys), but requiring a field that's unused is misleading to API consumers.🔧 Optional: Remove publicKey from validation
// Validate required fields - if (!walletId || !publicKey || !tokenAddress || !recipientAddress) { + if (!walletId || !tokenAddress || !recipientAddress) { return NextResponse.json( { error: "Missing required fields", missing: { walletId: !walletId, - publicKey: !publicKey, tokenAddress: !tokenAddress, recipientAddress: !recipientAddress, }, }, { status: 400 }, ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/starknet/transfer/route.ts` around lines 48 - 62, The validation block in route.ts incorrectly treats publicKey as required even though the authoritative walletPublicKey is fetched later via getStarknetWallet(walletId); remove publicKey from the required-fields check and from the returned missing object (keep walletId, tokenAddress, recipientAddress), and update any related error messaging so consumers aren't required to send publicKey while preserving the later use of getStarknetWallet to obtain walletPublicKey.app/context/StarknetContext.tsx (1)
244-280: Consider clarifying the error propagation behavior.The nested error handling creates a subtle behavior: the inner catch (lines 270-275) re-throws, but the outer catch (lines 277-279) swallows all errors. This means
ensureWalletExistsalways resolves successfully (never rejects), even when wallet creation fails.This is likely intentional (toast provides user feedback), but the JSDoc comment "silent operation" is misleading since toasts are shown. If callers need to react to failures (beyond the toast), consider either:
- Re-throwing from the outer catch, or
- Returning a boolean indicating success
Given the current usage in
handleNetworkSwitch(which continues on failure anyway), the current behavior is acceptable.📝 Optional: Update JSDoc to clarify behavior
/** * Ensures a Starknet wallet exists for the user - * Auto-creates if needed (silent operation) + * Auto-creates if needed. Shows toasts for progress/errors but never throws. */ const ensureWalletExists = async () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/context/StarknetContext.tsx` around lines 244 - 280, The outer catch in ensureWalletExists currently swallows errors thrown by the inner createWallet catch, causing ensureWalletExists to always resolve; decide whether callers should observe failures and fix accordingly: either (A) re-throw the caught error in the outer catch of ensureWalletExists so callers like handleNetworkSwitch can detect failures, or (B) change the function signature to return a boolean success value (true on created/existing deployed wallet, false on failure) and update all callers to check that return; also update the JSDoc for ensureWalletExists to accurately describe the behavior (i.e., not "silent" if toasts are shown or that it may throw/return false).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/starknet/transfer/route.ts`:
- Around line 64-69: The amount presence check is insufficient because
BigInt(amount) can throw for non-numeric strings; before converting, validate
that the request's amount string matches a safe integer format (e.g., a decimal
integer regex) and return NextResponse.json({ error: "Invalid amount format" },
{ status: 400 }) if it fails; then use the validated value (or re-parse) for
BigInt(amount) to avoid a 500 from BigInt conversion. Reference the amount
variable and the BigInt(amount) call in this route handler to locate where to
add the format check.
---
Nitpick comments:
In `@app/api/starknet/transfer/route.ts`:
- Around line 48-62: The validation block in route.ts incorrectly treats
publicKey as required even though the authoritative walletPublicKey is fetched
later via getStarknetWallet(walletId); remove publicKey from the required-fields
check and from the returned missing object (keep walletId, tokenAddress,
recipientAddress), and update any related error messaging so consumers aren't
required to send publicKey while preserving the later use of getStarknetWallet
to obtain walletPublicKey.
In `@app/context/StarknetContext.tsx`:
- Around line 244-280: The outer catch in ensureWalletExists currently swallows
errors thrown by the inner createWallet catch, causing ensureWalletExists to
always resolve; decide whether callers should observe failures and fix
accordingly: either (A) re-throw the caught error in the outer catch of
ensureWalletExists so callers like handleNetworkSwitch can detect failures, or
(B) change the function signature to return a boolean success value (true on
created/existing deployed wallet, false on failure) and update all callers to
check that return; also update the JSDoc for ensureWalletExists to accurately
describe the behavior (i.e., not "silent" if toasts are shown or that it may
throw/return false).
🪄 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: 2b31ed9c-ab5f-40a2-8ffb-7bae9876dd0d
📒 Files selected for processing (2)
app/api/starknet/transfer/route.tsapp/context/StarknetContext.tsx
…rknet integration - Introduced unified balance fetching for both EVM and Starknet networks, improving consistency in balance management. - Added support for displaying balances in the WalletDetails component specifically for Starknet. - Implemented user notifications for unsupported on-ramp functionality on Starknet, guiding users to switch to an EVM network. - Refactored state management in BalanceContext to handle Starknet balances more effectively, ensuring accurate cross-chain balance representation.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/context/BalanceContext.tsx`:
- Around line 283-289: When clearing balances in the code paths that
setStarknetWalletBalance, setSmartWalletBalance, setExternalWalletBalance,
setInjectedWalletBalance, and setCrossChainBalances, also reset the derived
smart-wallet totals so they don't remain stale: call the setters that clear
smartWalletRemainingTotal and smartWalletCrossChainTotals (and any
per-cross-chain remaining entries) in the same branches where balances are
cleared (both the branch around setStarknetWalletBalance and the other branch
around setCrossChainBalances at the second location) so all derived state is
reset whenever the Starknet address/user context is absent.
In `@app/utils.ts`:
- Around line 1017-1045: normalizeStarknetAddress currently accepts "0x" and
addresses that normalize to an all-zero address; update it to reject empty or
all-zero addresses by checking the hex part after stripping "0x": throw an error
if hexPart is empty (no hex chars) or if the hex part consists only of zeros
(e.g., hexPart.match(/^0+$/) is true) so the function does not return a
zeroed/padded address; keep existing validations (0x prefix, hex chars, max
length) and reference normalizeStarknetAddress to locate the change.
- Around line 1242-1249: The current block logs ensureWalletExists failures but
continues the network switch; change this so a failure stops the switch: in the
code path where you check network.chain.name === "Starknet" and call
ensureWalletExists(), remove the "continue" behavior and propagate the failure
(either rethrow the caught error or return an error/false from the surrounding
network switch handler) instead of swallowing it; ensure the UI/state update
that sets the network to Starknet only happens after ensureWalletExists()
succeeds so downstream logic (wallet-dependent flows) won't run without a valid
wallet.
🪄 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: ac69fd36-a9fa-4539-a554-af6a7ecd94ce
📒 Files selected for processing (5)
app/components/MainPageContent.tsxapp/components/WalletDetails.tsxapp/context/BalanceContext.tsxapp/mocks.tsapp/utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- app/mocks.ts
…streamline fallback token handling - Eliminated the temporary addition of USDT for the Base network during token fetching. - Simplified the logic for merging fallback tokens, ensuring only relevant networks are processed.
…ment - Added validation for Starknet addresses to ensure they are not empty or the zero address, improving error handling. - Updated BalanceContext to reset smart wallet totals and cross-chain balances upon error, ensuring consistent state management during balance fetching.
- Introduced new API routes for Starknet: `/api/starknet/transfer` and `/api/starknet/create-order`, expanding functionality for wallet operations. - Implemented comprehensive error handling and validation for wallet addresses across Starknet APIs, improving user feedback and security. - Enhanced state management in the Starknet context to support new wallet-related features, including tracking wallet creation and transfer processes. - Updated middleware to include wallet address extraction for improved request handling.
* remove celo & scroll from migration path (#451) * remove celo from migration path * exclude scroll --------- Co-authored-by: Prosper <40717516+onahprosper@users.noreply.github.com> * feat(rates): fetch quotes via aggregator v2 with buy/sell side (#453) * feat(rates): fetch quotes via aggregator v2 with buy/sell side Made-with: Cursor * feat(aggregator): enhance URL validation and parsing in aggregatorOriginForV2 function Updated the aggregatorOriginForV2 function to include error handling for the NEXT_PUBLIC_AGGREGATOR_URL. It now checks for valid absolute URLs and ensures the protocol is either HTTP or HTTPS before returning the base path. * Feat/onramp implementation (#333) * feat: enhance swap functionality in TransactionForm - Simplified the stateProps object in MainPageContent for better readability. - Introduced isSwapped state in TransactionForm to manage swap mode between token and currency. - Updated calculations for amount sent and received based on swap mode. - Enhanced dropdowns and button for swapping fields, improving user experience. * feat: implement `onramp` support and enhance transaction flow - Added support for wallet recipients in the transaction process, allowing users to send tokens directly to wallet addresses. - Introduced new state management for onramp mode, differentiating between wallet and bank/mobile money transactions. - Updated API interactions to handle wallet recipient data, including saving and migrating wallet recipients. - Enhanced UI components to accommodate wallet-specific fields and improve user experience during recipient selection. - Implemented ENS name resolution for wallet addresses to provide better visibility for users. - Updated database schema to include a new table for saved wallet recipients, ensuring proper data handling and security. * feat: enhance recipient management and transaction flow - Added a `name` field to wallet recipient details for better identification. - Implemented `getCurrencySymbol` utility to standardize currency symbol retrieval across components. - Updated transaction handling to validate and include recipient names in API interactions. - Enhanced UI components to display recipient names and improve user experience during transactions. - Modified database schema to ensure recipient names are stored and managed correctly. * feat: enhance recipient validation and UI components - Introduced type predicates for recipient details to improve type safety in filtering bank/mobile money and wallet recipients. - Added validation for wallet address format in the API, ensuring correct input before processing. - Updated UI components to utilize new type predicates for better handling of recipient data. - Enhanced error handling for missing required fields in recipient data during local storage migration. - Improved styling in the AddBeneficiaryModal for better user experience. * feat: enhance TransactionForm with onramp/offramp buttons - Added Onramp and Offramp buttons to the TransactionForm for improved user interaction. - Updated layout to better accommodate new buttons while maintaining UI consistency. - Ensured walletAddress is cleared when switching to onramp mode for better state management. * feat: add refund account management and payment order endpoints - Introduced refund account API endpoints for fetching and saving refund account details. - Added support for creating and retrieving v2 payment orders. - Updated types to include new refund account and payment order structures. - Enhanced middleware to include new API routes for refund accounts and payment orders. - Updated configuration to include aggregator sender API key. - Modified components and pages to integrate new refund account functionality and payment order handling. * chore: remove referral feature files from onramp branch Referral feature belongs to its own branch and has not been merged. Removing all referral-specific files to keep this branch focused on the onramp implementation. Made-with: Cursor * feat: enhance transaction handling and add mobile sheet view type - Introduced a new MobileSheetView type for better management of mobile sheet states. - Updated TransactionPreview to reset the refund account on currency changes. - Improved error handling in TransactionStatus for expired sessions during transaction tracking. * feat: enhance onramp functionality and transaction status handling - Added support for connected wallet address in RecipientDetailsForm. - Updated transaction status types to include "expired". - Implemented mapping of aggregator order status to database status for onramp transactions. - Enhanced error handling and status resolution for onramp payment orders. - Improved transaction details display for onramp transactions in TransactionDetails and TransactionPreview components. - Refactored payment instruction mapping for better clarity and functionality. * feat: introduce OnrampPaymentInstructions type and refactor mapping logic - Added OnrampPaymentInstructions type to define the structure for virtual account/bank transfer instructions. - Implemented mapProviderAccountToInstructions function to convert V2FiatProviderAccountDTO to OnrampPaymentInstructions. - Removed redundant onrampPaymentInstructions module and updated imports in relevant components. - Enhanced transaction handling in MakePayment and TransactionDetails components to utilize the new mapping function. * refactor: improve addBeneficiary function and transaction status handling - Enhanced the addBeneficiary function to return a boolean indicating success or failure, improving error handling and user feedback. - Cleaned up transaction status type definitions for better readability. - Updated UI elements to reflect changes in transaction status and actions based on the current state. * feat: add isSwapped state to transaction forms and update related logic - Introduced isSwapped boolean to FormData type for tracking swap mode. - Updated MainPageContent and TransactionForm components to manage isSwapped state. - Adjusted rate fetching logic based on isSwapped to differentiate between buy and sell sides. - Enhanced wallet address handling to conditionally set isSwapped based on user input. * fix: conditionally render receipt button based on onramp status * feat: enhance transaction status handling for onramp flows - Updated transaction completion logic to differentiate between onramp and off-ramp flows. - Introduced processingStartedAt state to track the start time of onramp processing. - Adjusted success visuals and button visibility based on transaction status for onramp transactions. - Improved time spent calculations to reflect onramp processing duration accurately. * feat: implement onramp client payment session management - Added functionality to track and manage the onramp client payment session expiration. - Introduced `isOnrampClientPaymentSessionExpired` utility to determine session validity based on transaction details. - Updated `TransactionDetails`, `TransactionList`, and `MakePayment` components to reflect session expiration in UI. - Enhanced transaction status handling to display "expired" when applicable, improving user feedback during onramp transactions. * fix: correct base URL configuration for payment orders API - Updated the base URL for the payment orders API to use the configured aggregator URL instead of a hardcoded value, ensuring proper integration with the aggregator service. * fix: ensure contentClassName is safely handled in AnimatedModal component - Updated the AnimatedModal component to use a fallback for contentClassName, preventing potential undefined values from causing issues in the className concatenation. --------- Co-authored-by: Prosper <40717516+onahprosper@users.noreply.github.com> * Update HomePage.tsx Homepage copy * feat/UI swap refund pending indicators (#459) * fix: update TransactionForm and TransactionPreview styles and structure - Adjusted text color for dark mode in TransactionForm to improve visibility. - Refactored refund account button in TransactionPreview for better accessibility and user interaction, including hover effects and aria-labels for improved screen reader support. * feat: enhance onramp transaction handling and UI notifications - Added utility functions to determine if an onramp order is awaiting user bank transfer. - Integrated onramp pending notification dot in WalletDetails component to indicate pending bank transfers. - Updated transaction fetching logic to ensure timely updates and re-evaluation of onramp status. - Improved transaction context to handle reindexing of pending transactions and update backend accordingly. * refactor: consolidate OnrampPendingNotificationDot into utils and enhance transaction context - Moved OnrampPendingNotificationDot component logic to utils for better organization. - Updated WalletDetails and TransactionPreview components to utilize the new OnrampPendingNotificationDot from utils. - Enhanced StepContext to manage onramp provider details visibility, improving user experience during transactions. - Refined isOnrampAwaitingUserBankTransfer function to simplify status checks for pending transactions. * feat: enhance onramp provider details management and transaction context. * feat(payment-orders): on-ramp-only proxy, messageHash apiKey, single env var (#464) * feat(payment-orders): on-ramp-only proxy, messageHash apiKey, single env var Restrict POST /api/v1/payment-orders to fiat on-ramp (v2); reject off-ramp. Embed sender API key UUID in encrypted createOrder recipient metadata for indexer parity. Use NEXT_PUBLIC_AGGREGATOR_SENDER_API_KEY_ID as the single config key for server and client. * feat(onramp): explicit receive selection, NGN on-ramp rules, tx amount labels Gate swap CTA and recipient flow on receiveDestinationExplicitlySelected. On-ramp: NGN send side, min fiat vs rate, rate fetch after token chosen; hero copy. Centralize transaction history amount/type display helpers; remove unused import. * fix(form): clear fiat when toggling off-ramp after on-ramp Reset currency to empty so off-ramp matches initial no-selection state. Adjust on-ramp hero: first line "Change cash to", second "stablecoins in seconds". * fix(form): reset partial amounts on ramp toggle; persist when flow complete Complete flow = receive destination + token + currency + both amounts > 0: swap amounts/formatting and keep assets. Otherwise clear amounts and apply defaults. On-ramp: normalize currency to NGN when switching from other fiat. Also: onrampFiatMin and Select token CTA only after token; useSwapButton aligns. * fix(home): align off-ramp hero line break with on-ramp (stablecoins to / cash in seconds) * fix(ui): on-ramp receipt explorer + transfer modal token label TransactionDetails: on-ramp headline uses fiat amount; hide duplicate onchain row; completed on-ramp opens explorer via View receipt (no PDF). Transfer funds: Select token dropdown title and selected value. * docs(faq): on-ramp copy and home tagline Expand FAQs for stablecoin↔fiat both ways; add bank/mobile money on-ramp FAQ. Home: move between stablecoins and cash line + 30s. * chore: refactor clipboard functionality to improve reliability (#463) * chore: refactor clipboard functionality to improve reliability and user feedback - Updated `copyToClipboard` function to use `navigator.clipboard` in secure contexts and fall back to `execCommand` for non-secure contexts. - Changed return type of `copyToClipboard` to boolean to indicate success or failure. - Refactored multiple components to utilize the updated `copyToClipboard` function, enhancing clipboard copy operations with toast notifications for user feedback. - Ensured consistent handling of clipboard actions across various components, including `CopyAddressWarningModal`, `MobileDropdown`, and `TransactionDetails`. * refactor: enhance clipboard copy functionality for improved reliability --------- Co-authored-by: Prosper <40717516+onahprosper@users.noreply.github.com> * Feat/starknet clean (#474) * feat(starknet): Starknet wallet, APIs, and app integration on latest main - Restore Starknet routes (create-wallet, get-public-key, transfer, create-order), context, lib, and STRK logo - Wire balance, transfer, transaction flows, network UI, and Privy wallet API helpers - Cast EVM viem Chain where migration/network lists still union with custom Starknet chain id type Made-with: Cursor * feat(aggregator): enhance fetchTokens function with URL validation and improved error handling - Added validation for the AGGREGATOR_URL to ensure it is set and properly formatted. - Improved error logging to provide more context on network issues when fetching tokens from the API. - Updated the API request to use a constructed URL for better clarity and maintainability. * feat(starknet): enhance create-order route with wallet address validation and error handling - Added validation for the wallet address format in the create-order API, ensuring it is a valid string. - Implemented error responses for missing or invalid wallet addresses to improve user feedback. - Refactored the logic to check if the wallet is deployed using the normalized wallet address. - Removed redundant code related to wallet deployment checks, streamlining the function. * feat(starknet): improve wallet address handling and state management in Starknet context - Added validation for wallet address format in the Starknet API, ensuring it is a valid string before processing. - Implemented error responses for missing or invalid wallet addresses to enhance user feedback. - Refactored state management in StarknetProvider to handle null values for wallet ID, address, and public key more effectively. - Streamlined the logic for checking if a wallet is deployed using the normalized wallet address, improving code clarity. * feat(starknet): enhance balance fetching and network handling for Starknet integration - Introduced unified balance fetching for both EVM and Starknet networks, improving consistency in balance management. - Added support for displaying balances in the WalletDetails component specifically for Starknet. - Implemented user notifications for unsupported on-ramp functionality on Starknet, guiding users to switch to an EVM network. - Refactored state management in BalanceContext to handle Starknet balances more effectively, ensuring accurate cross-chain balance representation. * refactor(utils): remove temporary USDT addition for Base network and streamline fallback token handling - Eliminated the temporary addition of USDT for the Base network during token fetching. - Simplified the logic for merging fallback tokens, ensuring only relevant networks are processed. * feat(starknet): enhance address validation and balance context management - Added validation for Starknet addresses to ensure they are not empty or the zero address, improving error handling. - Updated BalanceContext to reset smart wallet totals and cross-chain balances upon error, ensuring consistent state management during balance fetching. * feat(starknet): add new API routes and enhance wallet management - Introduced new API routes for Starknet: `/api/starknet/transfer` and `/api/starknet/create-order`, expanding functionality for wallet operations. - Implemented comprehensive error handling and validation for wallet addresses across Starknet APIs, improving user feedback and security. - Enhanced state management in the Starknet context to support new wallet-related features, including tracking wallet creation and transfer processes. - Updated middleware to include wallet address extraction for improved request handling. --------- Co-authored-by: sundayonah <sundayonah94@gmail.com> * refactor: centralize Starknet ready account class hash and paymaster configuration (#476) - Moved the Starknet ready account class hash and paymaster configuration from environment variables to the config module for better maintainability and clarity. - Updated relevant API routes and hooks to utilize the new centralized configuration, improving consistency across the application. - Removed redundant error handling for missing environment variables related to the paymaster setup, streamlining the codebase. * feat: enhance CopyAddressWarningModal for Starknet support (#477) - Updated the CopyAddressWarningModal to display specific warnings and information for Starknet addresses, ensuring users are aware of the differences in address formats and supported networks. - Centralized modal title, deposit description, and footer warning for improved readability and maintainability. - Adjusted the list of supported networks displayed based on the selected network, enhancing user experience and clarity regarding supported stablecoins and networks. * feat: enhance Starknet integration and UI components (#480) * feat: enhance Starknet integration and UI components - Added support for internal API origin configuration in the aggregator API to handle deployments with separate SPA and API origins. - Updated the networks list in mocks to reflect explicit ranking based on aggregator volume, improving user experience. - Introduced a new context for managing transaction form swap state, allowing global UI updates without prop drilling. - Enhanced dropdown components to support a disabled state, preventing user interaction when necessary. - Implemented validation for Starknet-specific on-ramp functionality, including UI adjustments to indicate unsupported features. - Refactored transaction handling to ensure correct wallet address usage for Starknet, improving API interaction and error handling. - Improved error handling in transaction fetching to gracefully manage 404 responses, enhancing user feedback. * feat: improve Starknet on-ramp handling and transaction management - Added validation to block form submission when the Starknet on-ramp is active and a swap is indicated, enhancing user experience. - Updated transaction context to align cache state with API responses, preventing stale data from being used in future fetches. - Enhanced swap button logic to disable interactions when the Starknet on-ramp is active, ensuring clarity for users. - Refined wallet address handling in the TransactionPreview component to ensure proper API interactions and error handling, improving transaction persistence reliability. * fix: improve error handling in TransactionsContext for transaction fetching - Enhanced error handling in the TransactionsContext to log detailed error messages when fetching transactions fails. - Simplified the handling of 404 errors by removing redundant state updates, ensuring clearer error reporting and user feedback. * feat: enhance TransferForm with improved address validation for Starknet and EVM networks (#481) - Added comprehensive validation for recipient addresses based on the selected network, ensuring correct formats for both Starknet and EVM addresses. - Integrated a trigger to validate the recipient address whenever the recipient network changes, improving user feedback and error handling. - Refactored existing validation logic to streamline address checks and provide clearer error messages for invalid formats. * feat: introduce swap mode functionality across transaction components - Added a new type to manage on-ramp and off-ramp states consistently. - Updated and various components to replace the boolean with , enhancing clarity and maintainability. - Implemented utility functions to derive swap mode from URL parameters and set initial values based on network conditions. - Refactored transaction handling and UI components to utilize the new swap mode logic, improving user experience and error handling during transactions. * feat(balances): parallelize rate+RPC, multicall per chain, SWR cache, CNGN UX (#482) * feat(balances): parallelize rate+RPC, multicall per chain, SWR cache, CNGN UX Speeds up wallet balance loads and removes the misleading "$0 red" CNGN display when the NGN-USD rate is unavailable. Highlights: - Run getCNGNRateForNetwork in parallel with cross-chain balance fetches so wall time is max(rate, RPC) instead of sum. - Replace per-token readContract loop with a single viem multicall per chain (allowFailure, sequential fallback when multicall3 is missing). - Add client-only TTL+single-flight cache (walletBalanceCache) keyed by chainId+address; expose softRefresh (cache-respecting) and refreshBalance (bypass-cache) on BalanceContext. - Treat missing CNGN rate as "USD valuation unknown" instead of \$0: introduce cngnUsdUnknown, render "—" with NGN-pegged copy in WalletDetails / WalletView, keep raw token amount visible. - Re-apply CNGN correction when the rate later resolves without re-fetching balances; quote NGN<->USD on a fixed corridor (CNGN_CROSS_CHAIN_QUOTE_NETWORK) for cross-chain consistency. - Identity-aware isRefreshing avoids showing another wallet's cached balances during account switches. - Add lightweight balanceTelemetry (opt-in via localStorage.balance_debug in dev, 2% sample in prod) and tokenBalanceRowVisible helper. Tests: extend coverage with tokenBalanceRowVisible.test.ts. * fix(balances): honor bypass on rate fallback, safe cache invalidation, sequential read fallback Pass bypassCache into getCNGNRateForNetwork when resolving CNGN rate in BalanceContext so manual refresh can skip the local quote cache. Prevent stale in-flight balance fetches from repopulating walletBalanceCache after invalidation, and clear inflight keys on invalidate. Use a true sequential ERC-20 readContract loop when multicall fails. * perf(balances): run native getBalance in parallel with ERC-20 multicall * fix(balances): bypass inflight dedupe on refresh, lazy-load cache on client When bypassCache is set, start a fresh fetch instead of joining an older in-flight request. Load walletBalanceCache via dynamic import in fetchEvmBalancesForAddress and skip cache entirely on the server so shared utils imports do not execute client-only Maps in Node. * perf(build): drop unused deps, lazy-load @react-pdf/renderer, enable Turbopack (#483) The production build was taking 10–12 minutes. This commit applies three independent changes that together remove the largest sources of build cost: 1. Removed dependencies that nothing in the app actually imports: - thirdweb (5.102.6 + duplicate 5.29.6) - @thirdweb-dev/auth, @thirdweb-dev/wallets - @vidstack/react - net, tls (already polyfilled-out via the package.json `browser` map and Next webpack `resolve.fallback`) These pulled in ~400 MB of transitive code (multiple viem versions, @WalletConnect copies, three.js, lucide-react, @thirdweb-dev/contracts-js, etc.). After re-locking, pnpm reports `+76 -693` packages and node_modules shrinks from 2.6 GB to 1.6 GB, which speeds up dep resolution, SWC transforms, and CI cache restore. 2. Lazy-load @react-pdf/renderer in TransactionDetails and TransactionStatus. PDFKit + fontkit + image decoders are only needed when the user clicks "Get receipt", so they no longer enter the first-load JS bundle. 3. Switch `next build` to use Turbopack (stable in Next 15.5+). Combined with the smaller dep tree, this should cut production build time by several minutes on its own. No app code or runtime behavior changes; the PDF receipt feature still works identically (just dynamically imported on click). * fix(build): disable Turbopack scope hoisting to avoid BytePos panic Workaround for the "high bits of the position ... are not all 0s or 1s" panic that crashes `next build --turbopack` on large module graphs (e.g. via Sanity). Fixed upstream in Next.js 16 (vercel/next.js#83399) but not in 15.5.x. Remove once we upgrade to a version that includes the fix. * fix(deps): patch 90+ Dependabot security vulnerabilities (#484) * fix(deps): patch 90+ Dependabot security vulnerabilities Update direct deps and add pnpm overrides to address all reachable CVEs. Skipped uuid@14 (major API break), undici@6 (5→6 major), and @tootallnate/once@3 (2→3 major) as they require breaking changes. Direct upgrades: - next ^15.5.7 → ^15.5.15 (DoS, high) - axios ^1.9.0 → ^1.15.0 (SSRF/header injection, medium) - postcss ^8.5.5 → ^8.5.10 (XSS, medium) - @sanity/cli ^3.99.0 → ^4.4.1 (removes vite@7.1.x from tree) New pnpm overrides (transitive): - @hpke/core 1.7.2→1.9.0 (critical) - jws 3.2.2→3.2.3, h3 1.15.3→1.15.11, valibot 1.1.0→1.3.1, preact 10.26.9→10.29.1 (high, production) - dompurify →3.4.2, follow-redirects →1.16.0, defu →6.1.7 (medium) - vite@7 7.1.2→7.3.2 (high, arbitrary file read) - rollup 4.46.2→4.60.2, tar 7.4.3→7.5.13, tar-fs 2.1.3→2.1.4, systeminformation 5.27.7→5.31.5 (high) - glob@10 →10.5.0, glob@11 →11.1.0 (high) - minimatch@3/5/9/10, picomatch@2/4, yaml@1/2, js-yaml@3/4 all patched - lodash/lodash-es →4.18.1, bn.js →5.2.3, qs →6.15.1, brace-expansion@2 →2.1.0, flatted →3.4.2, min-document →2.19.2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(deps): drop noop @isaacs/brace-expansion override The package was no longer in the dep tree after the related minimatch/glob upgrades resolved upstream forks, making the override an unused defensive guard. Removing reduces noise in the overrides config. Verified: pnpm install --frozen-lockfile passes; next build compiles successfully (15.5.15, Turbopack, 95s). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deps): patch undici CVE via @actions/http-client and @actions/github overrides (#485) Forces @actions/http-client to ^3.0.2 and @actions/github to ^9.1.1 via pnpm.overrides. Both v3+ of http-client ship with undici@^6.23.0, eliminating the vulnerable undici@5.29.0 that was pulled in via the @sanity/cli → @sanity/template-validator@2.4.3 → @actions/github@6 chain. Sanity's own latest releases (template-validator@3.x) already depend on @actions/github@^9, so these versions are validated by the Sanity team. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(deps): resolve Dependabot/npm audit vulnerabilities via pnpm overrides (#487) * fix(wallet): parse CNGN rate when fetchRate returns numeric data --------- Co-authored-by: Isaac Onyemaechi Ugwu <Amaechiisaac450@gmail.com> Co-authored-by: Prosper <40717516+onahprosper@users.noreply.github.com> Co-authored-by: Onah Sunday. <sundayonah94@gmail.com> Co-authored-by: Agom Michael Junior <agomichaeljnr@gmail.com> Co-authored-by: Francis Ocholi <5raan6@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Description
This pull request introduces comprehensive Starknet Layer 2 support to the application, enabling wallet creation, public key retrieval, token transfers, and order creation on Starknet via new API routes. It also adds associated environment configuration for Starknet and refactors wallet address handling in the frontend to improve maintainability and reliability. Additionally, a minor fix expands wallet address validation to support 64-character hexadecimal addresses.
The most important changes are:
Starknet Integration:
Added new API endpoints for Starknet operations:
app/api/starknet/create-wallet/route.ts: Allows users to create a Starknet wallet via Privy, including checks for existing wallets.app/api/starknet/get-public-key/route.ts: Fetches the public key for a Starknet wallet from Privy.app/api/starknet/transfer/route.ts: Handles token transfers on Starknet, with support for paymaster-sponsored and default gas modes, including account deployment if necessary.app/api/starknet/create-order/route.ts: Creates an order on Starknet, handling token approvals, order creation, and paymaster configuration.Added Starknet configuration variables to
.env.example, including RPC URL, account class hash, paymaster settings, and gas token address.Frontend Refactor:
MainPageContent.tsxto use the newuseWalletAddresshook, improving code clarity and maintainability. [1] [2]Validation Improvements:
track-logout/route.tsto support both 40- and 64-character hexadecimal addresses.References
closes #321
Testing
see loom here Loom
Checklist
mainBy submitting a PR, I agree to Paycrest's Contributor Code of Conduct and Contribution Guide.
Summary by CodeRabbit
New Features
Bug Fixes
Chores