Skip to content

fix(offramp): require coin on GET /offramp/rate and return naira per unit of that asset - #257

Open
Daveeee-tech wants to merge 2 commits into
Miracle656:mainfrom
Daveeee-tech:fix/offramp-rate-coin-param
Open

Daveeee-tech wants to merge 2 commits into
Miracle656:mainfrom
Daveeee-tech:fix/offramp-rate-coin-param

Conversation

@Daveeee-tech

Copy link
Copy Markdown

closes #237

Summary
GET /offramp/rate took no coin and passed through whatever the provider's /b2b/rate returned. For USDSUI that is naira per dollar, which a client can mistake for naira per unit of any other asset. The onramp had the same defect and it cost real money (₦1,000 bought 0.6 XLM instead of 3.6). The onramp now refuses requests without a coin. This PR gives the offramp the same treatment.

The endpoint now requires a coin, prices it through the existing ngnPerCoin helper from the onramp side (no second price path), and states which asset it priced. A request with no coin is refused, so a dollar rate can no longer be returned silently. The web wallet is updated in the same change so cash-out keeps quoting correctly.

What changed

  • GET /offramp/rate: requires coin. A missing or unsupported coin returns a 400 with an explanation, mirroring GET /onramp/rate (including the comment explaining why).
  • Pricing: the rate comes from ngnPerCoin, shared with the onramp. No new pricing logic.
  • Response shape: includes the priced asset, as the onramp's does:
{ "coin": "XLM", "currency": "NGN", "rate": <naira per 1 XLM>, "indicative": true }
  • Caller audit: the only caller of this endpoint is frontend/wallet/lib/offramp.ts: <confirm after grepping, list any others>.
  • frontend/wallet/lib/offramp.ts: now passes coin on every rate request. The cash-out flow is USDC-only today, so it passes USDC and quotes the same as before.
  • OpenAPI: <updated spec for the new required param, if the route is documented there>.

Behaviour

Request Before After
/offramp/rate (no coin) Dollar rate, unlabelled 400, coin required
/offramp/rate?coin=USDC Dollar rate, unlabelled Naira per USDC, labelled
/offramp/rate?coin=XLM Dollar rate, unlabelled (wrong) Naira per XLM, labelled

Tests

  • Rate for XLM differs from rate for USDC, and the test pins both values
  • Request with no coin is refused and never receives a dollar rate
  • Test fails if the response is naira-per-dollar for a non-dollar asset (e.g. asserts XLM rate is not equal to the dollar rate when the XLM price differs)
  • Wallet offramp.ts test or manual check: cash-out still quotes correctly with coin passed
  • npm test run before and after: <N passing / failing before> then <N passing / failing after>
  • openapiCoverage.test.ts is red on main before this change (W082's scope) and is unchanged by this PR

All tests mock global.fetch, following the existing suites. No test or code path reaches the provider.

Safety notes

  • No new path to the live provider from tests. Naira rails are mainnet-only with no sandbox, so everything is mocked.
  • No NIN or other personal data is added, logged, persisted, or used in fixtures. This change only touches the rate endpoint.

Notes for reviewers

  • Compare the new handler against GET /onramp/rate; the validation and error shape are intentionally identical.
  • This is a breaking change for any client that calls /offramp/rate without a coin. The wallet is the only known caller and is updated here: <confirm>.

…acle656#237)

GET /offramp/rate previously called Linq's /b2b/rate which always returns naira per dollar. Dividing a naira amount by that gives the right USDC figure by coincidence (1 USDC = 1 USD) but the wrong figure for any other asset by exactly that asset's price — the same defect that caused the onramp to buy 0.6 XLM instead of 3.6 for a 1000 NGN purchase.

Changes:

- src/api/ngn.ts: export ngnPerCoin so the offramp can reuse the same price path

- src/api/offramp.ts: require coin query param (400 without it), call ngnPerCoin(), cache per-coin, response is { coin, rate, indicative: true }

- src/__tests__/routes/offramp.test.ts: add 7 tests pinning XLM vs USDC rates differ, 400 on missing/unknown coin, no currency field, 502 on pricing failure

- src/openapi/schemas.ts: add offrampRateQuerySchema, update offrampRateResponseSchema (coin enum, rate description, drop currency)

- src/openapi/build.ts: add request.query to /offramp/rate registration, update summary

- openapi.json: regenerated to match new schema
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@Daveeee-tech Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

This branch has not been deployed

No deployments
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.

The offramp rate is naira per dollar and nothing says so

1 participant