Skip to content

Add a new currency (fiat or crypto)

Goal: add support for a new fiat or crypto currency (analogous to the existing DBC, USDT, BTC, etc.). Audience: customer engineering team adding a new currency for deposits, gameplay, withdrawals, leaderboards. Time: 1–2 weeks. Friction: medium-high — touches Prisma enums, every payment provider mapping, the FE currency picker, jurisdictional whitelist.

What you'll change

Layer Path Action
Currency enum libs/_prisma/src/schema/api.prisma:70 (enum CurrencySymbol) Add the new symbol. Run migration.
Crypto config (if crypto) libs/_prisma/src/schema/api.prisma:184 (model CryptoCurrencyConfig) Insert a new row via seed or admin API.
Exchange-rate provider apps/api/src/exchange-rates/provider/coingecko/ Add the CoinGecko id mapping.
Accounting helpers libs/accounting/src/currency/ Verify rounding / precision rules.
Payment provider mappings per-provider apps/api/src/payment/provider/integration/<provider>/ Confirm the new currency is supported by each enabled provider; map vendor codes.
Game currency support libs/games/src/house-games.config.ts (per-game minBet / maxBet) Set bet bounds for the new currency.
FE currency picker ebit-fe/src/components/... (search CurrencySymbol, currency selector) Display + selection.
FE i18n ebit-fe/messages/{locale}.json Currency-name translations.
Compliance / jurisdictional matrix {{TBD}} (docs/security-register?) Whitelist where the currency can be deposited / used.

Canonical references

  • Enumlibs/_prisma/src/schema/api.prisma:70 defines CurrencySymbol. Verified migrations: add_evo_currency (2025-01-15), add_dbc_currency (2025-03-12), drop_currency (2024-12-09). Pattern: add the enum value, write a migration that inserts a config row.
  • CryptoCurrencyConfig model at libs/_prisma/src/schema/api.prisma:184 keeps per-currency metadata (e.g. precision, network mapping). Fiat currencies typically don't need a row here; crypto does.
  • Exchange-rate provider at apps/api/src/exchange-rates/provider/coingecko/ calls CoinGecko by currency id (e.g. bitcoin, tether, usd-coin).
  • Existing currencies in the codebase: search CurrencySymbol\. in apps/api/src/. The DBC / EVO additions are recent and provide a fresh template.

Steps

1. Pre-flight [non-engineering]

  • [ ] Confirm with the customer: which jurisdictions accept this currency for play?
  • [ ] Confirm: which payment providers (CCPayment, NowPayments, SkinDeck, ...) support deposits / withdrawals in it? Document the matrix.
  • [ ] Confirm: source of exchange rate (CoinGecko id, or an alternative oracle). For fiat, CoinGecko also supports most fiat pairs.
  • [ ] Confirm: precision / smallest unit (e.g. BTC has 8 dp; some tokens have 18 dp). Mismatches break accounting.
  • [ ] Confirm: regulatory classification (security vs commodity vs e-money) for each market — has compliance impact.
  • [ ] Get sign-off: legal / compliance, finance.

2. Add the Prisma enum value [needs migration window]

Edit libs/_prisma/src/schema/api.prisma:70:

enum CurrencySymbol {
  // existing values…
  USDT
  BTC
  ETH
  DBC
  EVO
  // NEW:
  YOURCURRENCY
  @@map("currency_symbol")
  @@schema("public")
}

Generate the migration:

cd ebit-api
npm run db:migrate:dev -- --name add_yourcurrency_currency

Adding a Postgres enum value is non-blocking. The migration completes in seconds even on a busy database.

3. Insert the CryptoCurrencyConfig row (crypto only)

If the new currency is crypto, the seed at libs/_prisma/src/seed/ needs a CryptoCurrencyConfig entry. Verified pattern via existing migrations (add_dbc_currency, add_evo_currency).

For fiat, skip this step.

4. Add the CoinGecko mapping

Edit apps/api/src/exchange-rates/provider/coingecko/const.ts (or the equivalent — verify by grepping CurrencySymbol inside apps/api/src/exchange-rates/):

export const COINGECKO_ID_MAP: Record<CurrencySymbol, string> = {
  // existing…
  [CurrencySymbol.YOURCURRENCY]: 'your-coingecko-id',
};

Validate the id by curl:

curl -s "https://api.coingecko.com/api/v3/simple/price?ids=your-coingecko-id&vs_currencies=usd"

5. Update accounting helpers

Open libs/accounting/src/currency/. Verify:

  • Currency precision is set correctly (the smallest displayable / settle-able unit).
  • Rounding mode is consistent with existing currencies (typically half-up).

If the new currency has unusual precision (e.g. a token with 18 dp), add a precision override.

6. Confirm payment provider support

For each enabled payment provider (CCPayment, NowPayments, SkinDeck, customer's new provider), confirm:

  • The provider supports the new currency (check provider docs / sandbox).
  • The provider's currency code maps to our CurrencySymbol (e.g. provider's "USDT_TRC20" → our USDT).

Edit each provider's currency-mapping file. Locations:

  • apps/api/src/payment/provider/integration/ccpayment/utils.ts (or const.ts).
  • apps/api/src/payment/provider/integration/nowpayments/utils.ts.
  • apps/api/src/payment/provider/integration/skindeck/....

If a provider does not support the new currency, the deposit screen must hide that provider for the new currency. Verify the FE currency-provider compatibility logic.

7. Update house-game bet bounds

For each game in libs/games/src/house-games.config.ts, ensure the new currency has min/max bet entries:

minBet: {
  [CurrencySymbol.USDT]: '0.10',
  [CurrencySymbol.YOURCURRENCY]: '0.0001',   // adjust per token economics
},
maxBet: {
  [CurrencySymbol.YOURCURRENCY]: '100.0',
},

Without these the bet validator BetAmountValidationService rejects bets in the new currency.

8. Frontend currency picker

Search ebit-fe/src/ for CurrencySymbol or the currency-picker component. Typical hits:

  • A <CurrencyPicker> component reading the list of supported currencies from a backend endpoint.
  • A balance display using useFormatter().number(amount, { style: 'currency', currency }).

If the backend's "supported currencies" response now includes the new symbol, the FE picker auto-populates. Verify:

curl -s http://localhost:4000/currency | jq

(Path verified in docs/api-reference/api.md "Currency" tag.)

9. Translations for the new currency name

Edit ebit-fe/messages/{en,de,...}.json:

{
  "currency": {
    "USDT": "Tether",
    "BTC": "Bitcoin",
    "YOURCURRENCY": "Your Currency Name"
  }
}

10. Jurisdictional whitelist

{{TBD: confirm with security review}} — jurisdictional gating is currently spread across:

  • Geo-restriction config (per apps/api/src/system/... — search Country, GeoRestriction).
  • Per-provider whitelists.
  • Feature-flag gating (enable-feature-flag.md).

For a new currency, the safest path is a feature flag:

  1. Create currency-yourcurrency-enabled in GitLab Unleash.
  2. Default off.
  3. Gate the currency in the supported-list endpoint behind the flag (also gate the currency-picker entry on the FE).
  4. Enable for the customer's environment + cohort.

11. Tests

npm test -- apps/api/src/exchange-rates/ libs/accounting/src/
  • Exchange rate fetch returns a number for the new currency.
  • Accounting helpers round + format correctly.
  • Bet validator accepts a bet inside [minBet, maxBet] for the new currency, rejects outside.

12. Update the API surface SOT

./docs/api/sync-postman.sh

The OpenAPI changes are minor (new enum value in a few schemas) but the diff makes the change auditable. Append a docs/api/changelog.md entry: "Added currency YOURCURRENCY to CurrencySymbol enum; min/max bet bounds set for all house games."

Verification

  1. Currency listing: GET /currency returns the new symbol.
  2. Exchange rate: GET /exchange-rates returns a non-null rate for the new currency.
  3. Deposit: in the FE wallet, the new currency appears as a deposit option. Provider redirect works.
  4. Bet: a bet in the new currency validates (within bounds) and settles correctly. Balance updates.
  5. Withdrawal: withdrawal screen shows the new currency. Provider supports the withdrawal.
  6. Leaderboard / aggregates: existing leaderboards display amounts in the new currency without breaking (verify per-currency leaderboards if those exist).

Notes / known gaps

  • Migration of historical balances: users who held a different currency don't get auto-converted. Plan messaging.
  • Tax reporting: if the customer ships tax reports, confirm the new currency is recognised by the tax module (out of scope for this recipe).
  • Adverse selection: adding a currency with high volatility (memecoin, etc.) can shift edge across bet types. Coordinate with the casino-math team.
  • The naming convention is enum value = display symbol (BTC, USDT, DBC). Don't introduce lowercase or special-case names.