CurrencyRegistry answers one question for the factory and the router: for currency code X, which token is the quote asset, how many decimals does it have, is it a real stablecoin or a synthetic, where is its vault, and may new launches use it. Changes only affect launches created afterwards; an existing curve keeps its quote asset forever.
Source: contracts/src/CurrencyRegistry.sol (Ownable2Step). Interface: contracts/src/interfaces/ICurrencyRegistry.sol. Addresses: Addresses.
Configuration record
"USD" (the constant USD = "USD") as tier 1 with usdc as its quote asset and records the oracle. usdc is immutable; the oracle can be replaced.
Views
"INR" is 0x494e520000000000000000000000000000000000000000000000000000000000. In viem: stringToHex("INR", { size: 32 }); with Foundry: cast --format-bytes32-string INR.
Owner functions
setOracle replaces the registry’s oracle (ZeroAddress for 0x0). It changes what the factory uses to size new curves and what the router charges as an update fee. It does not change the oracle of any existing vault: each FxVault stores its oracle as an immutable at creation. See Oracles.
setRealCurrency registers, or re-registers, a tier-1 stablecoin for a code. The oracle must support the code (NotSupportedByOracle), the asset must be non-zero and have at most 18 decimals (DecimalsTooHigh). Calling it for a code that already exists overwrites the record: this is how a currency moves from tier 2 to tier 1 when a real stablecoin lands, without touching launches already quoted in the synthetic. The reverse lookup for the new asset is set; the old synthetic keeps its own reverse entry.
createSynthetic deploys a SynthToken(name, symbol) and an FxVault for the code, wires them (setVault), registers the pair as tier 2 with 18 decimals and enables it. The vault is owned by the registry’s current owner, uses the registry’s dollar and current oracle, and is named "<name> Underwriter" / "uw-<symbol>" for its share token. Reverts NotSupportedByOracle(code) if the oracle does not list the code and AlreadyExists(code) if the code already has a quote asset. A code can therefore never get a second synthetic.
setEnabled toggles whether new launches may use a code (UnknownCurrency for unregistered codes). Disabling a currency stops createLaunch for it; trading, minting and redeeming continue.
The owner cannot remove a code, change a synthetic’s vault, or edit a vault’s parameters from here (vault parameters are set on the vault itself; the registry owner is also each vault’s owner at creation).
Events
The indexer seeds its
currency table from allCodes() + get(code) and keeps it in sync from CurrencySet (see Currencies query).
Errors
How tiers are decided
Tier is a property of the registration, not of the currency. The deploy script registersUSD (always), EUR when the chain has EURC and the script is told about it, and the sixty synthetics from Currencies.sol with the vault parameters in FxVault. Tier 1 with quoteAsset == usdc() is the USD case the router and factory special-case; tier 1 with any other asset (EURC) can only be traded and launched with that asset directly; tier 2 goes through the vault. Any ISO currency that is not registered can still be a display currency in the app, converted off-chain.