Skip to content

feat(starknet): add Earn product via Vesu lending pools#499

Open
starkience wants to merge 10 commits into
paycrest:mainfrom
starkience:feat/starknet-earn
Open

feat(starknet): add Earn product via Vesu lending pools#499
starkience wants to merge 10 commits into
paycrest:mainfrom
starkience:feat/starknet-earn

Conversation

@starkience
Copy link
Copy Markdown

@starkience starkience commented May 12, 2026

Description

Adds an Earn product to the Starknet wallet drawer, letting users supply USDC and USDT to Vesu lending pools through the starkzap SDK.

User-facing

  • "Earn" button in the wallet sidebar action grid (Starknet only), placed after Transfer and Fund.
  • Earn modal: deposit / withdraw tabs, token selector (Transfer-style FormDropdown), Max chips, live APR + monthly / yearly projection, success view mirroring TransferForm.
  • "Earn activity" tab next to Balances and Transactions: per-token "Currently supplied" card and a date-grouped history of deposits and withdrawals.
  • Activity detail page mirroring TransactionDetails: token + network logos, amount, pool name (linked to Vesu Pro), pool address (copy), date, status, "View in Explorer" link to Voyager.

Pool selection

  • USDC → Clearstar USDC Reactor (0x01bc5de5…5803)
  • USDT → Prime (0x0451fe48…c3b5)

Server-side

  • app/api/starknet/earn/{deposit,withdraw,position}/route.ts: auth via the existing verifyJWT pipeline; reuses getStarknetWallet, rawSign, buildReadyAccount, setupPaymaster, and deployReadyAccount unchanged.
  • The recipient address is derived server-side via computeReadyAddress(walletPublicKey, classHash) using the public key fetched from Privy. Clients do not supply the address, so a mismatch between the signing account and the Vesu position owner is impossible by construction.

Integration approach

  • starkzap (^3.0.0) handles Vesu market reads and deposit / withdraw call preparation. This integration uses only starkzap's Vesu module, not its Tongo or Hyperlane / Solana bridge features.
  • Because starkzap's barrel statically imports from those optional peer dependencies (@fatsolutions/tongo-sdk, @hyperlane-xyz/*, @solana/web3.js), next.config.mjs (and the matching Turbopack resolveAlias) routes them to a small Proxy shim (app/lib/_starkzap-unused-shim.ts) so the bundler resolves cleanly without installing the unused peers. The shim throws on any property access; at runtime this never fires because the Vesu path does not touch Tongo or Solana modules.
  • No changes to existing Privy plumbing. Wallet creation, auth, signing, and balance contexts are untouched.

Live APR

Fetched from https://api.vesu.xyz/markets via starkzap's VesuLendingProvider.getMarkets() (see app/lib/earn.ts:183-198) and polled every 30s while the modal or activity tab is open. No hardcoded values, no fallback rates. We display the gross supplyApy field; Vesu's own UI shows post-fee APR, the delta being the protocol feeRate returned in the same response.

Breaking changes: none.

References

None.

Testing

Automated (read-only, ~2s, hits live Vesu mainnet):

npx tsx scripts/test-starkzap.ts

Verifies: getMarkets resolves both pinned pools; supplyApy is in [0, 0.5]; position reads return well-formed structures; prepareDeposit / prepareWithdraw produce the correct call shapes (approve target, deposit / withdraw entrypoints, vToken vs underlying); pool-name to address mapping matches the Vesu API. Use this as a regression check on starkzap upgrades.

Manual:

  1. pnpm install && pnpm build: confirm the build passes.
  2. Log in with an embedded wallet on Starknet. The wallet drawer's action grid shows the new Earn button after Transfer and Fund.
  3. Open Earn. The modal shows the USDC / USDT selector; APR + monthly / yearly projection populate after token selection.
  4. Submit a deposit. Success popup appears and a transaction hash is returned.
  5. Open the Earn activity tab. The deposit appears under today's date with the correct token, amount, and pool.
  6. Click the activity row. The detail page shows token / network logos, pool name (linking to Vesu), pool address, and a "View in Explorer" link to Voyager.
  7. Switch to the Withdraw tab, enter an amount (or click Max), and submit. Balance and activity update.

Environment: Node 22.x, pnpm 10.27, macOS / Linux. TypeScript / Next.js 15.

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

Checklist

  • I have added documentation and tests for new/changed functionality in this PR

A read-only starkzap smoke test is included at scripts/test-starkzap.ts. No Jest / unit coverage was added; existing components that the new UI mirrors (TransferForm, TransactionDetails) retain their existing coverage.

  • 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

    • Earn flow: authenticated APIs to deposit, withdraw, and fetch Vesu positions for USDC/USDT with on‑chain submission, confirmation reporting, and transaction hashes; improved error and explorer guidance.
  • UI

    • Earn wallet form, activity panel, and details view; integrated Earn tab/button in wallet, explorer links, and earnings projections (monthly/yearly) with APR display.
  • Client

    • Local caching, cross‑tab sync, 30s refresh, and Starknet balance tracking including per‑token base units.
  • Chores

    • Lending-provider integration, bundler shim for optional peers, added runtime dependency.
  • Tests

    • Smoke-test validating provider, markets, and prepared call shapes.

Review Change Stack

starkience and others added 2 commits May 7, 2026 12:57
Adds an "Earn" action in the Starknet wallet drawer that lets users supply
USDC and USDT to Vesu lending pools through the starkzap SDK:

- USDC → Clearstar USDC Reactor pool
- USDT → Prime pool

Surfaces:
- New "Earn" action button in the wallet sidebar (Starknet only).
- Earn modal with deposit/withdraw tabs, token selector, live APR /
  monthly / yearly projection, and a success view mirroring Transfer.
- New "Earn activity" tab next to Balances and Transactions, with a
  per-token "Currently supplied" card and a date-grouped list of past
  deposits/withdrawals. Clicking a row opens a detail page with a
  Voyager link, mirroring the Transactions detail flow.

Integration:
- starkzap SDK (^3.0.0) for Vesu deposit/withdraw call preparation,
  market metadata, and position reads.
- next.config aliases shim out starkzap's optional peer deps so Webpack
  resolves cleanly without bloating node_modules.
- Reuses existing noblocks Privy primitives (getStarknetWallet, rawSign,
  buildReadyAccount, setupPaymaster, deployReadyAccount). No changes to
  wallet creation, auth, or signing pipelines.

Includes scripts/test-starkzap.ts: a read-only integration smoke test
that hits Vesu mainnet (~2s) to verify SDK call shapes and pool wiring.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address reviewer feedback from @0xLucqs on #1:
the deposit/withdraw routes accepted a wallet address from the request
body without verifying it matched the one derived by buildReadyAccount.
No attack vector (auth gates the route), but a buggy client could send
a mismatched address and credit a Vesu position to the wrong owner.

Cleanest fix per his suggestion: remove the field. Compute the canonical
address server-side via computeReadyAddress(walletPublicKey, classHash)
using the publicKey fetched from Privy in getStarknetWallet. That value
equals what buildReadyAccount derives, so mismatch is now impossible by
construction. Drops a redundant validateAndParseAddress block and the
unused starknet/validateAndParseAddress import; also drops the unused
deployReadyAccount import in withdraw.

Client (useEarnHandler) no longer sends address in deposit/withdraw
request bodies. UI readiness gate keeps the address check (it gates the
button, not the payload).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 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 a Starknet Earn feature: Starkzap/Vesu wrappers and shim, server routes (position/deposit/withdraw) with paymaster flows, client hook and UI, balance raw-unit plumbing, and a smoke-test script.

Changes

Starknet Earn Implementation

Layer / File(s) Summary
Starkzap Setup and Module Aliasing
app/lib/_starkzap-unused-shim.ts, next.config.mjs, package.json, scripts/package.json, scripts/test-starkzap.ts
Adds starkzap dependency and a shim to avoid bundling optional peer deps; adds webpack/turbopack aliases, an ESM marker for scripts, and a smoke-test harness for Starkzap/Vesu.
Earn Library and Vesu Call Preparation
app/lib/earn.ts
Core library wrapping Starkzap's VesuLendingProvider to prepare deposit/withdraw/withdraw-max calls and to fetch position data and supply APY from Vesu markets.
Position Query API Route
app/api/starknet/earn/position/route.ts
GET endpoint with Bearer JWT auth that validates address/token and returns a Vesu position summary (supplied base units as string, formatted amount, supply APY) via getVesuPosition.
Deposit API Route
app/api/starknet/earn/deposit/route.ts
POST endpoint that authenticates via Privy JWT, validates body, computes ready-account/address, probes deployment, initializes paymaster, prepares Vesu deposit calls, estimates fees for non-sponsored flows, executes deploy or paymaster transaction, waits/inspects receipt, and returns transaction details on success.
Withdraw API Route
app/api/starknet/earn/withdraw/route.ts
POST endpoint mirroring deposit logic but supporting fixed-amount or max withdrawals; validates deployment, prepares withdraw calls, estimates/applies fees, executes paymaster transaction, confirms receipt, and returns transaction hash with amount or null for max.
Shared Fee Margin Helper
app/lib/starknet.ts, app/api/starknet/*
Introduces applySafetyMargin and replaces inline BigInt 1.5x margin calculations with the shared helper across paymaster fee estimations.
useEarnHandler React Hook
app/hooks/useEarnHandler.ts
Client hook caching per-token positions and activity in localStorage, syncing same-tab instances via a custom event; exposes refreshPosition(token), refreshAllPositions(), deposit(token, amount), and `withdraw(token, amount
EarnActivityDetails Component
app/components/EarnActivityDetails.tsx
Client component rendering an animated details view for a single earn activity: formatted amount, pool info/link, pool address copy, date/time, fixed "completed" status, and optional Voyager tx link.
EarnActivityPanel Component
app/components/EarnActivityPanel.tsx
Client component showing per-token earning cards (supplied amount, APR, monthly/yearly projections) and a grouped activity timeline (relative-date buckets) with Framer Motion transitions and selection callbacks.
EarnWalletForm Component
app/components/EarnWalletForm.tsx
Client deposit/withdraw form with tabs, token dropdown, amount input (6-decimal base units) and validation, live earnings projection, polling refresh, submission via useEarnHandler, and inline success UI with optional explorer link.
WalletDetails Integration
app/components/WalletDetails.tsx
Integrates Earn UI into WalletDetails: Starknet-only Earn button/modal, Earn activity tab wired to EarnActivityPanel, sidebar detail rendering for earn activities, and modal rendering of EarnWalletForm.
Balance & Utils Changes
app/context/BalanceContext.tsx, app/utils.ts
Adds balancesInWei?: Record<string, bigint> to WalletBalances and extends Starknet balance fetching to include balancesInWei (per-token bigint base units) in the returned shape.
Starkzap Integration Tests
scripts/test-starkzap.ts
Smoke-test script validating Starkzap/Vesu integration: market discovery for pinned pools, APY sanity bounds, position shapes, deposit/withdraw call shapes, withdraw-max behavior, and pool name mapping checks.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Hook as useEarnHandler
  participant API as /api/starknet/earn
  participant Starkzap
  participant StarknetRPC
  Client->>Hook: deposit(token, amount) / withdraw(...)
  Hook->>API: POST /deposit or /withdraw (Bearer JWT)
  API->>Starkzap: prepareDeposit / prepareWithdraw
  Starkzap->>StarknetRPC: returns Call[] / estimates
  API->>StarknetRPC: execute paymaster tx or deploy ready account
  StarknetRPC-->>API: transactionHash / receipt
  API-->>Hook: { success, transactionHash }
Loading

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • paycrest/noblocks#190: Touches the CNGN-rate balance correction path; this PR extends balance plumbing to carry balancesInWei.
  • paycrest/noblocks#474: Introduced related Starknet fee/clean endpoints; this PR adds applySafetyMargin usage consistent with that work.

Suggested labels

enhancement

Suggested reviewers

  • onahprosper
  • chibie

"🐰 I hop through code and pockets bright,
USDC pools hum beneath moonlight,
Deposits skip and withdrawals stride,
Tiny ledger hops, a balanced tide,
A rabbit cheers each on-chain bite."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an Earn product to Starknet via Vesu lending pools.
Description check ✅ Passed The PR description comprehensively addresses all template requirements: clear purpose/background, implementation details, breaking changes (none), pool selection details, server-side and integration approach explanation, testing instructions (both automated and manual), and checklist completion.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 4

🧹 Nitpick comments (2)
scripts/test-starkzap.ts (1)

57-79: 💤 Low value

Consider importing constants from app/lib/earn.ts to avoid duplication.

The constants are duplicated with a comment "kept in sync by hand." This creates a maintenance burden and risk of drift. Since this script can use ES imports, consider importing these values directly from app/lib/earn.ts.

However, if the goal is to have a truly independent smoke test that doesn't rely on Next.js module resolution, the current approach is acceptable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test-starkzap.ts` around lines 57 - 79, Replace the duplicated token
and pool constants with imports from the canonical source in app/lib/earn.ts:
remove the local STARKNET_USDC_TOKEN, STARKNET_USDT_TOKEN,
VESU_USDC_POOL_ADDRESS, and VESU_USDT_POOL_ADDRESS definitions and import the
corresponding exported constants (or names used there) instead; ensure the ES
import path resolves in this script (adjust relative path or
tsconfig/moduleResolution as needed) so the script uses the single source of
truth.
app/lib/earn.ts (1)

161-167: ⚡ Quick win

Consider more granular error handling for position fetching.

The try-catch block swallows all errors and returns an empty positions array. This conflates "user has no position" with "RPC/network error" or "provider malfunction." While returning a safe default prevents crashes, it may hide real connectivity or API issues from monitoring.

Consider either:

  • Logging the error before returning the fallback
  • Allowing certain error types (e.g., network failures) to propagate
  • Returning a structured result that distinguishes "no position" from "fetch failed"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/lib/earn.ts` around lines 161 - 167, The try-catch around
provider.getPositions (the call using provider.getPositions?.(ctx, { user:
fromAddress(walletAddress) })) currently swallows all errors; update the catch
to log the caught error with context (walletAddress and the
provider/getPositions call) using the module logger (e.g., processLogger or
logger) and then either rethrow network/critical errors or return a structured
result so callers can distinguish "no positions" from "fetch failed" (e.g.,
return { positions: [], error } or throw for retryable errors). Ensure you
modify the handling around the positions variable and provider.getPositions
usage so the code both logs details and exposes the failure state instead of
silently returning an empty array.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/api/starknet/earn/withdraw/route.ts`:
- Around line 219-231: The code currently swallows errors from
account.waitForTransaction and can still return { success: true } later; update
the withdrawal handler so that if account.waitForTransaction throws or
txReceipt.isSuccess() is false you do NOT return confirmed success.
Specifically, in the try/catch around
account.waitForTransaction(result.transaction_hash) (and the txReceipt check),
either return a non-success JSON response (e.g., { error: "Transaction not
confirmed" } with appropriate 4xx/5xx or 202 pending) or rethrow the error so
the outer handler can return failure; ensure you reference
account.waitForTransaction and txReceipt.isSuccess() when making the change and
do not leave an empty catch that masks confirmation failures.

In `@app/components/EarnActivityDetails.tsx`:
- Around line 183-190: The copy button inside the EarnActivityDetails component
is icon-only and needs an explicit accessible label; add an aria-label (for
example aria-label="Copy pool address") to the <button> that calls
copyToClipboard(pool.address, "Pool address") so screen readers announce the
action; locate the button element in EarnActivityDetails (the onClick handler
invoking copyToClipboard) and add the aria-label attribute with a clear
descriptive phrase.

In `@app/components/EarnActivityPanel.tsx`:
- Around line 201-207: The activity row uses a clickable motion.div which isn’t
keyboard-accessible; replace the element with a semantic interactive element by
using motion.button (from framer-motion) or wrap the content in a <button
type="button"> so the existing onClick prop becomes keyboard-operable, keep the
motion props (initial, animate, exit) and className, and ensure you add
type="button" to avoid form submissions and preserve any focus/hover styles and
aria attributes for accessibility.

In `@app/components/EarnWalletForm.tsx`:
- Around line 79-86: The code converts a display number back to base units
causing precision loss; update the Starknet balance flow so EarnWalletForm reads
native base units instead of reconstructing them: extend
fetchStarknetBalancesUnified to populate balancesInWei (matching the EVM
pattern), update the WalletBalances type in BalanceContext to include
balancesInWei for starknetWallet, and modify EarnWalletForm to use
starknetWallet.balancesInWei[token] (or BigInt("0") fallback) for
walletBalanceBaseUnits instead of parsing walletBalanceUnit.toString(); keep
existing display balances for UI only.

---

Nitpick comments:
In `@app/lib/earn.ts`:
- Around line 161-167: The try-catch around provider.getPositions (the call
using provider.getPositions?.(ctx, { user: fromAddress(walletAddress) }))
currently swallows all errors; update the catch to log the caught error with
context (walletAddress and the provider/getPositions call) using the module
logger (e.g., processLogger or logger) and then either rethrow network/critical
errors or return a structured result so callers can distinguish "no positions"
from "fetch failed" (e.g., return { positions: [], error } or throw for
retryable errors). Ensure you modify the handling around the positions variable
and provider.getPositions usage so the code both logs details and exposes the
failure state instead of silently returning an empty array.

In `@scripts/test-starkzap.ts`:
- Around line 57-79: Replace the duplicated token and pool constants with
imports from the canonical source in app/lib/earn.ts: remove the local
STARKNET_USDC_TOKEN, STARKNET_USDT_TOKEN, VESU_USDC_POOL_ADDRESS, and
VESU_USDT_POOL_ADDRESS definitions and import the corresponding exported
constants (or names used there) instead; ensure the ES import path resolves in
this script (adjust relative path or tsconfig/moduleResolution as needed) so the
script uses the single source of truth.
🪄 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: 47bbaec8-df29-4479-89e6-5786a861a515

📥 Commits

Reviewing files that changed from the base of the PR and between f81c0bf and 22fa214.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • app/api/starknet/earn/deposit/route.ts
  • app/api/starknet/earn/position/route.ts
  • app/api/starknet/earn/withdraw/route.ts
  • app/components/EarnActivityDetails.tsx
  • app/components/EarnActivityPanel.tsx
  • app/components/EarnWalletForm.tsx
  • app/components/WalletDetails.tsx
  • app/hooks/useEarnHandler.ts
  • app/lib/_starkzap-unused-shim.ts
  • app/lib/earn.ts
  • next.config.mjs
  • package.json
  • scripts/package.json
  • scripts/test-starkzap.ts

Comment thread app/api/starknet/earn/withdraw/route.ts
Comment thread app/components/EarnActivityDetails.tsx
Comment thread app/components/EarnActivityPanel.tsx Outdated
Comment thread app/components/EarnWalletForm.tsx Outdated
starkience and others added 3 commits May 12, 2026 11:27
- deposit/withdraw routes: log a warning when waitForTransaction soft-fails,
  matching the existing pattern in app/api/starknet/transfer/route.ts
  instead of silently swallowing the error
- EarnActivityDetails: add aria-label to icon-only copy button
- EarnActivityPanel: clickable row uses motion.button (with type="button"
  and w-full/text-left/bg-transparent so the layout matches the prior
  motion.div) so the row is keyboard-operable
- earn.ts: log getPositions failures via console.error before falling back
  to the empty positions array, so production debugging can distinguish
  "user has no position" from "fetch failed"

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CodeRabbit follow-up on PR paycrest#499:

1. Deposit / withdraw responses now include a `confirmed: boolean` field.
   When `waitForTransaction` throws (network error, RPC hiccup), the
   handler still returns `success: true` with the tx hash so the client
   gets the optimistic UX, but `confirmed: false` so the response is
   honest about the on-chain state. The client can ignore the field
   today (preserves current UX) or surface it in a future pass.

2. Starknet wallet balances now carry `balancesInWei: Record<string,
   bigint>` end-to-end:
   - `fetchStarknetBalancesUnified` (app/utils.ts) was already computing
     the per-token bigint internally; it now stores and returns it,
     mirroring the EVM path.
   - `fetchStarknetBalance` wrapper exposes the new field.
   - `WalletBalances` interface (app/context/BalanceContext.tsx) declares
     `balancesInWei?` as an optional field, so existing consumers are
     unaffected.
   - `EarnWalletForm` reads `allBalances.starknetWallet?.balancesInWei?.[token]`
     directly instead of the lossy `parseAmountToBaseUnits(walletBalanceUnit.toString())`
     round-trip. The Max button and amount validation now use exact base
     units, eliminating precision loss at the 6th USDC decimal.

The `WalletBalances` change is backward-compatible (optional field with
a `BigInt("0")` fallback in the consumer); other balance consumers
(Transfer, swap, balance display) are unaffected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/utils.ts (1)

904-910: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add balanceWei to Starknet entries for consistency with EVM.

Starknet ChainBalanceEntry objects don't populate the balanceWei field, while EVM entries do (Line 784). This inconsistency could break code that expects balanceWei to be present for per-token exact-integer math.

🔧 Proposed fix
 const entries: ChainBalanceEntry[] = tokens.map((token) => ({
   chainName,
   symbol: token.symbol,
   address: token.address,
   decimals: token.decimals,
   balance: balances[token.symbol] ?? 0,
+  balanceWei: balancesInWei[token.symbol],
 }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/utils.ts` around lines 904 - 910, The Starknet token entries created in
the tokens.map (producing ChainBalanceEntry objects) are missing the balanceWei
field; update the map that builds entries to include balanceWei (populate it the
same way EVM entries do) by pulling the per-token exact-integer wei value (e.g.,
from the existing Wei-balances map or by converting balance+decimals into the
integer wei representation) so every ChainBalanceEntry (created in the
tokens.map) includes a properly typed balanceWei value.
app/context/BalanceContext.tsx (1)

117-135: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Critical: balancesInWei is lost when applying CNGN conversion for EVM balances.

The JSDoc at Line 47 states that balancesInWei is "Populated by Starknet and EVM fetchers," but EVM balances in this context lose balancesInWei when buildWalletBalancesFromRaw is called:

  1. fetchWalletBalance (utils.ts:973) returns balancesInWei.
  2. Callers extract only result.balances (e.g., Line 295: const rawBalances = { ...rawResult.balances }).
  3. buildWalletBalancesFromRaw accepts only rawBalances (human-readable numbers) and doesn't preserve balancesInWei.
  4. Result: balancesInWei is discarded.

This affects Lines 295, 480, 511, 535, 572, 594, 622. The Starknet path (Line 369-371) is fine because fetchStarknetBalance result is set directly without CNGN conversion.

Impact: The Earn feature needs balancesInWei for exact contract call amounts. If Earn UI reads from BalanceContext, it won't find balancesInWei for EVM networks, breaking deposit/withdraw flows.

🔧 Proposed fix

Update buildWalletBalancesFromRaw to accept and preserve balancesInWei:

 function buildWalletBalancesFromRaw(
   rawBalances: Record<string, number>,
   rate: number | null,
+  balancesInWei?: Record<string, bigint>,
 ): WalletBalances {
   const rawTotal = Object.values(rawBalances).reduce(
     (s, v) => s + (typeof v === "number" && !isNaN(v) ? v : 0),
     0,
   );
   const rawResult = { total: rawTotal, balances: rawBalances };
   const correctedTotal = calculateCorrectedTotalBalance(rawResult, rate);
   const correctedBalances = applyCNGNBalanceConversion(rawBalances, rate);
   return {
     total: correctedTotal,
     balances: correctedBalances,
     rawBalances: { ...rawBalances },
+    balancesInWei,
     cngnUsdUnknown:
       hasPositiveCngn(rawBalances) && !(rate != null && rate > 0),
   };
 }

Then update all call sites to pass balancesInWei. Example for Line 295:

-const rawBalances = { ...rawResult.balances };
-return {
-  network,
-  balances: buildWalletBalancesFromRaw(rawBalances, cngnRateValue),
-};
+const rawBalances = { ...rawResult.balances };
+const rawBalancesInWei = rawResult.balancesInWei;
+return {
+  network,
+  balances: buildWalletBalancesFromRaw(rawBalances, cngnRateValue, rawBalancesInWei),
+};

Apply similar changes to Lines 480, 511, 535, 572, 594, 622.

Also applies to: 295-299, 480-480, 511-511, 535-535, 572-572, 594-594, 622-622

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/context/BalanceContext.tsx` around lines 117 - 135,
buildWalletBalancesFromRaw currently only accepts human-readable rawBalances and
drops balancesInWei from EVM fetchers; update buildWalletBalancesFromRaw to
accept an optional balancesInWei param and preserve it on the returned
WalletBalances object (return balancesInWei: balancesInWei ? { ...balancesInWei
} : undefined), while keeping existing behavior for
calculateCorrectedTotalBalance and applyCNGNBalanceConversion; then update each
call site that uses fetchWalletBalance (e.g., where rawBalances is constructed
from rawResult.balances in the callers referenced) to pass
rawResult.balancesInWei (or undefined) into buildWalletBalancesFromRaw so EVM
flows retain balancesInWei for Earn contract calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/context/BalanceContext.tsx`:
- Around line 117-135: buildWalletBalancesFromRaw currently only accepts
human-readable rawBalances and drops balancesInWei from EVM fetchers; update
buildWalletBalancesFromRaw to accept an optional balancesInWei param and
preserve it on the returned WalletBalances object (return balancesInWei:
balancesInWei ? { ...balancesInWei } : undefined), while keeping existing
behavior for calculateCorrectedTotalBalance and applyCNGNBalanceConversion; then
update each call site that uses fetchWalletBalance (e.g., where rawBalances is
constructed from rawResult.balances in the callers referenced) to pass
rawResult.balancesInWei (or undefined) into buildWalletBalancesFromRaw so EVM
flows retain balancesInWei for Earn contract calls.

In `@app/utils.ts`:
- Around line 904-910: The Starknet token entries created in the tokens.map
(producing ChainBalanceEntry objects) are missing the balanceWei field; update
the map that builds entries to include balanceWei (populate it the same way EVM
entries do) by pulling the per-token exact-integer wei value (e.g., from the
existing Wei-balances map or by converting balance+decimals into the integer wei
representation) so every ChainBalanceEntry (created in the tokens.map) includes
a properly typed balanceWei value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c67a3ca-09f4-4e4c-b4e4-614ca15b20f9

📥 Commits

Reviewing files that changed from the base of the PR and between 0bb88c4 and c7ee937.

📒 Files selected for processing (5)
  • app/api/starknet/earn/deposit/route.ts
  • app/api/starknet/earn/withdraw/route.ts
  • app/components/EarnWalletForm.tsx
  • app/context/BalanceContext.tsx
  • app/utils.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/api/starknet/earn/deposit/route.ts
  • app/api/starknet/earn/withdraw/route.ts
  • app/components/EarnWalletForm.tsx

CodeRabbit follow-up on PR paycrest#499: the previous commit added
`balancesInWei?` to the WalletBalances interface and plumbed it through
the Starknet path, but EVM balances still discarded it in
`buildWalletBalancesFromRaw`. That made the JSDoc claim ("Populated by
Starknet and EVM fetchers") misleading and left a latent precision gap
for any future caller doing exact-integer math on EVM amounts.

Concretely:
- `buildWalletBalancesFromRaw` now accepts an optional `balancesInWei`
  parameter and returns it on the WalletBalances object. The CNGN
  conversion logic is unchanged; base units bypass the float corridor
  entirely (correct, since CNGN <-> NGN is a display-only rate).
- All 7 call sites in BalanceContext.tsx that build WalletBalances from
  a `fetchWalletBalance` result now pass `result.balancesInWei` through.
- The two patch sites in the `[cngnRate]` useEffect (re-applying the
  rate after it resolves) preserve `balancesInWei` from the existing
  WalletBalances object.
- `fetchStarknetBalancesUnified` (utils.ts) populates `balanceWei` on
  each ChainBalanceEntry, matching the EVM entries shape so per-token
  exact-integer math is available downstream.

All changes are backward-compatible: `balancesInWei` is optional on
WalletBalances, and existing consumers that read only `balances`
(display number) are unaffected. The roadmap to bridge EVM USDC into
Vesu via the Earn product can now read `allBalances.<chain>?.balancesInWei?.[token]`
on any network without further infrastructure changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@starkience
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread app/lib/earn.ts
Comment thread app/api/starknet/earn/deposit/route.ts
starkience and others added 2 commits May 14, 2026 11:58
…ails

Addresses CodeRabbit's follow-up on PR paycrest#499 (after @onahprosper asked
for a recheck). The previous approach (return `success: true,
confirmed: false`) relied on a client-side check that does not exist
in useEarnHandler, so withdrawals where on-chain confirmation failed
were still being recorded as completed activity in the UI.

Switching to CodeRabbit's original suggestion of an HTTP 502 response:
the failure is now honest at the protocol level, the existing client
check (`!res.ok || !data?.success`) treats it correctly without any
client change, and the response body still carries `transactionHash`
so callers can point the user to the explorer.

Drops the now-unused `confirmed` flag from both deposit and withdraw
response shapes. Same simplification applied to both routes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Addresses @onahprosper's review feedback on PR paycrest#499: replaces the
hard-coded EARN_TOKENS dropdown with a runtime fetch of Paycrest's
supported-token list (via the existing `getNetworkTokens("Starknet")`
helper, which already wraps `api.paycrest.io/v2/tokens` with cache +
fallback).

New `useEarnAvailableTokens()` hook in useEarnHandler.ts:

- Fetches Paycrest's Starknet token list on mount.
- Intersects with `EARN_TOKENS` (the registry of tokens we have Vesu
  pool mappings for) so we never expose a token without a pool.
- Falls back to the full `EARN_TOKENS` list if Paycrest is unreachable
  or returns no Starknet tokens.

EarnWalletForm.tsx now consumes the hook for both the dropdown items
and the `onSelect` handler (the previous `name === "USDT" ? ... : ...`
hardcode is replaced with a lookup against the dynamic list).

Effect: when Paycrest adds a new Starknet token AND we add a matching
Vesu pool mapping to `EARN_TOKEN_CONFIG`, the new token appears in
the UI with no further code change. The `EARN_TOKENS` registry remains
the source of truth for which tokens our backend can build calls for;
it is kept static (and exported) for type-level coverage and for the
position-refresh / activity-filter loops that should iterate every
supported token regardless of Paycrest's current advertisement.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@starkience
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/api/starknet/earn/deposit/route.ts`:
- Around line 214-264: The fee estimation is being run for undeployed accounts
and its result is unused on the deploy path; only estimate fees for the execute
path. Change the guard to run account.estimatePaymasterTransactionFee (and
compute withMargin15) only when isDeployed && !isSponsored, keep maxFee assigned
there, and ensure maxFee is passed into account.executePaymasterTransaction; do
not call estimatePaymasterTransactionFee or apply withMargin15 before calling
deployReadyAccount — rely on deployReadyAccount's internal deployment-aware fee
handling instead.

In `@app/api/starknet/earn/withdraw/route.ts`:
- Around line 189-193: The helper named withMargin15 is misnamed and duplicated;
replace it with a single shared utility (e.g., rename to inflateFeeBy50Pct or
applySafetyMargin) exported from a common module (e.g., app/lib/starknet) that
implements ceil(1.5x) using the existing formula ((bi * 3 + 1) / 2) on BigInt
inputs, then update all call sites that do maxFee =
withMargin15(est.suggested_max_fee_in_gas_token) (and other occurrences) to
import and call the new function; add a short doc comment "Apply 1.5x (50%)
safety margin, ceil" on the new function and remove the duplicated local
definitions.
🪄 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: 1463edd5-e082-4bc3-a777-e1b2ef77f07f

📥 Commits

Reviewing files that changed from the base of the PR and between c7ee937 and 914fb52.

📒 Files selected for processing (6)
  • app/api/starknet/earn/deposit/route.ts
  • app/api/starknet/earn/withdraw/route.ts
  • app/components/EarnWalletForm.tsx
  • app/context/BalanceContext.tsx
  • app/hooks/useEarnHandler.ts
  • app/utils.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/context/BalanceContext.tsx
  • app/utils.ts
  • app/components/EarnWalletForm.tsx

Comment thread app/api/starknet/earn/deposit/route.ts
Comment thread app/api/starknet/earn/withdraw/route.ts Outdated
starkience and others added 2 commits May 14, 2026 13:09
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
… fee estimation on isDeployed

Addresses two CodeRabbit comments on PR paycrest#499:

1. Fee estimation runs on undeployed accounts without deployment context, and
   the result is discarded on the deploy path.

   In `app/api/starknet/earn/deposit/route.ts`, the fee-estimation block now
   only runs when `isDeployed && !isSponsored`. On the first-deposit path
   (account not yet deployed), `deployReadyAccount` already builds its own
   `deploy_and_invoke` paymaster transaction with proper `deploymentData`
   and performs its own deployment-aware fee estimation internally, so
   estimating here without `deploymentData` produced an inaccurate fee that
   was never used. Withdraw is unaffected because it already rejects
   undeployed accounts upfront.

2. `withMargin15` was misleadingly named and duplicated across 5 sites.

   The helper actually applies a 1.5x (50%) safety margin via
   `(bi * 3 + 1) / 2`, not 15%. Extracted into a single shared
   `applySafetyMargin(v)` export in `app/lib/starknet.ts` with a clarifying
   doc comment. All 5 call sites updated to import and use it:

   - `app/lib/starknet.ts:deployReadyAccount`
   - `app/api/starknet/transfer/route.ts`
   - `app/api/starknet/create-order/route.ts`
   - `app/api/starknet/earn/deposit/route.ts`
   - `app/api/starknet/earn/withdraw/route.ts`

   The local duplicate copies are removed; the formula is unchanged. This
   keeps the cross-route fee-margin behaviour consistent and gives the
   helper an honest name that matches the math.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@starkience
Copy link
Copy Markdown
Author

Hi @onahprosper — addressing your four threads from commit 3e70153:

  • Withdraw confirmation (withdraw/route.ts): hardened to return 502 on waitForTransaction failure — e512610. Matches CodeRabbit's Option A (no soft-success).
  • EarnWalletForm precision (EarnWalletForm.tsx): replaced the lossy number → bigint round-trip with end-to-end balancesInWei plumbing through BalanceContext + utils.ts3e70153.
  • Token list from aggregator (earn.ts): useEarnAvailableTokens() now fetches the Paycrest Starknet token list via getNetworkTokens('Starknet') and intersects with the Vesu pool registry — 914fb52.
  • walletAddress normalisation (deposit/route.ts:68): the header is analytics-only and never reaches the signer (signing address is derived server-side via computeReadyAddress). Same pattern as transfer/route.ts:335. Happy to add validateAndParseAddress defensively if you'd prefer.

CodeRabbit's two follow-up items (applySafetyMargin extraction, gate fee estimation on isDeployed) landed in 95b6f8d and e8fb724. Re-requesting review.

@starkience
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/api/starknet/transfer/route.ts (1)

226-233: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate fee estimation by deployment state to avoid false 500s.

At Line 226, estimation runs for all non-sponsored requests, but undeployed accounts are handled later by deployReadyAccount (Line 254). If estimation fails, the handler returns 500 at Line 243 and never reaches deploy flow. Gate this block with isDeployed.

💡 Proposed fix
-    if (!isSponsored) {
+    if (!isSponsored && isDeployed) {
       try {
         const est = await account.estimatePaymasterTransactionFee(
           calls,
           paymasterDetails,
         );
         maxFee = applySafetyMargin(est.suggested_max_fee_in_gas_token);
       } catch (error: any) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/starknet/transfer/route.ts` around lines 226 - 233, The fee
estimation for non-sponsored requests should only run when the account is
already deployed; wrap the existing estimation block (the isSponsored check that
calls account.estimatePaymasterTransactionFee, sets maxFee via applySafetyMargin
on est.suggested_max_fee_in_gas_token, and catches errors) with an additional
guard checking isDeployed so that undeployed accounts proceed to
deployReadyAccount instead of triggering the catch path and returning a 500;
ensure paymasterDetails and calls still flow into the estimation when isDeployed
is true and preserve the existing error handling and maxFee assignment.
app/api/starknet/create-order/route.ts (1)

280-287: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid pre-deploy estimation failures blocking create-order.

At Line 280, fee estimation runs before the deployment branch at Line 307. For undeployed accounts, estimation errors return 500 (Line 297) and prevent deployReadyAccount from executing. Restrict estimation to deployed accounts.

💡 Proposed fix
-    if (!isSponsored) {
+    if (!isSponsored && isDeployed) {
       try {
         const est = await account.estimatePaymasterTransactionFee(
           calls,
           paymasterDetails,
         );
         maxFee = applySafetyMargin(est.suggested_max_fee_in_gas_token);
       } catch (error: any) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/starknet/create-order/route.ts` around lines 280 - 287, The fee
estimation is running for undeployed accounts and can throw, blocking
deployReadyAccount; move or guard the call to
account.estimatePaymasterTransactionFee (and applySafetyMargin) so it only runs
for already-deployed accounts (e.g., check the existing deployment indicator
used in this file such as deployReadyAccount/deployTransactionHash or an
isDeployed flag) and skip estimation when the account is not deployed or when
isSponsored is true, allowing deployReadyAccount to proceed uninterrupted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/api/starknet/create-order/route.ts`:
- Around line 280-287: The fee estimation is running for undeployed accounts and
can throw, blocking deployReadyAccount; move or guard the call to
account.estimatePaymasterTransactionFee (and applySafetyMargin) so it only runs
for already-deployed accounts (e.g., check the existing deployment indicator
used in this file such as deployReadyAccount/deployTransactionHash or an
isDeployed flag) and skip estimation when the account is not deployed or when
isSponsored is true, allowing deployReadyAccount to proceed uninterrupted.

In `@app/api/starknet/transfer/route.ts`:
- Around line 226-233: The fee estimation for non-sponsored requests should only
run when the account is already deployed; wrap the existing estimation block
(the isSponsored check that calls account.estimatePaymasterTransactionFee, sets
maxFee via applySafetyMargin on est.suggested_max_fee_in_gas_token, and catches
errors) with an additional guard checking isDeployed so that undeployed accounts
proceed to deployReadyAccount instead of triggering the catch path and returning
a 500; ensure paymasterDetails and calls still flow into the estimation when
isDeployed is true and preserve the existing error handling and maxFee
assignment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06ae40b3-06d0-4264-982b-4af1eeda0f47

📥 Commits

Reviewing files that changed from the base of the PR and between 914fb52 and 95b6f8d.

📒 Files selected for processing (5)
  • app/api/starknet/create-order/route.ts
  • app/api/starknet/earn/deposit/route.ts
  • app/api/starknet/earn/withdraw/route.ts
  • app/api/starknet/transfer/route.ts
  • app/lib/starknet.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/api/starknet/earn/withdraw/route.ts
  • app/api/starknet/earn/deposit/route.ts

@starkience
Copy link
Copy Markdown
Author

Re: CodeRabbit review #pullrequestreview-4290058247 — both findings (transfer/route.ts:226-233, create-order/route.ts:280-287) are technically valid, but they flag pre-existing behavior in routes the Earn feature doesn't touch:

  • No Earn code path calls transfer or create-order (verified across app/api/starknet/earn/*, useEarnHandler.ts, and Earn components).
  • Earn's own routes handle the pattern correctly already:
    • deposit/route.ts — gated on isDeployed && !isSponsored in e8fb724
    • withdraw/route.ts — rejects undeployed accounts upfront (HTTP 400, lines 141–145)
    • position/route.ts — read-only, no fee estimation
  • The flagged code is unchanged from upstream/main; CodeRabbit picked it up because 95b6f8d touched these files for the applySafetyMargin refactor.

Keeping this PR scoped to Earn. The e8fb724 one-line fix transfers cleanly to both routes whenever the Noblocks team chooses to apply it. Happy to mirror it in a separate small PR or issue if that's preferred.

Copy link
Copy Markdown
Contributor

@5ran6 5ran6 left a comment

Choose a reason for hiding this comment

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

LGTM

@onahprosper
Copy link
Copy Markdown
Collaborator

Re: CodeRabbit review #pullrequestreview-4290058247 — both findings (transfer/route.ts:226-233, create-order/route.ts:280-287) are technically valid, but they flag pre-existing behavior in routes the Earn feature doesn't touch:

  • No Earn code path calls transfer or create-order (verified across app/api/starknet/earn/*, useEarnHandler.ts, and Earn components).

  • Earn's own routes handle the pattern correctly already:

    • deposit/route.ts — gated on isDeployed && !isSponsored in e8fb724
    • withdraw/route.ts — rejects undeployed accounts upfront (HTTP 400, lines 141–145)
    • position/route.ts — read-only, no fee estimation
  • The flagged code is unchanged from upstream/main; CodeRabbit picked it up because 95b6f8d touched these files for the applySafetyMargin refactor.

Keeping this PR scoped to Earn. The e8fb724 one-line fix transfers cleanly to both routes whenever the Noblocks team chooses to apply it. Happy to mirror it in a separate small PR or issue if that's preferred.

All good.

@onahprosper
Copy link
Copy Markdown
Collaborator

Hi @onahprosper — addressing your four threads from commit 3e70153:

  • Withdraw confirmation (withdraw/route.ts): hardened to return 502 on waitForTransaction failure — e512610. Matches CodeRabbit's Option A (no soft-success).
  • EarnWalletForm precision (EarnWalletForm.tsx): replaced the lossy number → bigint round-trip with end-to-end balancesInWei plumbing through BalanceContext + utils.ts3e70153.
  • Token list from aggregator (earn.ts): useEarnAvailableTokens() now fetches the Paycrest Starknet token list via getNetworkTokens('Starknet') and intersects with the Vesu pool registry — 914fb52.
  • walletAddress normalisation (deposit/route.ts:68): the header is analytics-only and never reaches the signer (signing address is derived server-side via computeReadyAddress). Same pattern as transfer/route.ts:335. Happy to add validateAndParseAddress defensively if you'd prefer.

CodeRabbit's two follow-up items (applySafetyMargin extraction, gate fee estimation on isDeployed) landed in 95b6f8d and e8fb724. Re-requesting review.

All good mate

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.

3 participants