Skip to content

Feat/starknet clean#474

Merged
onahprosper merged 8 commits into
mainfrom
feat/starknet-clean
Apr 29, 2026
Merged

Feat/starknet clean#474
onahprosper merged 8 commits into
mainfrom
feat/starknet-clean

Conversation

@onahprosper
Copy link
Copy Markdown
Collaborator

@onahprosper onahprosper commented Apr 27, 2026

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:

Frontend Refactor:

  • Refactored wallet address handling in MainPageContent.tsx to use the new useWalletAddress hook, improving code clarity and maintainability. [1] [2]
  • Minor code formatting and cleanup in imports and function definitions for better readability. [1] [2]

Validation Improvements:

  • Expanded wallet address validation in track-logout/route.ts to support both 40- and 64-character hexadecimal addresses.

References

closes #321

Testing

see loom here Loom

  • This change adds test coverage for new/changed/fixed functionality

Checklist

  • I have added documentation and tests for new/changed functionality in this PR
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not main

By submitting a PR, I agree to Paycrest's Contributor Code of Conduct and Contribution Guide.

Summary by CodeRabbit

  • New Features

    • Full Starknet support: wallet creation/management, address normalization, paymaster-enabled transfers, on-chain order creation, Starknet-aware hooks/contexts, and UI/network-switch integration with app-wide Starknet balance tracking
  • Bug Fixes

    • Improved network request diagnostics and clearer error messages
    • Expanded wallet address validation to accept Starknet formats
  • Chores

    • Added Starknet configuration to env example and updated runtime dependencies

…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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Env & deps
\.env.example, package.json, jest.setup.js
Adds Starknet env vars (RPC, READY classhash, paymaster URL/mode/API key, gas token), upgrades @privy-io/server-auth, adds starknet runtime dependency, updates test env var.
Server Starknet APIs
app/api/starknet/.../route.ts
app/api/starknet/create-wallet/route.ts, app/api/starknet/get-public-key/route.ts, app/api/starknet/transfer/route.ts, app/api/starknet/create-order/route.ts
New JWT-protected POST endpoints implementing Privy lookups, raw-sign/paymaster integration, deployment vs execute flows, fee estimation, event parsing, and structured error handling.
Starknet core libs
app/lib/starknet.ts, app/lib/starknetRpc.ts, app/lib/authorization.ts
New utilities: compute Ready address, build/get/deploy Ready accounts, Privy raw-sign wrapper, cached user authorization key & signature builder, and RPC/provider/paymaster helpers.
Privy init
app/lib/privy.ts
Cache/lazy-init Privy client, validate env vars, optionally update wallet auth key (warn on failure).
Context & state
app/context/StarknetContext.tsx, app/context/BalanceContext.tsx, app/context/TokensContext.tsx, app/context/index.ts
Add StarknetContext/provider and useStarknet; add starknetWalletBalance and USD balances to balance context; exclude Starknet from fallback-token merge; re-export provider/hook.
UI wiring
app/components/...
app/components/Navbar.tsx, MobileDropdown.tsx, SettingsDropdown.tsx, NetworkSelectionModal.tsx, NetworksDropdown.tsx, MainPageContent.tsx
Prefer Starknet smart-wallet/address when on Starknet, integrate useStarknet and useWalletAddress, ensure wallet provisioning on network switch, clear Starknet cache on logout.
Transfer & transaction UI
app/components/TransferForm.tsx, app/components/WalletDetails.tsx, app/pages/TransactionForm.tsx, app/pages/TransactionPreview.tsx, app/pages/TransactionStatus.tsx, app/components/WalletTransferApprovalModal.tsx
Starknet-specific balance selection/validation; TransactionPreview adds create-order flow calling server API; status/analytics and balance refresh updates.
Hooks & transfer flow
app/hooks/useWalletAddress.ts, app/hooks/useSmartWalletTransfer.ts, app/hooks/useCNGNRate.ts
New useWalletAddress hook; extend smart-wallet transfer to POST to Starknet transfer API and persist transactions; skip CNGN rate fetch for Starknet.
Utilities & tokens
app/utils.ts, app/mocks.ts
Add starknetMainnet network, explorer/RPC mappings, normalizeStarknetAddress, fetchStarknetBalance, unified balance APIs, gateway mapping, update FALLBACK_TOKENS, and migration exclusion typing.
Aggregator & logout
app/api/aggregator.ts, app/api/track-logout/route.ts
Sanitize AGGREGATOR_URL, improve Axios network error messages, extend logout address regex to accept 64-hex Starknet addresses.
Providers
app/providers.tsx
Insert StarknetProvider into the provider hierarchy before TokensProvider.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • 5ran6
  • chibie

Poem

🐰 A rabbit's note on Starknet days:
Ready accounts and paymasters hum,
Privy signs the hash — away we run,
Hexes align and orders flow,
Little paws nudge transactions to go! 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/starknet clean' is overly vague and uses generic terms like 'clean' that don't convey meaningful information about the changeset, making it inconclusive. Use a more descriptive title such as 'Add Starknet Layer 2 integration with wallet creation and transfer support' to clearly summarize the main feature being added.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is mostly complete, detailing the main Starknet integration and refactoring changes with clear examples and references to linked issue #321.
Linked Issues check ✅ Passed The PR successfully addresses issue #321 by implementing comprehensive Starknet integration, including new API endpoints for wallet creation, public key retrieval, transfers, and order creation, plus frontend refactoring for improved wallet handling.
Out of Scope Changes check ✅ Passed All code changes are directly within scope of #321 Starknet integration; the wallet address validation fix for 64-character addresses is a justified supporting change for Starknet compatibility.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/starknet-clean

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 | 🟡 Minor

Duplicate BREVO_LIST_ID entry.

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 by getNetworkTokens() in BalanceContext.

📝 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.log statement, 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 blanket as Chain casts.

The current casts work today but remove compile-time/runtime safeguards. A small guard makes this path safer if migrationChecklistNetworks ever 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 userId is directly assigned from authUserId on line 32, which was already validated as truthy on lines 25-30. The subsequent check on lines 34-39 will never trigger since authUserId being 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" and account.id === walletId. For consistency with create-wallet/route.ts (which checks for chainType === "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 returns undefined if env var is not set.

Unlike other networks which construct URLs with optional rpcUrlKey, the Starknet case directly returns the environment variable which could be undefined. Callers should handle this case.

The createStarknetRpcProvider in app/lib/starknetRpc.ts accepts string | undefined, so this is handled, but explicit validation in fetchStarknetBalance would 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 fetchTokenPrice function calls CoinGecko's public API without any rate limiting or caching. In fetchStarknetBalance, this is called for each token in parallel, which could lead to rate limiting errors (CoinGecko's public API has strict limits).

Consider:

  1. Batching price requests using CoinGecko's multi-coin endpoint
  2. Adding a simple in-memory cache for prices
  3. 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 between createWallet implementation and interface.

The createWallet function returns Promise<string> (line 134), but the StarknetContextType interface declares createWallet: () => Promise<void> (line 22). The createWalletWrapper (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 (likely OrderCreated event 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 publicKey is destructured from the request body (line 40), but then getStarknetWallet is called to fetch walletPublicKey (line 114) which is used instead. Either remove the unused publicKey from 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 = true followed by if (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: Avoid any types for paymaster configuration.

Using any for paymasterDetails and maxFee loses 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 RawSigner class 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 buildReadyAccount to 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.ts and create-order/route.ts share 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

📥 Commits

Reviewing files that changed from the base of the PR and between cfa42dc and 4cd3165.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/logos/strk-logo.svg is excluded by !**/*.svg
📒 Files selected for processing (35)
  • .env.example
  • app/api/aggregator.ts
  • app/api/starknet/create-order/route.ts
  • app/api/starknet/create-wallet/route.ts
  • app/api/starknet/get-public-key/route.ts
  • app/api/starknet/transfer/route.ts
  • app/api/track-logout/route.ts
  • app/components/MainPageContent.tsx
  • app/components/MobileDropdown.tsx
  • app/components/Navbar.tsx
  • app/components/NetworkSelectionModal.tsx
  • app/components/NetworksDropdown.tsx
  • app/components/SettingsDropdown.tsx
  • app/components/TransferForm.tsx
  • app/components/WalletDetails.tsx
  • app/components/WalletTransferApprovalModal.tsx
  • app/context/BalanceContext.tsx
  • app/context/StarknetContext.tsx
  • app/context/TokensContext.tsx
  • app/context/index.ts
  • app/hooks/useCNGNRate.ts
  • app/hooks/useSmartWalletTransfer.ts
  • app/hooks/useWalletAddress.ts
  • app/lib/authorization.ts
  • app/lib/privy.ts
  • app/lib/starknet.ts
  • app/lib/starknetRpc.ts
  • app/mocks.ts
  • app/pages/TransactionForm.tsx
  • app/pages/TransactionPreview.tsx
  • app/pages/TransactionStatus.tsx
  • app/providers.tsx
  • app/utils.ts
  • jest.setup.js
  • package.json

Comment thread app/api/starknet/create-order/route.ts Outdated
Comment thread app/api/starknet/transfer/route.ts Outdated
Comment thread app/api/starknet/transfer/route.ts
Comment thread app/context/StarknetContext.tsx Outdated
Comment thread app/hooks/useSmartWalletTransfer.ts
Comment thread app/lib/authorization.ts
Comment thread app/lib/starknet.ts
Comment thread app/lib/starknet.ts
Comment thread app/lib/starknet.ts
Comment thread app/pages/TransactionStatus.tsx
…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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
app/lib/starknet.ts (1)

237-238: ⚠️ Potential issue | 🟡 Minor

Signature parsing assumes fixed length without validation.

The signature splitting logic (body.slice(0, 64) and body.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 0x3427759bfd3b941f14e687e129519da3c9b0046c5b9aaa290bb1dede63753b3 identifies the order creation event, and event.data[2] extracts the orderId. 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 = true on line 269 is always true, so the if (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

📥 Commits

Reviewing files that changed from the base of the PR and between 4cd3165 and 17cb540.

📒 Files selected for processing (6)
  • app/api/starknet/create-order/route.ts
  • app/api/starknet/transfer/route.ts
  • app/context/StarknetContext.tsx
  • app/hooks/useSmartWalletTransfer.ts
  • app/hooks/useWalletAddress.ts
  • app/lib/starknet.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/hooks/useWalletAddress.ts
  • app/hooks/useSmartWalletTransfer.ts

Comment thread app/api/starknet/transfer/route.ts Outdated
Comment thread app/context/StarknetContext.tsx
Comment thread app/context/StarknetContext.tsx
…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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/api/starknet/transfer/route.ts (1)

48-62: Consider removing publicKey from required fields.

The validation at line 49 requires publicKey from the request body, but line 115 ignores it and fetches the authoritative walletPublicKey directly from Privy via getStarknetWallet(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 ensureWalletExists always 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:

  1. Re-throwing from the outer catch, or
  2. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 17cb540 and d24b52d.

📒 Files selected for processing (2)
  • app/api/starknet/transfer/route.ts
  • app/context/StarknetContext.tsx

Comment thread app/api/starknet/transfer/route.ts
…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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between d24b52d and 8d2ecb7.

📒 Files selected for processing (5)
  • app/components/MainPageContent.tsx
  • app/components/WalletDetails.tsx
  • app/context/BalanceContext.tsx
  • app/mocks.ts
  • app/utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/mocks.ts

Comment thread app/context/BalanceContext.tsx
Comment thread app/utils.ts
Comment thread app/utils.ts Outdated
…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.
Comment thread app/api/starknet/create-order/route.ts Outdated
Comment thread app/api/starknet/create-order/route.ts
Comment thread app/api/starknet/create-order/route.ts
Comment thread app/api/starknet/create-wallet/route.ts Outdated
Comment thread app/context/StarknetContext.tsx Outdated
- 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.
@onahprosper onahprosper merged commit 1088d18 into main Apr 29, 2026
1 check passed
@onahprosper onahprosper deleted the feat/starknet-clean branch April 29, 2026 07:47
onahprosper added a commit that referenced this pull request May 1, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Starknet Network Support on Noblocks via Privy Tier 2 chain

2 participants