fix(offramp): derive destination rail from account type, not country (PEANUT-API-5P/5M/5N)#2173
fix(offramp): derive destination rail from account type, not country (PEANUT-API-5P/5M/5N)#2173Hugo0 wants to merge 1 commit into
Conversation
2026-06-02 21:24 UTC, PEANUT-API-5P/5M/5N. A user with a UK GBP bank
account tried to offramp USDC → EUR via SEPA. Bridge rejected with
"country is not supported for SEPA" — SEPA only credits EUR-denominated
Eurozone accounts, not GBP/UK.
Root cause was in BankFlowManager.handleCreateOfframpAndClaim:
const destination = getOfframpCurrencyConfig(
account.country ?? selectedCountry!.id
)
When `account.country` is missing, the picker falls back to
`selectedCountry`. If `selectedCountry` doesn't match the saved-account's
actual type (e.g. user picks a random country, or country resolution is
buggy), the helper's "everything unknown → EUR/SEPA" default fires —
even for a clearly GBP/UK account.
The account's own `type` already carries the right answer for every
Bridge destination we support (us/gb/clabe/iban). Adding a tiny helper
`getOfframpConfigFromAccount` that derives currency+rail directly from
account.type closes this class of bug.
- `gb` → gbp/faster_payments
- `us` → usd/ach
- `clabe` → mxn/spei
- `iban` → eur/sepa
- `manteca` → throws (must use the Manteca offramp path, not Bridge)
- type missing → falls back to country-based picking (prior behavior)
The helper also accepts BE Prisma-shape suffixes (`BANK_IBAN`,
`BANK_ACCOUNT_GB`, etc.) so it doesn't matter which side of the API the
account row came from.
7 new unit tests in bridge.utils.test.ts cover:
- The GB regression (was EUR/SEPA, now GBP/faster_payments)
- All 4 supported account types map to the right rail
- BE Prisma-suffix shapes (BANK_IBAN, BANK_ACCOUNT_GB)
- Manteca throws to surface a wrong-path call early
- Missing-type fallback to country picking still works
All 71 bridge.utils tests passing.
Companion PR: peanut-api-ts #964 — surfaces Bridge's actual error
message in Sentry + stops Discord-paging on user-input 4xx errors.
Even after this FE fix, other user-input mistakes (e.g. a Bridge
customer status change mid-flow) shouldn't page on-call.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughThis PR refactors how the UI derives off-ramp destination configuration. Instead of selecting the off-ramp currency and payment rail based on the user's selected country, the system now derives them from the selected bank account's type field. A new utility function implements type-to-configuration mapping with fallback to country-based selection, and BankFlowManager is updated to use it. ChangesOff-ramp routing by account type
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 5878.54 → 5879.42 (+0.88) 🆕 New findings (14)
✅ Resolved (14)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
Why
2026-06-02 21:24 UTC. PEANUT-API-5P/5M/5N + Discord on-call page.
A user with a UK GBP bank account tried to offramp USDC → EUR via SEPA. Bridge correctly rejected:
```
country is not supported for SEPA
```
SEPA only credits EUR-denominated Eurozone accounts. You can't SEPA-credit a GBP account.
Root cause (FE)
`BankFlowManager.handleCreateOfframpAndClaim` picked the destination from country, not from the bank account's actual type:
```ts
const destination = getOfframpCurrencyConfig(
account.country ?? selectedCountry!.id
)
```
When `account.country` is missing or doesn't match, the picker falls through to `selectedCountry`. `getOfframpCurrencyConfig`'s "everything unknown → EUR/SEPA" default then fires — even for a clearly GBP/UK account. Result: a GB account was paired with EUR/SEPA → Bridge 400.
The bank account's own `type` already carries the right answer for every Bridge destination we support (`us` / `gb` / `clabe` / `iban`). Deriving directly from type closes this class of bug at the source.
What this PR does
New helper `getOfframpConfigFromAccount(account)` in `src/utils/bridge.utils.ts`:
Also accepts BE Prisma-shape suffixes (`BANK_IBAN`, `BANK_ACCOUNT_GB`, …) so it works regardless of which side of the API the account row came from.
Use site: `BankFlowManager.view.tsx:248` — the only call site for the offramp destination picker on this code path.
Tests
`src/utils/tests/bridge.utils.test.ts` — 7 new cases under `getOfframpConfigFromAccount (PEANUT-API-5P/5M/5N regression)`:
71 tests passing in the file (was 64 before).
What this PR does NOT do (deliberate)
Test plan