Skip to content

chore: back-merge main into dev (2026-09-07) - #3020

Merged
innolope-dev merged 20 commits into
devfrom
chore/back-merge-main-2026-09-07
Sep 7, 2026
Merged

innolope-dev merged 20 commits into
devfrom
chore/back-merge-main-2026-09-07

Conversation

@innolope-dev

Copy link
Copy Markdown
Collaborator

Summary

Back-merges current main into dev. dev had 18 commits from main not yet merged, most recently main's TASK-22185 watchdog fix (2026-09-04) — no open back-merge PR existed for this yet.

8 conflicts, all in the same area: the cross-chain withdraw cap feature (main's xchain-withdraw-limit / TASK-22154) landed the same week dev was independently extending the cross-chain withdraw flow (rate limiting, stale-quote guards, expired-quote-vs-error retry UX). This is the frontend half of the equivalent back-merge on peanut-api-ts (peanutprotocol/peanut-api-ts#1541).

What conflicted and how it was resolved

  • api-error.ts / friendly-error.utils.tsx — both sides added different wire error codes / friendly-copy branches (LINK_ALREADY_CLAIMED+BELOW_MIN_BRIDGE_AMOUNT vs XCHAIN_WITHDRAW_LIMIT_REACHED). Kept both.
  • services/rhino-sda.ts / rhino-bridge.ts — dev used the shared apiFetch helper with an ad-hoc thrown error; main used a lower-level fetchWithSentry+manual-auth-headers approach with a proper ApiError (carries retryAfterSec, needed for the new cap 429's countdown copy). apiFetch already wraps fetchWithSentry/auth/demo-mode/dev-fixtures — strictly more complete — so kept it, but swapped the error construction to apiErrorFromResponse (main's), since wireErrorCode/cooldownMinutes need the retryAfterSec it captures and the old ad-hoc error didn't carry it. Verified rhino-bridge.ts's own postJson already combined the two this exact way, unconflicted — confirms this is the intended shape.
  • useCrossChainTransfer.ts — dev added a stale-response guard (live() wrapping every setter, since concurrent quote recalculations are routine on re-quote) and a quoteId null-assertion before commit; main added context/contextId threading into the bridge path (required — commitBridgeQuote's signature already takes a 4th charge param, confirmed by grep) so the cap gate covers bridge commits too. Combined: kept the live() guards, added context/contextId to the bridge path call and to commitBridgeQuote, and swapped the plain-string catch handler for toFriendlyError(...) (still live()-wrapped) so the cap's localized 429 copy renders instead of the raw error.
  • useCrossChainTransfer.test.ts — rewrote to merge both test suites (dev's fee/quote-math + stale-response-guard tests, main's charge-naming tests) under one mock setup; useFriendlyError now needs mocking for every test since the hook calls it unconditionally, not just on error.
  • withdraw/crypto/page.tsx — dev and main each defined what looked like the same "quote the route" callback with different names (quoteRoute vs calculateCurrentRoute) and different amount variables (effectiveAmount, the frozen sign-time amount, vs amountToWithdraw, the live input amount). Turned out both are needed and both are referenced elsewhere unconflicted — quoteRoute re-quotes right before signing when the on-screen quote expires (needs the frozen amount for consistency), calculateCurrentRoute drives the view-entry effect and the retry-after-error path (needs the live amount). Kept both functions; dropped dev's own view-entry useEffect since main's unconflicted one (calling calculateCurrentRoute) already covers that.
    • In handleConfirmWithdrawal's "nothing prepared" branch, main's version only retried when a routeError was already set, else showed a dead-end "not prepared" error; dev's test (crypto-withdraw-confirm.test.tsx, unconflicted, pre-existing) requires an unconditional retry with no dead-end message at all, matching dev's own comment: "a persistent failure keeps surfacing through routeError," never a one-off error banner. Went with dev's unconditional-retry philosophy, keeping main's double-tap guard (isCalculating) — confirmed by running the full suite (see below), main's own new retry tests don't require the routeError-gated behavior, only dev's pre-existing test does.
  • crypto-withdraw-confirm.test.tsx — two disjoint new describe blocks (dev's balance-gate + frozen-spend tests, main's retry-after-cap-error tests). Concatenated, no overlap.

Verified

  • pnpm typecheck clean.
  • useCrossChainTransfer.test.ts, crypto-withdraw-confirm.test.tsx, friendly-error.utils.test.tsx all green (30 + 58 tests).
  • pnpm prettier --check on every touched file: no diff.

Worth a second look

Nothing outstanding — every conflict resolved to a state confirmed compatible by an existing (possibly newly-combined) test, unlike the api-ts counterpart which had a couple of items needing a product call.

abalinda and others added 19 commits September 2, 2026 23:40
…te the route (TASK-22154)

peanut-api-ts #1497 answers POST /rhino/sda-transfer with 429
{ error, code: XCHAIN_WITHDRAW_LIMIT_REACHED, retryAfterSec } once a user
is over the per-user cross-chain withdrawal cap. Today postRhino throws
"Failed to provision SDA transfer: 429 {json}" and the confirm view shows
that string; its Retry then calls onConfirm with no prepared transactions
and dead-ends on "transaction not prepared".

- postRhino throws an ApiError (message = backend text, plus code and
  retryAfterSec), and apiErrorFromResponse now carries retryAfterSec.
- friendlyError maps XCHAIN_WITHDRAW_LIMIT_REACHED to localized copy that
  states the wait in the coarsest unit (minutes / hours / days) and points
  at Arbitrum (no limit) and support; en, es-419, es-AR, pt-BR.
- useCrossChainTransfer surfaces errors through useFriendlyError instead
  of echoing err.message.
- Retry after a route error recomputes the route instead of failing on
  the transactions the failed route never built.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
…0903-014858

content: publish latest to production (src/content → peanut-content@d700af2)
…-stablecoin withdrawals

peanut-api-ts #1497 gates POST /rhino/bridge/quote with the same per-user
cross-chain cap as the SDA route when the quote names its charge. Send
context + contextId from the bridge path (ETH/WETH/… destinations), which
never touched /rhino/sda-transfer and so was uncapped.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
…nder-promises

Chip review (bc90092):
- handleConfirmWithdrawal branched on routeError but its dependency list
  omitted routeError and calculateCurrentRoute, so a 429 that arrived after
  the callback was memoized still fell through to "transaction not
  prepared". Both are dependencies now; regression test added to
  crypto-withdraw-confirm.test.tsx.
- The cap wait rounded the shown unit down (119 min → "1 hour", 47 h →
  "1 day"). It now rounds up, and a unit is used only once the wait
  reaches it.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
Chip on peanut-api-ts#1497 (60991481): rhino-bridge.ts postJson/getJson
still threw a plain Error, so a cap 429 on POST /rhino/bridge/quote would
have rendered the generic 'contact support' copy. Same apiErrorFromResponse
as rhino-sda.ts now.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
…e binding, drop stray legal-versions bump

Chip review (42de3c5):
- Retry recomputes only when no calculation is in flight, so a double tap
  cannot provision twice (each provision holds a cap slot) or race the
  route state.
- useCrossChainTransfer test: the bridge quote carries context + contextId
  for a withdraw and omits both for claim-xchain.
- src/constants/legal-versions.generated.ts was regenerated by the dev
  server's predev hook against a newer content submodule and committed by
  mistake; restored to main. The privacy bump ships with its backend half.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
Chip (f2e511f): the cap counts request payments too, and a payer blocked
on the semantic-request flow saw "withdrawals to other networks … withdraw
on Arbitrum instead" — advice they cannot act on, the request fixed the
destination. friendlyError takes a crossChainSurface hint; the cross-chain
hook passes 'payment' for pay-request, which selects xchainPaymentLimit*
copy (no Arbitrum advice) in en, es-419, es-AR, pt-BR.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
Chip (3844036): the isCalculating guard on the route-error Retry branch
had no test. A second tap while a recalculation is in flight must not call
calculate again or set 'not prepared'.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
…ASK-22185) (#2942)

* fix(watchdog): compare the mirror stamp by ancestry, not by string

The mirror stamps peanut-content with the mono commit it ran at. When
someone pulls without rebasing and pushes the merge, that stamp is the
merge commit, which only contains the content change. The watchdog read
"expected 7cf077f, published 37adb24" as a stale mirror and alerted
Discord every 15 minutes for 8 hours on 2026-09-03 while production was
serving the right content.

The mirror is current when the stamped commit is the expected commit or
a descendant of it. Ask the compare API (contents:read, which the mono
token already has) and accept "identical" or "ahead". Anything else,
including an unknown SHA, still alerts.

Claude-Session: https://claude.ai/code/session_01Rg9upFPdzh4ns3Dwyk6cao

* fix(watchdog): also require the stamp to be on main; API failure is a watchdog fault

Review findings on the ancestry check:

- "ahead" from expected...stamped does not prove the stamp is on mono
  main. The mirror has workflow_dispatch and checks out the triggering
  ref, so a manual run from a branch would publish content main never
  had and the watchdog would stay green. Add a second compare,
  stamped...main, and alert when it is not identical or ahead.
- A failed compare call (rate limit, 404, token scope) produced an empty
  status that read as a stale mirror and paged with no grace. Route it
  to the DEGRADED branch instead, and log the raw response.

Claude-Session: https://claude.ai/code/session_01Rg9upFPdzh4ns3Dwyk6cao
…0903-131959

content: publish latest to production (src/content → peanut-content@152377f)
Chip on peanut-api-ts#1497 (6e815a66): only the quote sent context/contextId;
with the cap on, POST /rhino/bridge/commit refuses commits without them, so a
non-stablecoin withdrawal would quote and then fail at commit. The commit
now carries the same charge (none for claim-xchain). Hook test asserts both.

Claude-Session: https://claude.ai/code/session_017idXbJRcFgbugvA8Xxr5YC
…0903-171034

content: publish latest to production (src/content → peanut-content@8edbfaf)
…t-error-22154

fix(withdraw): show the cross-chain withdrawal cap message and retry the route (TASK-22154)
…0904-120851

content: publish latest to production (src/content → peanut-content@40503da)
…-2026-09-07

# Conflicts:
#	src/app/(mobile-ui)/withdraw/crypto/__tests__/crypto-withdraw-confirm.test.tsx
#	src/app/(mobile-ui)/withdraw/crypto/page.tsx
#	src/features/payments/shared/hooks/__tests__/useCrossChainTransfer.test.ts
#	src/features/payments/shared/hooks/useCrossChainTransfer.ts
#	src/services/api-error.ts
#	src/services/rhino-bridge.ts
#	src/services/rhino-sda.ts
#	src/utils/friendly-error.utils.tsx
@vercel

vercel Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 7, 2026 4:44pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: f7282918-d2c3-4704-a396-beed34f0fc77

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7260.1 → 7263.33 (+3.23)
Findings: +3 net (+29 new, -26 resolved)

🆕 New findings (29)

  • critical complexity — src/app/(mobile-ui)/withdraw/crypto/page.tsx — CC 106, MI 49.4, SLOC 494
  • critical complexity — src/utils/friendly-error.utils.tsx — CC 105, MI 44.28, SLOC 332
  • critical method-complexity — src/utils/friendly-error.utils.tsx:214 — CC 62 SLOC 212
  • high hotspot — src/app/(mobile-ui)/withdraw/crypto/page.tsx — 66 commits, +750/-375 lines since 6 months ago
  • high complexity — src/features/payments/shared/hooks/useCrossChainTransfer.ts — CC 48, MI 46.09, SLOC 398
  • medium react-long-component — src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage is 786 lines — split it
  • medium high-mdd — src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage: MDD 166.8 (uses across many lines from declarations)
  • medium high-dlt — src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage: DLT 74 (calls 74 distinct functions — high context load)
  • medium high-mdd — src/features/payments/shared/hooks/useCrossChainTransfer.ts:179 — useCrossChainTransfer: MDD 63.5 (uses across many lines from declarations)
  • medium high-mdd — src/features/payments/shared/hooks/useCrossChainTransfer.ts:249 — : MDD 56.4 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/withdraw/crypto/page.tsx:409 — : MDD 40.1 (uses across many lines from declarations)
  • medium high-dlt — src/features/payments/shared/hooks/useCrossChainTransfer.ts:179 — useCrossChainTransfer: DLT 40 (calls 40 distinct functions — high context load)
  • medium high-mdd — src/features/payments/shared/hooks/useCrossChainTransfer.ts:463 — runBridgePath: MDD 28.3 (uses across many lines from declarations)
  • medium method-complexity — src/app/(mobile-ui)/withdraw/crypto/page.tsx:409 — CC 27 SLOC 150
  • medium structural-dup — app/(mobile-ui)/withdraw/crypto/page.tsx:197 — 23 duplicate lines / 107 tokens with app/(mobile-ui)/withdraw/crypto/page.tsx:226
  • medium complexity — src/services/api-error.ts — CC 23, MI 56.34, SLOC 62
  • medium method-complexity — src/app/(mobile-ui)/withdraw/crypto/page.tsx:258 — CC 18 SLOC 95
  • medium method-complexity — src/features/payments/shared/hooks/useCrossChainTransfer.ts:249 — CC 16 SLOC 146
  • medium complexity — src/hooks/useFriendlyError.ts — CC 9, MI 64.74, SLOC 25
  • low high-dlt — src/features/payments/shared/hooks/useCrossChainTransfer.ts:249 — : DLT 28 (calls 28 distinct functions — high context load)

…and 9 more.

✅ Resolved (26)

  • src/app/(mobile-ui)/withdraw/crypto/page.tsx — CC 101, MI 49.58, SLOC 472
  • src/utils/friendly-error.utils.tsx — CC 99, MI 45.39, SLOC 310
  • src/utils/friendly-error.utils.tsx:201 — CC 56 SLOC 190
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx — 62 commits, +723/-369 lines since 6 months ago
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts — CC 46, MI 46.4, SLOC 389
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage is 750 lines — split it
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage: MDD 157.0 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:49 — WithdrawCryptoPage: DLT 73 (calls 73 distinct functions — high context load)
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:178 — useCrossChainTransfer: MDD 56.5 (uses across many lines from declarations)
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:247 — : MDD 53.6 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:377 — : MDD 40.1 (uses across many lines from declarations)
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:178 — useCrossChainTransfer: DLT 38 (calls 38 distinct functions — high context load)
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:377 — CC 26 SLOC 149
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:455 — runBridgePath: MDD 24.8 (uses across many lines from declarations)
  • src/services/api-error.ts — CC 20, MI 58.08, SLOC 55
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:226 — CC 18 SLOC 95
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:247 — CC 16 SLOC 143
  • src/services/rhino-sda.ts — CC 13, MI 62.41, SLOC 39
  • src/features/payments/shared/hooks/useCrossChainTransfer.ts:247 — : DLT 27 (calls 27 distinct functions — high context load)
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx:377 — : DLT 25 (calls 25 distinct functions — high context load)

…and 6 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/hooks/useFriendlyError.ts 5.5 6.5 +1.0
src/services/api-error.ts 5.2 5.8 +0.6
src/utils/friendly-error.utils.tsx 11.1 11.7 +0.6
src/features/payments/shared/hooks/useCrossChainTransfer.ts 13.3 13.8 +0.6
src/app/(mobile-ui)/withdraw/crypto/page.tsx 18.8 19.3 +0.5
src/services/rhino-sda.ts 6.4 5.6 -0.8

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 6114 ran, 0 failed, 0 skipped, 1.9m

📊 Coverage (unit)

metric %
statements 74.7%
branches 60.5%
functions 68.8%
lines 75.6%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Scan that recovers on the retry lands on the payment screen, not an error
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Going offline blames the connection, and reconnecting clears it for the recovered scan
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🖼 Visual diff — 10 screens moved

10 of 68 shots changed · 58 identical · baseline 5c52afa → head 9df6488

worst % screen widths
3.19% profile 320
1.65% avatar-picker 430
0.07% home 320
0.07% unverified 320
0.03% add-money-crypto 430
0.03% add-money 430
0.03% badges 430
0.02% reconsent 430
0.01% empty-accounts 320
0.01% settings-language 320

job summary · before/after/diff images — artifact

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Chip review — no blocking findings — this is not an approval

One major merge-resolution regression remains: initial and retry cross-chain quotes use the rounded UI amount instead of the amount frozen into the charge, so max withdrawals can leave funds behind. Exact-head required CI is green.

Findings

  • MAJOR · src/app/(mobile-ui)/withdraw/crypto/page.tsx:233 · Quote the amount frozen into the withdrawal charge
    handleSetupReview freezes preparedAmount from liveResolvedAmount and writes that value into the charge, but this entry/retry callback sends amountToWithdraw to the pay-mode quote. For a max withdrawal with 10.126123 USDC displayed as 10.12, the charge is for 10.126123 while the SDA or bridge transaction is sized to 10.12, leaving 0.006123 behind and persisting quote economics that disagree with the charge. Reuse quoteRoute here, or pass effectiveAmount and depend on it, and cover a cross-chain max withdrawal where the frozen amount differs from the displayed cents.

  • MAJOR · src/app/(mobile-ui)/withdraw/crypto/page.tsx:233 · [claude-opus] Back-merge re-quotes the withdraw route off the unfrozen amount; no test covers the amount calculateRoute is called with
    dev (parent ecd7c0b / base 5c52afa) quoted the route through quoteRoute, which passes effectiveAmount (= preparedAmount, the amount frozen when the charge was built — TASK-21899). main (parent 623291d) still had the older calculateCurrentRoute, which passes amountToWithdraw, the displayed-cents amount. The merge kept both: quoteRoute survives but is now reachable only from the quote-expiry branch (page.tsx:437), while the confirm-view entry effect (page.tsx:251-253) and the new cap-429 Retry path (page.tsx:423) both call calculateCurrentRoute, i.e. tokenAmount: amountToWithdraw (page.tsx:233).

Failure case: a 'use full balance' withdrawal where spendable balance is 10.126123 USDC. resolveWithdrawAmount (src/utils/withdraw.utils.ts:391) returns '10.126123' and that is frozen into preparedAmount and into the charge the API validator settles against; amountToWithdraw stays '10.12'. Entering CONFIRM (and every Retry after a cap 429) now quotes the route with '10.12'. On the bridge path the quote is sized in mode: 'pay' from source.tokenAmount, and for a cross-chain withdrawal the kernel spends payAmount derived from that quote (page.tsx:702) — so the spend and delivery are sized off a number that is not the charge's, the sub-cent remainder is stranded again (exactly the bug the frozen-amount work fixed), and the first quote and the expiry re-quote disagree with each other.

The reason this merged green: the whole 'the spend is frozen with the charge' suite only asserts sendMoney's argument (crypto-withdraw-confirm.test.tsx:709-778), which covers the same-chain path; the two new Retry tests assert only that calculate was called once more, never with what amount. There is no test that a max cross-chain withdrawal quotes the route with the frozen amount — that is the untested money case, and per the repo's hard test rule it needs one.

Fix: point the confirm-entry effect and the Retry branch at the frozen amount (either call quoteRoute() in both places, as dev did, or change calculateCurrentRoute's tokenAmount to effectiveAmount and delete the duplicate), and add a case asserting calculate is invoked with source.tokenAmount === preparedAmount for a max withdrawal on both confirm-entry and Retry.

Checked clean

  • Confirmed the detached worktree head, trusted author, PR number, base ref, and supplied base SHA; the merge base is ecd7c0b.
  • Reviewed the complete merge-base diff and all eight conflict resolutions across withdrawal routing, Rhino services, error classification, tests, translations, and the content watchdog.
  • Checked the sibling backend contract: SDA provisioning derives the authoritative withdrawal source amount from charge metadata and persists the client quote economics.
  • Exact-head ci-success, unit, typecheck, eslint, format, native-export, and ds-lint checks succeeded; advisory ds-shots was still in progress.
  • Security pass found no exposed secret or reachable command injection in the watchdog; compare inputs are constrained to hexadecimal revisions.

Security review: did not run — openrouter-http-402. This review is one reviewer short.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 9011d72e7447 · Context: repo, sibling · Took 19m

Comment thread src/app/(mobile-ui)/withdraw/crypto/page.tsx Outdated

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Chip review — no blocking findings — this is not an approval

The frozen withdrawal amount is now used and tested on both confirm entry and Retry, fixing P1/P2. Two unrelated dev changes are still reverted by the back-merge.

Findings

  • MINOR · package.json:117 · Preserve the PostHog upgrade from dev
    The exact base already contains #3016 / 987542a, which intentionally upgraded posthog-js from 1.360.0 to 1.418.11 and posthog-node from 5.29.2 to 5.50.0. This merge restores both older versions (and rewrites the lockfile accordingly), so merging the back-merge would silently undo that completed dependency update. Resolve the merge by retaining dev's package.json and matching pnpm-lock.yaml entries.

  • MINOR · scripts/ds-lint-baseline.json:7 · Do not raise the DS baseline to absorb reverted cleanup
    The base already contains #3015's cleanup, with nonDsClassesInViews reduced to 6. This head deletes the named 60vh utilities, restores arbitrary 60vh/min-width/text-size classes, and raises the baseline to 15, so ds-lint stays green by accepting nine reintroduced violations. Preserve the base-side utility/class changes and keep the baseline at 6 instead of masking the regression.

Inline anchors unavailable for 2 finding(s); the findings remain in this summary.

Checked clean

  • P1 fixed: both withdrawal route calculators use effectiveAmount, which resolves to the amount frozen with the charge.
  • P2 fixed: confirm-entry and Retry tests assert calculateRoute receives source.tokenAmount 10.126123 when the displayed live amount is 10.12.
  • Reviewed withdrawal route calculation, Rhino SDA/bridge error propagation and charge binding, friendly-error localization, conflict-resolution tests, dependency and design-system merge results, and the secret-bearing watchdog workflow.
  • Exact-head required CI completed successfully; ds-shots and Deploy Preview were still in progress when checked.
  • Local focused Jest execution could not start because the detached worktree has no installed node_modules; exact-head unit CI passed.
  • git diff --check passed.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 9df64880465e · Context: repo · Took 10m

@innolope-dev
innolope-dev merged commit 739f30e into dev Sep 7, 2026
23 checks passed

This branch was successfully deployed

1 active deployment
Preview — 9df64880 Deployed Sep 7, 2026 by vercel[bot]
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