Overview
Sovran allows you to use multiple Cashu mints simultaneously. The mint management system handles:
Discovering and adding new mints
Viewing mint information and metadata
Trusting/untrusting mints
Checking mint balances
Restoring funds from mints
Adding Mints
Via the Add Mints Screen
The primary mint discovery interface (app/(mint-flow)/add.tsx) shows:
Discovered mints from Nostr and Sovran’s curated list
KYM scores (community ratings)
Audit data (success rates, operation counts)
Currency filters (SAT, USD, EUR, GBP)
import { useMintManagement } from '@/hooks/coco/useMintManagement' ;
import { useNostrDiscoveredMints } from 'hooks/coco/useNostrDiscoveredMints' ;
import { useSovranDiscoveredMints } from 'hooks/coco/useSovranDiscoveredMints' ;
function AddMintsScreen () {
const { addMint } = useMintManagement ();
const { mints : nostrMints } = useNostrDiscoveredMints ();
const { mints : sovranMints } = useSovranDiscoveredMints ();
// Merge and deduplicate
const discoveredMints = [ ... nostrMints , ... sovranMints ];
}
Discovery Sources
Nostr Discovery (useNostrDiscoveredMints):
Subscribes to kind 10002 (relay lists) for mint recommendations
Extracts mint URLs from Nostr events
Fetches mint info for each discovered URL
Sovran Discovery (useSovranDiscoveredMints):
Curated list from Sovran’s backend
Pre-vetted, high-quality mints
Updated regularly
Adding a Mint Programmatically
import { useMintManagement } from '@/hooks/coco/useMintManagement' ;
const { addMint , getMintInfo } = useMintManagement ();
// Add mint and trust it
await addMint ( 'https://mint.example.com' );
// Fetch mint information
const info = await getMintInfo ( 'https://mint.example.com' );
URL Validation
The useDebouncedMintValidation hook validates mint URLs in real-time:
import { useDebouncedMintValidation } from 'hooks/coco/useDebouncedMintValidation' ;
const {
url ,
setUrl ,
validationState , // { isValid, isLoading, error }
mintInfo
} = useDebouncedMintValidation ( 800 ); // 800ms debounce
Validation checks:
URL format (https:// or http://)
Reachability
Valid Cashu mint (responds to GET /v1/info)
NUT support (protocols implemented)
Mint List Screen
The mint list (app/(mint-flow)/list.tsx) displays owned mints with:
Balance per mint
Currency filtering
Mint selection for operations
Quick access to mint details
import { MintListScreen } from 'components/screens/MintListScreen' ;
< MintListScreen
requireBalance = { true } // Only show mints with balance
minAmount = { 1000 } // Minimum balance filter (sats)
allowedMints = { [ 'mint1' , 'mint2' ]} // Restrict to specific mints
showDetailsButton={true}
onMintSelect={(mint) => {
// Handle mint selection
}}
onInspectMint={(mintUrl) => {
router.navigate('/info', { mintUrl });
}}
/>
Filtering Mints
By currency:
const mintsForUnit = trustedMints . filter (( mint ) => {
if ( ! mint . mintInfo ?. nuts ?.[ '4' ]?. methods ) return false ;
return mint . mintInfo . nuts [ '4' ]. methods . some (
( method ) => method . unit ?. toLowerCase () === 'sat'
);
});
By balance:
const { balance } = useBalanceContext ();
const mintsWithBalance = trustedMints . filter (( mint ) => {
return ( balance [ mint . mintUrl ] || 0 ) > 0 ;
});
The mint info screen (app/(mint-flow)/info.tsx) displays comprehensive mint details:
Basic Info
interface MintInfo {
name : string ;
pubkey ?: string ;
version ?: string ;
description ?: string ;
description_long ?: string ;
icon_url ?: string ;
motd ?: string ; // Message of the day
contact ?: Contact [];
nuts : Record < string , any >; // Supported NUTs
}
interface Contact {
method : 'email' | 'twitter' | 'nostr' | string ;
info : string ;
}
Usage Example
import { useAuditedMint } from 'hooks/coco/useAuditedMint' ;
import { useKYMMint } from 'hooks/coco/useKYMMint' ;
function MintInfoScreen ({ mintUrl } : { mintUrl : string }) {
const { auditInfo , mintInfo , loading } = useAuditedMint ( mintUrl );
const { score , recommendations } = useKYMMint ( mintUrl );
return (
< View >
< Text >{mintInfo?.name || 'Unknown Mint' } </ Text >
< Text > Community Rating : { score ?. toFixed ( 1 )}/ 5 </ Text >
< Text > Success Rate : { auditInfo ?. successRate }%</ Text >
</ View >
);
}
Mint Operations
Trust Management
import { CocoManager } from 'helper/coco/manager' ;
const manager = CocoManager . getInstance ();
// Add and trust a mint
await manager . mint . addMint ( mintUrl );
await manager . mint . trustMint ( mintUrl );
// Check if mint is trusted
const isTrusted = await manager . mint . isTrustedMint ( mintUrl );
// Untrust a mint
await manager . mint . untrustMint ( mintUrl );
// Get all trusted mints
const trustedMints = await manager . mint . getAllTrustedMints ();
Getting Balances
import { useBalanceContext } from 'coco-cashu-react' ;
function BalanceDisplay () {
const { balance } = useBalanceContext ();
// balance: Record<mintUrl, amount>
const totalBalance = Object . values ( balance ). reduce (
( sum , amt ) => sum + amt ,
0
);
return < Text >{ totalBalance } sats </ Text > ;
}
Restoring Funds
If you’ve lost local proof data but have your mnemonic, restore from mints:
import { useMintManagement } from '@/hooks/coco/useMintManagement' ;
const { restoreMint } = useMintManagement ();
// Scans mint for proofs derived from your seed
await restoreMint ( mintUrl );
How it works:
Derives proof secrets from your mnemonic
Queries mint for matching proofs
Downloads and stores valid proofs
Updates local balance
Restore only works if the mint still has your proofs. If proofs were already redeemed by someone else, they cannot be recovered.
Mint Selection UI
The MintListScreen component provides a reusable mint selection interface:
import { MintListScreen } from 'components/screens/MintListScreen' ;
< MintListScreen
requireBalance = { true }
showDetailsButton = { true }
currencyLabel = "Currency"
mintsLabel = "Your mints"
closeButtonLabel = "Close"
onMintSelect = {(mint) => {
console . log ( 'Selected:' , mint );
}}
onInspectMint = {(mintUrl) => {
console . log ( 'Inspect:' , mintUrl );
}}
onClose = {() => {
router . back ();
}}
/>
Currency Tabs
The mint list includes currency filtering with animated tabs:
import { MintCurrencyTabs } from 'components/blocks/sheets/mint-balance/MintCurrencyTabs' ;
< MintCurrencyTabs
currencies = { [ 'ALL' , 'SAT' , 'USD' , 'EUR' ]}
selectedCurrency={selectedCurrency}
onCurrencyChange={setSelectedCurrency}
scrollY={scrollY} // For sticky header animation
/>
Advanced: Direct Manager Access
For low-level operations, use the CocoManager directly:
import { CocoManager } from 'helper/coco/manager' ;
const manager = CocoManager . getInstance ();
// Get wallet for specific mint
const wallet = await manager . walletService . getWallet ( mintUrl );
// Get mint info
const info = await manager . mint . getMintInfo ( mintUrl );
// Check if manager is initialized
if ( CocoManager . isInitialized ()) {
// Safe to use manager
}
// Restore inflight proofs (after failed operations)
await CocoManager . restoreInflightProofsForMint ( mintUrl );
Best Practices
Don’t put all your funds in one mint. Distribute across 3-5 trusted mints to reduce custodial risk.
Check Ratings Before Adding
Always review KYM scores and audit data before trusting a new mint. See Know Your Mint .
Keep an eye on per-mint balances. Use wallet rebalancing to maintain target distributions.
Your mnemonic is the only way to restore funds if you lose your device. Store it securely offline.
Error Handling
import { useMintManagement } from '@/hooks/coco/useMintManagement' ;
import {
managerNotInitializedPopup ,
mintsAddFailedPopup ,
mintsAddedPopup
} from '@/helper/popup' ;
const { addMint , error } = useMintManagement ();
try {
await addMint ( mintUrl );
mintsAddedPopup ({ added: 1 });
} catch ( err ) {
if ( err . message . includes ( 'not initialized' )) {
managerNotInitializedPopup ();
} else {
mintsAddFailedPopup ();
}
}
Know Your Mint Learn about mint auditing and ratings
Wallet Rebalancing Automatically distribute balance across mints