Sovran supports multiple fiat currency displays for your Bitcoin balance. Choose from USD, EUR, GBP, or display in sats with customizable formatting.
Supported Currencies
USD
United States Dollar (Default)
GBP
British Pound Sterling
Sats
Bitcoin satoshis (native)
Currency Configuration
The display currency is managed through the settingsStore:
export type DisplayCurrency = 'usd' | 'eur' | 'gbp';
interface SettingsState {
displayCurrency: DisplayCurrency; // Default: 'usd'
displayBtc: number; // 0-3 (sats display mode)
}
interface SettingsActions {
setDisplayCurrency: (currency: DisplayCurrency) => void;
getDisplayCurrency: () => DisplayCurrency;
setDisplayBtc: (display: number) => void;
getDisplayBtc: () => number;
}
Sats Display Modes
Sovran offers 4 different ways to display satoshi amounts:
Mode 0: Bitcoin Icon Prefix
₿ 0.00012345 — Shows BTC icon with 8 decimal places
Mode 1: Lightning Icon Suffix
12,345 ⚡ — Shows sats with lightning bolt
Mode 2: Text-only Sats
12,345 sats — Plain text format
Mode 3: Alternative BTC Icon
₿ 0.00012345 — Alternative BTC display (same as mode 0)
Setting Display Mode
import { useSettingsStore } from 'stores/settingsStore';
const setDisplayBtc = useSettingsStore(state => state.setDisplayBtc);
// Use lightning icon mode
setDisplayBtc(1);
Price Data Management
Exchange rates are fetched and stored in the pricelistStore.
Pricelist Store
interface PricelistData {
usd: { btc: number }; // USD per BTC
eur?: { btc: number }; // EUR per BTC
gbp?: { btc: number }; // GBP per BTC
}
interface PricelistState {
pricelist: PricelistData | null;
isLoading: boolean;
lastUpdated: number | null; // Timestamp
error: string | null;
}
Setting Exchange Rates
import { usePricelistStore } from 'stores/pricelistStore';
const setBtcPrices = usePricelistStore(state => state.setBtcPrices);
// Update all currency rates
setBtcPrices({
USD: 63900,
EUR: 53500,
GBP: 47800,
});
// Update only USD
const setBtcPrice = usePricelistStore(state => state.setBtcPrice);
setBtcPrice(64000);
Retrieving Prices
import { useBtcPrice } from 'stores/pricelistStore';
// React hook
const usdPrice = useBtcPrice('usd'); // Current USD/BTC rate
const eurPrice = useBtcPrice('eur');
// Direct access
const getBtcPrice = usePricelistStore(state => state.getBtcPrice);
const currentPrice = getBtcPrice('usd');
Price Staleness Check
Check if cached prices are outdated:
const isStale = usePricelistStore(state => state.isStale);
if (isStale(5)) { // 5 minutes
// Fetch fresh prices
}
Fiat Currency Pill Component
The FiatCurrencyPill component displays fiat equivalents with currency selection.
Basic Usage
components/blocks/FiatCurrencyPill.tsx
import { FiatCurrencyPill } from 'components/blocks/FiatCurrencyPill';
<FiatCurrencyPill
displayText="≈ $12.34"
onPress={() => toggleInputMode()}
showToggleGlyph
enableCurrencyMenu
/>
Props
interface FiatCurrencyPillProps {
displayText: string; // Display string (e.g., "≈ $12.34")
onPress?: () => void; // Tap handler (toggle input mode)
onSelectCurrency?: (currency: DisplayCurrency) => void; // Override currency selection
showToggleGlyph?: boolean; // Show ⇄ icon
textSize?: number; // Font size (default: 14)
enableCurrencyMenu?: boolean; // Enable long-press menu (default: true)
}
On iOS with liquid glass support, the pill shows a native context menu:
<Menu
onPrimaryAction={onPress}
label={<SwiftUIText>{displayText}</SwiftUIText>}>
<SwiftUIButton
systemImage="dollarsign"
label="USD"
onPress={() => handleSelectCurrency('usd')}
/>
<SwiftUIButton
systemImage="eurosign"
label="EUR"
onPress={() => handleSelectCurrency('eur')}
/>
<SwiftUIButton
systemImage="sterlingsign"
label="GBP"
onPress={() => handleSelectCurrency('gbp')}
/>
</Menu>
Fallback Behavior
On Android or older iOS versions, currency selection uses ActionSheetIOS:
ActionSheetIOS.showActionSheetWithOptions(
{
options: ['USD', 'EUR', 'GBP', 'Cancel'],
cancelButtonIndex: 3,
userInterfaceStyle: 'dark',
},
(buttonIndex) => {
if (buttonIndex === 0) handleSelectCurrency('usd');
if (buttonIndex === 1) handleSelectCurrency('eur');
if (buttonIndex === 2) handleSelectCurrency('gbp');
}
);
The AmountFormatter component handles all currency display logic.
Basic Usage
components/ui/AmountFormatter.tsx
import { AmountFormatter } from 'components/ui/AmountFormatter';
<AmountFormatter
amount={12345}
unit="sat"
size={42}
weight="heavy"
useTypeColors
transactionType="receive"
/>
Props
interface AmountFormatterProps {
amount: number; // Amount to display
unit: CurrencyUnit; // 'sat' | 'usd' | 'eur' | string
size?: number; // Font size (default: 42)
weight?: FontWeight; // Font weight (default: 'heavy')
color?: string; // Text color
style?: StyleProp<ViewStyle>; // Container style
animated?: boolean; // Animate size changes
useTypeColors?: boolean; // Color based on transaction type
transactionType?: TransactionType; // 'send' | 'receive'
centered?: boolean; // Center alignment
className?: string; // Tailwind classes
}
Display Modes
// Mode 0 & 3: BTC icon prefix
<HStack>
<BtcIcon /> {/* ₿ */}
<Text>0.00012345</Text>
</HStack>
// Mode 1: Lightning icon suffix
<HStack>
<Text>12,345</Text>
<LightningUnit /> {/* ⚡ */}
</HStack>
// Mode 2: Text-only
<Text>12,345 sats</Text>
// Fiat currencies
<Text>$123.45</Text>
Type-based Colors
function getTypeColor(
amount: number,
transactionType: TransactionType,
foreground: string,
danger: string
): string {
if (!amount) return opacity(foreground, 0.4);
return transactionType === 'receive' ? foreground : danger;
}
The helper/currency.ts module provides formatting functions.
import { formatAmount } from 'helper/currency';
// Basic formatting
formatAmount({ amount: 12345, unit: 'sat' });
// → "12,345"
// Convert to different currency
formatAmount({ amount: 100000, unit: 'sat' }, { displayAs: 'usd' });
// → "$63.90"
// Use user's display preference
formatAmount({ amount: 12345, unit: 'sat' }, { useUserPreference: true });
// → "12,345" or "0.00012345" depending on displayBtc mode
Currency Symbols
const SYMBOLS: Record<string, string> = {
usd: '$',
eur: '€',
gbp: '£',
btc: '₿',
sats: 'ṩ',
};
Exchange Rate Conversion
All conversions route through BTC as the base unit:
function getRate(unit: string): number {
const pricelist = usePricelistStore.getState().pricelist;
const rates: Record<string, number> = {
btc: 1,
sats: 100_000_000,
usd: pricelist?.usd?.btc ?? 63_900, // Fallback
eur: pricelist?.eur?.btc ?? 53_500,
gbp: pricelist?.gbp?.btc ?? 47_800,
};
return rates[unit] ?? 1;
}
// Convert 1000 sats to USD
const satsInBtc = 1000 / 100_000_000; // → 0.00001 BTC
const usdValue = satsInBtc * 63_900; // → $0.639
const satsFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
});
const fiatFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
const btcFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 8,
maximumFractionDigits: 8,
});
Fiat amounts are stored as cents (not dollars) to avoid floating-point errors:// Storing $12.34
const storedValue = 1234; // Cents
// Converting for display
const dollars = storedValue / 100; // → 12.34
Complete Example
Here’s a complete example showing currency selection and display:
import { useState } from 'react';
import { useSettingsStore } from 'stores/settingsStore';
import { useBtcPrice } from 'stores/pricelistStore';
import { formatAmount } from 'helper/currency';
import { AmountFormatter } from 'components/ui/AmountFormatter';
import { FiatCurrencyPill } from 'components/blocks/FiatCurrencyPill';
function WalletBalance() {
const satsBalance = 123456;
const displayCurrency = useSettingsStore(state => state.displayCurrency);
const btcPrice = useBtcPrice(displayCurrency);
// Calculate fiat equivalent
const fiatValue = formatAmount(
{ amount: satsBalance, unit: 'sat' },
{ displayAs: displayCurrency, currencyDisplay: 'symbol' }
);
return (
<View>
{/* Main balance */}
<AmountFormatter
amount={satsBalance}
unit="sat"
size={48}
weight="heavy"
/>
{/* Fiat equivalent */}
<FiatCurrencyPill
displayText={`≈ ${fiatValue}`}
enableCurrencyMenu
/>
</View>
);
}
Best Practices
Always provide fallback exchange rates:const rate = pricelist?.usd?.btc ?? 63_900; // Hardcoded fallback
Check price staleness before displaying fiat values:const isStale = usePricelistStore(state => state.isStale);
if (isStale(5)) {
// Show "Price data outdated" indicator
// Trigger background refresh
}
Never perform currency conversions on the frontend without validated exchange rates. Always fetch from a trusted price feed.
Themes
Customize your wallet appearance
Settings Overview
Configure all app preferences