How the Remitwise protocol decides which currencies to whitelist for on-chain settlement, billing, and remittance operations.
Contributors and integrators who need to understand which currencies are accepted, how whitelisting works, and how to add a new currency.
The protocol uses two defence-in-depth layers:
| Level | Scope | Enforcement | Location |
|---|---|---|---|
| Supported token registry | Protocol-wide set of tokens with known decimals | Compile-time enum + match arms | remitwise-common/src/tokens.rs |
| Stable currency allowlist | Contract-level allowlist of currency codes accepted in bill/invoice flows | Runtime require_stable_currency() check |
remitwise-common/src/lib.rs |
The SupportedToken enum in remitwise-common/src/tokens.rs defines the
canonical set of tokens the protocol natively supports:
| Variant | Discriminant | Currency code | Decimals | Base units per unit |
|---|---|---|---|---|
XLM |
1 | "XLM" |
7 | 10_000_000 |
USDC |
2 | "USDC" |
6 | 1_000_000 |
EURC |
3 | "EURC" |
7 | 10_000_000 |
Each variant implements:
decimals()— returns the number of decimal placesbase_units_per_unit()— returns the scaling factor for 1 whole unitcurrency_code()— returns the three-to-four-letter ISO-style code asSymbolfrom_currency_code()— reverse lookup from aSymbolto aSupportedToken
Adding a new token requires:
- Adding a new variant to the
SupportedTokenenum - Implementing all four methods for the new variant
- Updating every match arm that exhaustively covers
SupportedToken
The STABLE_CURRENCIES constant in remitwise-common/src/lib.rs is the
runtime allowlist:
pub const STABLE_CURRENCIES: &[&str] = &[
"USDC", "USDT", "USDP", "BUSD", "GUSD", "TUSD", "USDD",
"EURC", "EURS", "DAI", "XLM",
];Validation flow (require_stable_currency):
- Input is trimmed of leading/trailing whitespace
- Input is uppercased
- Length is checked against
MAX_CURRENCY_LEN(10 characters) - Each character is verified as ASCII alphabetic
- The result is matched against
STABLE_CURRENCIES(case-insensitive) - If no match, the call panics with
StableCurrencyError::UnsupportedCurrency
Design invariants:
- Default currency:
"XLM"(lowest friction for new users) - Fail closed: unknown codes are rejected, not silently defaulted
- Case-insensitive:
"usdc","Usdc", and"USDC"all resolve to USDC - Defence in depth: the allowlist is checked independently of the
SupportedTokenenum — a token must pass both checks to be usable
The validate_and_normalize_currency() function in
bill_payments/src/lib.rs applies the full pipeline:
raw input
→ trim whitespace
→ uppercase
→ validate characters (ASCII alphabetic only)
→ validate length (≤ MAX_CURRENCY_LEN)
→ require stable currency (allowlist match)
→ return normalized Symbol
Entry points that accept currency input:
create_billcreate_bill_schedule
Both call validate_and_normalize_currency at the boundary before any
storage writes, ensuring malformed or unsupported currency codes are
rejected early.
bill_payments maintains an on-chain currency index for efficient
per-currency queries (get_bills_by_owner_currency):
get_currency_index()— returns the current cursor for a currencyindex_add_currency()— appends a bill ID to a currency's indexindex_remove_currency()— removes a bill ID from a currency's indexget_bills_by_owner_currency()— paginated read of bills filtered by currency
The index is kept in sync automatically by create_bill, which calls
validate_and_normalize_currency followed by index_add_currency.
-
Add to
SupportedTokenenum (remitwise-common/src/tokens.rs):- Add a new variant with the next available discriminant
- Implement
decimals(),base_units_per_unit(),currency_code(), andfrom_currency_code() - Update all exhaustive match arms across the workspace
-
Add to
STABLE_CURRENCIES(remitwise-common/src/lib.rs):- Append the currency code (uppercase, 3-10 characters)
-
Update documentation:
- This file (
CURRENCY_SUPPORT_POLICY.md) docs/STABLE_CURRENCIES.mddocs/token-registry.md
- This file (
-
Write tests:
require_stable_currencyaccepts the new code (case-insensitive)SupportedToken::from_currency_coderesolves the new code- Existing entry points accept the new currency
- Paginated reads filter correctly by the new currency
| Document | Content |
|---|---|
docs/STABLE_CURRENCIES.md |
Stable currency allowlist rationale and full list |
docs/SETTLEMENT_CURRENCY_POLICY.md |
Per-invoice settlement currency whitelist (planned) |
docs/token-registry.md |
Centralized token registry reference |
docs/DECIMAL_CATALOGUE.md |
Token decimal catalogue |
docs/CANONICALISATION.md |
Currency trim/uppercase canonicalisation |
docs/whitelisting-test-coverage.md |
Test coverage for whitelisting |
docs/bill-payments-unpaid-by-currency-pagination.md |
Currency-indexed pagination |
SC-015_CURRENCY_VALIDATION.md |
Original currency validation implementation doc |
remitwise-common/src/tokens.rs |
SupportedToken enum source |
remitwise-common/src/lib.rs |
STABLE_CURRENCIES, require_stable_currency source |
bill_payments/src/lib.rs |
validate_and_normalize_currency source |