fix: block crypto withdrawals below Rhino route minimums#2458
Conversation
Rhino rejects bridge deposits under the route minimum (UNDER_MIN) and parks the funds at the SDA with no auto-refund — a $2.50 withdrawal to Ethereum (min $5) sat stranded since 07-15 with the intent COMPLETED. Floor every crypto withdrawal at $0.50 on the amount step (red, blocking), warn $0.50–$5 amounts that Ethereum needs $5, and hard-block chain-aware minimums (ETH $5 / Tron $10) at review time before any charge exists.
The withdraw chain picker's NON_EVM_WITHDRAW_CHAINS entry carries chainId 'tron', so the '728126428' map key was unreachable and Tron silently degraded to the $0.50 default. Caught by adversarial replay review before push.
…t tests Adversarial review findings: (1) the review-time minimum error said 'pick a different network' but nothing cleared it on chain change, so Review stayed dead after following the instruction; (2) the Tron test asserted a hand-typed id no picker record carries — now asserts the REAL registry records; (3) the Ethereum notice read the app-wide token price on a USD-pinned input; (4) stale 'no minimum' comment contradicted the new floor; (5) notice copy works under the Send header too.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughCrypto withdrawals now use USD-pinned amount validation, chain-specific minimums, minimum guidance, pre-setup enforcement, stale-error clearing on chain changes, and expanded regression coverage. ChangesCrypto withdrawal minimums
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant WithdrawPage
participant WithdrawCryptoPage
participant MinimumLookup
participant WithdrawalSetup
WithdrawPage->>WithdrawPage: Parse entered amount as USD
WithdrawPage-->>WithdrawCryptoPage: Continue with USD amount
WithdrawCryptoPage->>MinimumLookup: Get minimum for destination chain
MinimumLookup-->>WithdrawCryptoPage: Return chain minimum
WithdrawCryptoPage->>WithdrawalSetup: Create request and charge after validation
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code-analysis diffPainscore total: 6223.44 → 6226.18 (+2.74) 🆕 New findings (25)
…and 5 more. ✅ Resolved (23)
…and 3 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
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 `@src/app/`(mobile-ui)/withdraw/page.tsx:
- Around line 452-460: Update the description in the showEthereumMinNotice
InfoCard to avoid claiming the amount works on every other network. Replace that
statement with wording indicating that other networks may have different
withdrawal minimums, while preserving the Ethereum minimum amount interpolation.
- Around line 134-137: Update the withdrawal amount validation around
isCryptoWithdraw so the minimum check compares the raw USD value from
AmountInput directly against MIN_CRYPTO_WITHDRAW_USD. Apply this USD comparison
before any multiplication by selectedTokenData.price, ensuring Continue remains
disabled for values below $0.50 regardless of stale token pricing.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 951e4334-9792-446d-8bfa-1846d3c1f6e4
📒 Files selected for processing (6)
src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsxsrc/app/(mobile-ui)/withdraw/crypto/page.tsxsrc/app/(mobile-ui)/withdraw/page.tsxsrc/components/Withdraw/views/Initial.withdraw.view.tsxsrc/utils/cross-chain-fee.utils.test.tssrc/utils/cross-chain-fee.utils.ts
CodeRabbit major: the amount page's AmountInput is hard-pinned to USD, but the min/balance gates multiplied by the app-wide selectedTokenData price — a stale non-USD price could let $0.01 past the $0.50 floor (or false-block valid amounts). The typed value IS the USD value; compare it directly. Also: the Ethereum notice no longer overclaims that every other network accepts the amount (Tron is $10).
Product call (Aleks): same-chain Arbitrum USDC withdrawals are direct transfers — no Rhino, no minimum — and must keep parity with send-via-link and P2P sends. Drop the blanket $0.50 amount-step floor; warn there instead (destination unknown), and enforce the per-network Rhino minimums at review time only for bridged/swapped routes.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…lection Product call (Aleks): no yellow warning on the amount page; the per-network Rhino minimum blocks at review, where the destination is actually chosen.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
Rhino rejects bridge-SDA deposits below the route minimum (
UNDER_MINwebhook) and parks the funds — no auto-refund (2026-07-15 incident: $2.50 → Ethereum against a $5 min, stranded at the SDA while the intent completed off the source leg; a second case from 05-21 sat unnoticed for two months). Prod's only net is a confirm-time CTA check that fires after the request/charge already exist.This PR adds the missing early guard (FE), scoped to where Rhino is actually involved — same-chain Arbitrum USDC withdrawals are direct transfers with no minimum (parity with send-via-link and P2P sends), and the amount step is untouched (no floor, no warning):
handleSetupReview— at the network/coin selection step, before any request/charge is created, for every destination except same-chain Arbitrum USDC: Ethereum $5, Tron $10 (keyed by the picker's real'tron'slug), $0.50 default. Map incross-chain-fee.utils.ts, verified against Rhino'sgetSupportedTokensAPI 2026-07-21.Design notes / accepted trade-offs
minDisplayformatting appears twice (page + review guard) — left inline, 1 expression × 2 sites.Out of scope (follow-ups)
peanut-api-ts/rhino/sda-transfer(payAmountvsminDepositLimitUsd).DEPOSIT_ADDRESS_BRIDGE_REJECTED→ intent flag + alert (currently updates an in-memory store only).pay-request/claim-xchaincross-chain flows fund SDAs unguarded (amounts fixed by requester/link, not the payer).Risks
main→ back-merge debt main→dev (dev's crypto-min line will conflict trivially — keep this branch's version).QA
cross-chain-fee.utils.test.ts(mins map incl. real-registry anti-drift assertions),withdraw-states.test.tsx(no amount-step floor at $0.4, bank minimums untouched).🤖 Generated with Claude Code