suix_getCoinMetadata
Retrieves comprehensive metadata for any coin type on the Sui network, including name, symbol, decimal places, and display information.
Overview​
The suix_getCoinMetadata
method is essential for applications that handle multiple coin types on Sui. It provides standardized metadata that enables proper formatting of coin balances, display of coin information in user interfaces, and integration with external systems. This method is crucial for wallets, DEX interfaces, portfolio trackers, and any application that needs to present coin information in a user-friendly format.
Parameters​
Parameter | Type | Required | Description |
---|---|---|---|
coinType | string | Yes | The coin type identifier in format: packageId::module::type |
Coin Type Format​
The coin type follows the standard Sui format: packageId::module::type
Common examples:
- SUI:
0x2::sui::SUI
- USDC:
0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC
- Custom tokens:
0xpackageId::module::COIN_NAME
Returns​
Returns an object containing comprehensive metadata about the specified coin type.
Field | Type | Description |
---|---|---|
decimals | number | Number of decimal places for the coin |
name | string | Full name of the coin |
symbol | string | Trading symbol of the coin |
description | string | Description of the coin |
iconUrl | string | URL to the coin's icon image (optional) |
id | string | Unique identifier for the metadata object |
Decimal Places​
The decimals
field indicates how to format the coin balance:
- SUI: 9 decimals (1 SUI = 1,000,000,000 MIST)
- USDC: 6 decimals (1 USDC = 1,000,000 micro-USDC)
- Custom tokens: Variable, as defined by the token creator
Code Examples​
- cURL
- JavaScript
- Python
# Get SUI metadata
curl -X POST https://sui-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "suix_getCoinMetadata",
"params": [
"0x2::sui::SUI"
],
"id": 1
}'
# Get USDC metadata
curl -X POST https://sui-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "suix_getCoinMetadata",
"params": [
"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
],
"id": 1
}'
# Get custom token metadata
curl -X POST https://sui-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "suix_getCoinMetadata",
"params": [
"0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT"
],
"id": 1
}'
import { SuiClient } from '@mysten/sui.js/client';
const client = new SuiClient({
url: 'https://sui-mainnet.dwellir.com/YOUR_API_KEY'
});
// Get metadata for a single coin type
async function getCoinMetadata(coinType) {
try {
const metadata = await client.getCoinMetadata({ coinType });
console.log(`Coin: ${metadata.name} (${metadata.symbol})`);
console.log(`Decimals: ${metadata.decimals}`);
console.log(`Description: ${metadata.description}`);
if (metadata.iconUrl) {
console.log(`Icon: ${metadata.iconUrl}`);
}
return metadata;
} catch (error) {
console.error(`Failed to get metadata for ${coinType}:`, error);
return null;
}
}
// Get metadata for multiple coin types
async function getMultipleCoinMetadata(coinTypes) {
const metadataPromises = coinTypes.map(async (coinType) => {
try {
const metadata = await client.getCoinMetadata({ coinType });
return { coinType, metadata, error: null };
} catch (error) {
return { coinType, metadata: null, error: error.message };
}
});
const results = await Promise.all(metadataPromises);
return results.reduce((acc, result) => {
acc[result.coinType] = result.metadata || { error: result.error };
return acc;
}, {});
}
// Format balance using metadata
async function formatCoinBalance(coinType, rawBalance) {
const metadata = await getCoinMetadata(coinType);
if (!metadata) {
return `${rawBalance} (unknown)`;
}
const formattedBalance = Number(rawBalance) / Math.pow(10, metadata.decimals);
return {
raw: rawBalance,
formatted: formattedBalance,
display: `${formattedBalance.toFixed(metadata.decimals <= 2 ? metadata.decimals : 4)} ${metadata.symbol}`,
metadata: metadata
};
}
// Create a coin metadata cache
class CoinMetadataCache {
constructor(client) {
this.client = client;
this.cache = new Map();
this.pendingRequests = new Map();
}
async getMetadata(coinType) {
// Check cache first
if (this.cache.has(coinType)) {
return this.cache.get(coinType);
}
// Check if request is already pending
if (this.pendingRequests.has(coinType)) {
return await this.pendingRequests.get(coinType);
}
// Create new request
const request = this._fetchMetadata(coinType);
this.pendingRequests.set(coinType, request);
try {
const metadata = await request;
this.cache.set(coinType, metadata);
this.pendingRequests.delete(coinType);
return metadata;
} catch (error) {
this.pendingRequests.delete(coinType);
throw error;
}
}
async _fetchMetadata(coinType) {
const metadata = await this.client.getCoinMetadata({ coinType });
return metadata;
}
// Batch load multiple metadata
async loadMetadataBatch(coinTypes) {
const promises = coinTypes.map(coinType => this.getMetadata(coinType));
return Promise.allSettled(promises);
}
// Clear cache
clearCache() {
this.cache.clear();
}
// Get cache stats
getCacheStats() {
return {
cachedTypes: this.cache.size,
pendingRequests: this.pendingRequests.size,
cachedCoinTypes: Array.from(this.cache.keys())
};
}
}
// Portfolio formatter with metadata
async function formatPortfolioBalances(balances) {
const metadataCache = new CoinMetadataCache(client);
// Extract unique coin types
const coinTypes = [...new Set(balances.map(b => b.coinType))];
// Load all metadata in batch
await metadataCache.loadMetadataBatch(coinTypes);
// Format each balance
const formattedBalances = await Promise.all(
balances.map(async (balance) => {
try {
const metadata = await metadataCache.getMetadata(balance.coinType);
const formattedAmount = Number(balance.totalBalance) / Math.pow(10, metadata.decimals);
return {
coinType: balance.coinType,
symbol: metadata.symbol,
name: metadata.name,
rawBalance: balance.totalBalance,
formattedBalance: formattedAmount,
displayBalance: `${formattedAmount.toFixed(4)} ${metadata.symbol}`,
decimals: metadata.decimals,
iconUrl: metadata.iconUrl,
objectCount: balance.coinObjectCount || 1
};
} catch (error) {
console.warn(`Failed to format balance for ${balance.coinType}:`, error);
return {
coinType: balance.coinType,
symbol: 'UNKNOWN',
name: 'Unknown Token',
rawBalance: balance.totalBalance,
formattedBalance: balance.totalBalance,
displayBalance: `${balance.totalBalance} UNKNOWN`,
decimals: 0,
iconUrl: null,
error: error.message
};
}
})
);
return formattedBalances;
}
// Coin registry for common tokens
class CoinRegistry {
constructor() {
this.metadataCache = new CoinMetadataCache(client);
this.knownCoins = new Map([
['0x2::sui::SUI', { symbol: 'SUI', name: 'Sui', decimals: 9 }],
['0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', { symbol: 'USDC', name: 'USD Coin', decimals: 6 }]
]);
}
async registerCoin(coinType) {
try {
const metadata = await this.metadataCache.getMetadata(coinType);
this.knownCoins.set(coinType, {
symbol: metadata.symbol,
name: metadata.name,
decimals: metadata.decimals,
description: metadata.description,
iconUrl: metadata.iconUrl
});
return metadata;
} catch (error) {
console.error(`Failed to register coin ${coinType}:`, error);
return null;
}
}
getKnownCoin(coinType) {
return this.knownCoins.get(coinType) || null;
}
getAllKnownCoins() {
return Array.from(this.knownCoins.entries()).map(([coinType, info]) => ({
coinType,
...info
}));
}
async formatBalance(coinType, balance) {
let metadata = this.getKnownCoin(coinType);
if (!metadata) {
metadata = await this.registerCoin(coinType);
}
if (!metadata) {
return {
formatted: balance,
display: `${balance} UNKNOWN`,
error: 'Unknown coin type'
};
}
const formatted = Number(balance) / Math.pow(10, metadata.decimals);
return {
formatted: formatted,
display: `${formatted.toFixed(4)} ${metadata.symbol}`,
symbol: metadata.symbol,
name: metadata.name,
decimals: metadata.decimals,
iconUrl: metadata.iconUrl
};
}
}
// DEX price calculator with metadata
async function calculateTradeValue(fromCoinType, toCoinType, amount) {
const metadataCache = new CoinMetadataCache(client);
const [fromMetadata, toMetadata] = await Promise.all([
metadataCache.getMetadata(fromCoinType),
metadataCache.getMetadata(toCoinType)
]);
const fromAmount = Number(amount) / Math.pow(10, fromMetadata.decimals);
// Note: This would require price feed integration for actual exchange rates
const exchangeRate = 1; // Placeholder
const toAmount = fromAmount * exchangeRate;
const toAmountRaw = Math.floor(toAmount * Math.pow(10, toMetadata.decimals));
return {
from: {
coinType: fromCoinType,
symbol: fromMetadata.symbol,
amount: fromAmount,
display: `${fromAmount.toFixed(4)} ${fromMetadata.symbol}`
},
to: {
coinType: toCoinType,
symbol: toMetadata.symbol,
amount: toAmount,
amountRaw: toAmountRaw.toString(),
display: `${toAmount.toFixed(4)} ${toMetadata.symbol}`
},
exchangeRate: exchangeRate
};
}
// Usage examples
const commonCoinTypes = [
'0x2::sui::SUI',
'0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',
'0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT'
];
// Get SUI metadata
const suiMetadata = await getCoinMetadata('0x2::sui::SUI');
console.log('SUI Metadata:', suiMetadata);
// Get multiple coin metadata
const allMetadata = await getMultipleCoinMetadata(commonCoinTypes);
console.log('All Metadata:', allMetadata);
// Format a balance
const formattedBalance = await formatCoinBalance('0x2::sui::SUI', '1500000000');
console.log('Formatted Balance:', formattedBalance.display);
// Use coin registry
const registry = new CoinRegistry();
const balanceInfo = await registry.formatBalance('0x2::sui::SUI', '2500000000');
console.log('Registry Balance:', balanceInfo.display);
import requests
import json
from typing import Dict, List, Any, Optional, Union
from decimal import Decimal, getcontext
from dataclasses import dataclass
import asyncio
import aiohttp
from functools import lru_cache
# Set high precision for decimal calculations
getcontext().prec = 50
@dataclass
class CoinMetadata:
decimals: int
name: str
symbol: str
description: str
icon_url: Optional[str] = None
coin_type: Optional[str] = None
class SuiCoinMetadataClient:
def __init__(self, rpc_url: str):
self.rpc_url = rpc_url
self.metadata_cache = {}
def get_coin_metadata(self, coin_type: str) -> Optional[CoinMetadata]:
"""Get metadata for a specific coin type"""
# Check cache first
if coin_type in self.metadata_cache:
return self.metadata_cache[coin_type]
payload = {
"jsonrpc": "2.0",
"method": "suix_getCoinMetadata",
"params": [coin_type],
"id": 1
}
try:
response = requests.post(
self.rpc_url,
headers={'Content-Type': 'application/json'},
data=json.dumps(payload),
timeout=30
)
result = response.json()
if 'error' in result:
print(f"RPC Error for {coin_type}: {result['error']}")
return None
metadata_data = result['result']
metadata = CoinMetadata(
decimals=metadata_data['decimals'],
name=metadata_data['name'],
symbol=metadata_data['symbol'],
description=metadata_data.get('description', ''),
icon_url=metadata_data.get('iconUrl'),
coin_type=coin_type
)
# Cache the result
self.metadata_cache[coin_type] = metadata
return metadata
except Exception as e:
print(f"Error fetching metadata for {coin_type}: {e}")
return None
def get_multiple_coin_metadata(self, coin_types: List[str]) -> Dict[str, Optional[CoinMetadata]]:
"""Get metadata for multiple coin types"""
results = {}
for coin_type in coin_types:
results[coin_type] = self.get_coin_metadata(coin_type)
return results
def format_balance(
self,
coin_type: str,
raw_balance: Union[str, int],
precision: Optional[int] = None
) -> Dict[str, Any]:
"""Format a raw balance using coin metadata"""
metadata = self.get_coin_metadata(coin_type)
if not metadata:
return {
'raw': str(raw_balance),
'formatted': str(raw_balance),
'display': f"{raw_balance} UNKNOWN",
'symbol': 'UNKNOWN',
'error': 'Metadata not found'
}
# Use Decimal for precise calculations
raw_decimal = Decimal(str(raw_balance))
divisor = Decimal(10) ** metadata.decimals
formatted_decimal = raw_decimal / divisor
# Determine precision for display
if precision is None:
if metadata.decimals <= 2:
precision = metadata.decimals
elif metadata.decimals <= 6:
precision = 4
else:
precision = 6
formatted_float = float(formatted_decimal)
display_str = f"{formatted_float:.{precision}f} {metadata.symbol}"
return {
'raw': str(raw_balance),
'formatted': formatted_float,
'formatted_decimal': formatted_decimal,
'display': display_str,
'symbol': metadata.symbol,
'name': metadata.name,
'decimals': metadata.decimals,
'icon_url': metadata.icon_url
}
def parse_display_amount(self, coin_type: str, display_amount: str) -> Optional[str]:
"""Convert display amount back to raw balance"""
metadata = self.get_coin_metadata(coin_type)
if not metadata:
return None
try:
# Remove symbol and parse number
amount_str = display_amount.replace(metadata.symbol, '').strip()
amount_decimal = Decimal(amount_str)
# Convert to raw balance
multiplier = Decimal(10) ** metadata.decimals
raw_balance = amount_decimal * multiplier
return str(int(raw_balance))
except Exception as e:
print(f"Error parsing display amount: {e}")
return None
def create_balance_summary(self, balances: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Create a formatted summary of multiple balances"""
summary = {
'total_balance_types': 0,
'balances': [],
'total_value_usd': 0, # Would need price feed integration
'unknown_tokens': []
}
for balance in balances:
coin_type = balance['coinType']
total_balance = balance['totalBalance']
formatted = self.format_balance(coin_type, total_balance)
balance_info = {
'coin_type': coin_type,
'symbol': formatted.get('symbol', 'UNKNOWN'),
'name': formatted.get('name', 'Unknown'),
'raw_balance': formatted['raw'],
'formatted_balance': formatted['formatted'],
'display': formatted['display'],
'decimals': formatted.get('decimals', 0),
'icon_url': formatted.get('icon_url'),
'object_count': balance.get('coinObjectCount', 1)
}
if 'error' in formatted:
summary['unknown_tokens'].append(balance_info)
else:
summary['balances'].append(balance_info)
summary['total_balance_types'] = len(summary['balances']) + len(summary['unknown_tokens'])
# Sort by formatted balance descending
summary['balances'].sort(key=lambda x: x['formatted_balance'], reverse=True)
return summary
def get_cache_stats(self) -> Dict[str, Any]:
"""Get statistics about the metadata cache"""
return {
'cached_coin_types': len(self.metadata_cache),
'coin_types': list(self.metadata_cache.keys()),
'cache_size_bytes': sum(
len(str(metadata.__dict__))
for metadata in self.metadata_cache.values()
)
}
def clear_cache(self):
"""Clear the metadata cache"""
self.metadata_cache.clear()
class CoinRegistry:
"""Registry for managing known coins and their metadata"""
def __init__(self, client: SuiCoinMetadataClient):
self.client = client
self.known_coins = {
'0x2::sui::SUI': {
'symbol': 'SUI',
'name': 'Sui',
'decimals': 9,
'description': 'Native token of the Sui blockchain'
},
'0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC': {
'symbol': 'USDC',
'name': 'USD Coin',
'decimals': 6,
'description': 'USD-backed stablecoin'
}
}
def register_coin(self, coin_type: str) -> bool:
"""Register a new coin type by fetching its metadata"""
metadata = self.client.get_coin_metadata(coin_type)
if metadata:
self.known_coins[coin_type] = {
'symbol': metadata.symbol,
'name': metadata.name,
'decimals': metadata.decimals,
'description': metadata.description,
'icon_url': metadata.icon_url
}
return True
return False
def get_known_coin_info(self, coin_type: str) -> Optional[Dict[str, Any]]:
"""Get info for a known coin"""
return self.known_coins.get(coin_type)
def get_all_known_coins(self) -> List[Dict[str, Any]]:
"""Get all known coins with their info"""
return [
{'coin_type': coin_type, **info}
for coin_type, info in self.known_coins.items()
]
def is_known_coin(self, coin_type: str) -> bool:
"""Check if a coin type is known"""
return coin_type in self.known_coins
def format_known_balance(self, coin_type: str, raw_balance: Union[str, int]) -> Dict[str, Any]:
"""Format balance for a known coin type"""
if not self.is_known_coin(coin_type):
# Try to register it
if not self.register_coin(coin_type):
return {'error': 'Unknown coin type'}
return self.client.format_balance(coin_type, raw_balance)
class PortfolioFormatter:
"""Advanced portfolio formatting with metadata"""
def __init__(self, client: SuiCoinMetadataClient):
self.client = client
self.registry = CoinRegistry(client)
def format_portfolio(self, balances: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Format a complete portfolio with metadata"""
portfolio = {
'summary': {
'total_coin_types': 0,
'total_objects': 0,
'has_unknown_tokens': False
},
'major_holdings': [], # > $100 value (would need price feeds)
'minor_holdings': [], # < $100 value
'dust_holdings': [], # < $1 value
'unknown_tokens': [],
'metadata_errors': []
}
for balance in balances:
coin_type = balance['coinType']
total_balance = balance['totalBalance']
object_count = balance.get('coinObjectCount', 1)
formatted = self.client.format_balance(coin_type, total_balance)
holding_info = {
'coin_type': coin_type,
'raw_balance': formatted['raw'],
'formatted_balance': formatted['formatted'],
'display': formatted['display'],
'object_count': object_count,
'metadata': {
'symbol': formatted.get('symbol', 'UNKNOWN'),
'name': formatted.get('name', 'Unknown'),
'decimals': formatted.get('decimals', 0),
'icon_url': formatted.get('icon_url')
}
}
portfolio['summary']['total_objects'] += object_count
if 'error' in formatted:
portfolio['unknown_tokens'].append(holding_info)
portfolio['summary']['has_unknown_tokens'] = True
else:
# Categorize by value (would need price feeds for actual USD values)
formatted_value = formatted['formatted']
if formatted_value >= 100: # Placeholder logic
portfolio['major_holdings'].append(holding_info)
elif formatted_value >= 1:
portfolio['minor_holdings'].append(holding_info)
else:
portfolio['dust_holdings'].append(holding_info)
# Sort holdings by balance
for category in ['major_holdings', 'minor_holdings', 'dust_holdings']:
portfolio[category].sort(
key=lambda x: x['formatted_balance'],
reverse=True
)
portfolio['summary']['total_coin_types'] = (
len(portfolio['major_holdings']) +
len(portfolio['minor_holdings']) +
len(portfolio['dust_holdings']) +
len(portfolio['unknown_tokens'])
)
return portfolio
def create_portfolio_report(self, balances: List[Dict[str, Any]]) -> str:
"""Create a text report of the portfolio"""
portfolio = self.format_portfolio(balances)
report_lines = [
"=== PORTFOLIO REPORT ===",
f"Total Coin Types: {portfolio['summary']['total_coin_types']}",
f"Total Objects: {portfolio['summary']['total_objects']}",
""
]
if portfolio['major_holdings']:
report_lines.extend([
"MAJOR HOLDINGS:",
"-" * 40
])
for holding in portfolio['major_holdings']:
report_lines.append(
f"{holding['metadata']['symbol']:8} | {holding['display']:>20} | "
f"{holding['object_count']} objects"
)
report_lines.append("")
if portfolio['minor_holdings']:
report_lines.extend([
"MINOR HOLDINGS:",
"-" * 40
])
for holding in portfolio['minor_holdings']:
report_lines.append(
f"{holding['metadata']['symbol']:8} | {holding['display']:>20} | "
f"{holding['object_count']} objects"
)
report_lines.append("")
if portfolio['dust_holdings']:
report_lines.extend([
"DUST HOLDINGS:",
"-" * 40
])
for holding in portfolio['dust_holdings']:
report_lines.append(
f"{holding['metadata']['symbol']:8} | {holding['display']:>20} | "
f"{holding['object_count']} objects"
)
report_lines.append("")
if portfolio['unknown_tokens']:
report_lines.extend([
"UNKNOWN TOKENS:",
"-" * 40
])
for holding in portfolio['unknown_tokens']:
short_type = holding['coin_type'][:50] + "..." if len(holding['coin_type']) > 50 else holding['coin_type']
report_lines.append(f"UNKNOWN | {holding['raw_balance']:>20} | {short_type}")
report_lines.append("")
return "\n".join(report_lines)
# Usage examples
client = SuiCoinMetadataClient('https://sui-mainnet.dwellir.com/YOUR_API_KEY')
# Example 1: Get SUI metadata
sui_metadata = client.get_coin_metadata('0x2::sui::SUI')
if sui_metadata:
print(f"SUI: {sui_metadata.name} ({sui_metadata.symbol}), {sui_metadata.decimals} decimals")
# Example 2: Format balance
formatted = client.format_balance('0x2::sui::SUI', '1500000000')
print(f"Formatted SUI balance: {formatted['display']}")
# Example 3: Get multiple coin metadata
coin_types = [
'0x2::sui::SUI',
'0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',
'0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT'
]
all_metadata = client.get_multiple_coin_metadata(coin_types)
for coin_type, metadata in all_metadata.items():
if metadata:
print(f"{metadata.symbol}: {metadata.decimals} decimals")
else:
print(f"{coin_type}: Failed to get metadata")
# Example 4: Portfolio formatting
sample_balances = [
{'coinType': '0x2::sui::SUI', 'totalBalance': '2500000000', 'coinObjectCount': 3},
{'coinType': '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', 'totalBalance': '100000000', 'coinObjectCount': 1}
]
formatter = PortfolioFormatter(client)
portfolio_report = formatter.create_portfolio_report(sample_balances)
print("\n" + portfolio_report)
# Example 5: Coin registry usage
registry = CoinRegistry(client)
known_coins = registry.get_all_known_coins()
print(f"\nKnown coins: {len(known_coins)}")
for coin in known_coins:
print(f"- {coin['symbol']}: {coin['name']}")
Response Example​
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"decimals": 9,
"name": "Sui",
"symbol": "SUI",
"description": "The native token of the Sui blockchain",
"iconUrl": "https://sui.io/assets/sui-icon.png",
"id": "0x2::sui::SUI"
}
}
USDC Example Response​
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"decimals": 6,
"name": "USD Coin",
"symbol": "USDC",
"description": "A fully reserved digital dollar backed by short-term US Treasury bonds",
"iconUrl": "https://www.centre.io/images/usdc/usdc-icon-86x86.png",
"id": "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
}
}
Common Use Cases​
1. Balance Formatting in Wallets​
async function displayWalletBalances(balances) {
const metadataCache = new CoinMetadataCache(client);
const formattedBalances = await Promise.all(
balances.map(async (balance) => {
const metadata = await metadataCache.getMetadata(balance.coinType);
const amount = Number(balance.totalBalance) / Math.pow(10, metadata.decimals);
return {
symbol: metadata.symbol,
name: metadata.name,
amount: amount.toFixed(4),
icon: metadata.iconUrl,
fiatValue: amount * (await getPriceUSD(balance.coinType)) // External price API
};
})
);
return formattedBalances.sort((a, b) => b.fiatValue - a.fiatValue);
}
2. DEX Interface Token Selection​
async function buildTokenList(supportedCoinTypes) {
const tokens = [];
for (const coinType of supportedCoinTypes) {
try {
const metadata = await getCoinMetadata(coinType);
tokens.push({
address: coinType,
symbol: metadata.symbol,
name: metadata.name,
decimals: metadata.decimals,
logoURI: metadata.iconUrl,
tags: inferTokenTags(metadata) // Custom logic
});
} catch (error) {
console.warn(`Failed to load metadata for ${coinType}:`, error);
}
}
return tokens.sort((a, b) => a.symbol.localeCompare(b.symbol));
}
function inferTokenTags(metadata) {
const tags = [];
if (metadata.symbol === 'SUI') tags.push('native');
if (['USDC', 'USDT', 'DAI'].includes(metadata.symbol)) tags.push('stablecoin');
if (metadata.description?.toLowerCase().includes('test')) tags.push('testnet');
return tags;
}
3. Cross-chain Bridge Interface​
async function prepareBridgeTransaction(fromCoinType, toCoinType, amount) {
const [fromMetadata, toMetadata] = await Promise.all([
getCoinMetadata(fromCoinType),
getCoinMetadata(toCoinType)
]);
// Normalize amounts for comparison
const fromAmount = Number(amount) / Math.pow(10, fromMetadata.decimals);
const expectedToAmount = fromAmount * await getExchangeRate(fromCoinType, toCoinType);
const toAmountRaw = Math.floor(expectedToAmount * Math.pow(10, toMetadata.decimals));
return {
fromToken: {
symbol: fromMetadata.symbol,
amount: fromAmount,
decimals: fromMetadata.decimals
},
toToken: {
symbol: toMetadata.symbol,
expectedAmount: expectedToAmount,
expectedAmountRaw: toAmountRaw.toString(),
decimals: toMetadata.decimals
},
bridgeFee: await calculateBridgeFee(fromCoinType, toCoinType, amount)
};
}
4. Analytics and Reporting​
async function generateTokenReport(coinTypes) {
const metadataList = await Promise.all(
coinTypes.map(async (coinType) => {
try {
return await getCoinMetadata(coinType);
} catch {
return null;
}
})
);
const validMetadata = metadataList.filter(Boolean);
const report = {
totalTokens: validMetadata.length,
byDecimals: {},
bySymbolLength: {},
hasIcons: validMetadata.filter(m => m.iconUrl).length,
commonSymbols: {},
longestName: '',
shortestName: ''
};
validMetadata.forEach(metadata => {
// Group by decimals
report.byDecimals[metadata.decimals] = (report.byDecimals[metadata.decimals] || 0) + 1;
// Group by symbol length
const symbolLen = metadata.symbol.length;
report.bySymbolLength[symbolLen] = (report.bySymbolLength[symbolLen] || 0) + 1;
// Track common symbols
report.commonSymbols[metadata.symbol] = (report.commonSymbols[metadata.symbol] || 0) + 1;
// Track name lengths
if (metadata.name.length > report.longestName.length) {
report.longestName = metadata.name;
}
if (!report.shortestName || metadata.name.length < report.shortestName.length) {
report.shortestName = metadata.name;
}
});
return report;
}
Best Practices​
1. Caching Strategy​
// Implement aggressive caching for metadata as it rarely changes
const metadataCache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
async function getCachedMetadata(coinType) {
const cached = metadataCache.get(coinType);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.metadata;
}
const metadata = await client.getCoinMetadata({ coinType });
metadataCache.set(coinType, {
metadata,
timestamp: Date.now()
});
return metadata;
}
2. Error Handling​
async function safeFormatBalance(coinType, balance) {
try {
const metadata = await getCoinMetadata(coinType);
const formatted = Number(balance) / Math.pow(10, metadata.decimals);
return {
success: true,
formatted: formatted,
display: `${formatted.toFixed(4)} ${metadata.symbol}`,
metadata: metadata
};
} catch (error) {
// Fallback for unknown tokens
return {
success: false,
formatted: Number(balance),
display: `${balance} UNKNOWN`,
error: error.message
};
}
}
3. Batch Loading​
// Load metadata for multiple tokens efficiently
async function preloadMetadata(coinTypes) {
const batchSize = 10;
const results = [];
for (let i = 0; i < coinTypes.length; i += batchSize) {
const batch = coinTypes.slice(i, i + batchSize);
const batchResults = await Promise.allSettled(
batch.map(coinType => getCoinMetadata(coinType))
);
results.push(...batchResults);
// Small delay to prevent overwhelming the RPC
if (i + batchSize < coinTypes.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
Error Handling​
async function handleMetadataErrors(coinType) {
try {
const metadata = await client.getCoinMetadata({ coinType });
return { success: true, data: metadata };
} catch (error) {
if (error.message.includes('not found')) {
return {
success: false,
error: 'Coin type not found',
fallback: {
symbol: 'UNKNOWN',
name: 'Unknown Token',
decimals: 0
}
};
}
return {
success: false,
error: 'Network error',
retry: true
};
}
}
Related Methods​
- suix_getBalance - Query coin balances
- suix_getTotalSupply - Get total coin supply
- suix_getCoins - Get coin objects
Notes​
- Metadata is immutable once set for a coin type
- Cache aggressively as metadata rarely changes
- Handle missing metadata gracefully for unknown tokens
- Use proper decimal precision to avoid floating point errors
- Icon URLs may be external and should be validated before use
Need help? Contact our support team or check the Sui documentation.