Skip to main content

GetCoinInfo

Query Coin Type Metadata#

The GetCoinInfo method retrieves comprehensive metadata for a specific coin type on Sui, including the token's name, symbol, decimals, description, and icon URI. This information is essential for displaying tokens correctly in wallets, exchanges, and DeFi applications.

Method Signature#

Service: sui.rpc.v2beta2.LiveDataService Method: GetCoinInfo Type: Unary RPC

Parameters#

ParameterTypeRequiredDescription
coin_typestringYesFull coin type identifier (e.g., 0x2::sui::SUI)

Common Coin Types#

CoinType Identifier
SUI0x2::sui::SUI
USDC0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN
USDT0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN

Response Structure#

message CoinMetadata {
uint32 decimals = 1;
string name = 2;
string symbol = 3;
string description = 4;
string icon_url = 5;
optional string supply = 6;
}

Field Descriptions#

  • decimals: Number of decimal places (e.g., 9 for SUI, 6 for USDC)
  • name: Full token name (e.g., "Sui")
  • symbol: Trading symbol (e.g., "SUI")
  • description: Token description from metadata
  • icon_url: URI for token icon/logo
  • supply: Total supply if available (optional)

Code Examples#

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

const ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com';
const API_TOKEN = 'your_api_token_here';

const packageDefinition = protoLoader.loadSync('./protos/livedata.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ['./protos']
});

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const credentials = grpc.credentials.createSsl();
const client = new protoDescriptor.sui.rpc.v2beta2.LiveDataService(ENDPOINT, credentials);

const metadata = new grpc.Metadata();
metadata.add('x-api-key', API_TOKEN);

async function getCoinInfo(coinType: string): Promise<any> {
return new Promise((resolve, reject) => {
const request = { coin_type: coinType };

client.GetCoinInfo(request, metadata, (error: any, response: any) => {
if (error) {
console.error('GetCoinInfo error:', error.message);
reject(error);
return;
}

resolve(response);
});
});
}

// Usage
const suiInfo = await getCoinInfo('0x2::sui::SUI');

console.log('Name:', suiInfo.name);
console.log('Symbol:', suiInfo.symbol);
console.log('Decimals:', suiInfo.decimals);
console.log('Icon:', suiInfo.icon_url);

Use Cases#

Token Display in Wallet#

interface TokenDisplay {
name: string;
symbol: string;
iconUrl: string;
formattedBalance: string;
}

async function formatTokenForDisplay(
coinType: string,
rawBalance: string
): Promise<TokenDisplay> {
const info = await getCoinInfo(coinType);

// Convert raw balance to human-readable format
const balance = Number(rawBalance) / Math.pow(10, info.decimals);

return {
name: info.name,
symbol: info.symbol,
iconUrl: info.icon_url,
formattedBalance: balance.toFixed(info.decimals)
};
}

// Usage
const display = await formatTokenForDisplay(
'0x2::sui::SUI',
'5000000000' // 5 SUI in MIST
);
console.log(`${display.formattedBalance} ${display.symbol}`); // "5.000000000 SUI"

Multi-Token Portfolio Display#

interface PortfolioToken {
coinType: string;
balance: string;
metadata: any;
}

async function enrichPortfolio(
tokens: PortfolioToken[]
): Promise<any[]> {
// Fetch all coin metadata in parallel
const metadataPromises = tokens.map(token =>
getCoinInfo(token.coinType)
.catch(err => {
console.warn(`Failed to get info for ${token.coinType}:`, err.message);
return null;
})
);

const metadataList = await Promise.all(metadataPromises);

return tokens.map((token, index) => {
const metadata = metadataList[index];
if (!metadata) {
return {
...token,
name: 'Unknown Token',
symbol: '???',
decimals: 0
};
}

const balance = Number(token.balance) / Math.pow(10, metadata.decimals);

return {
coinType: token.coinType,
name: metadata.name,
symbol: metadata.symbol,
iconUrl: metadata.icon_url,
rawBalance: token.balance,
formattedBalance: balance.toFixed(metadata.decimals),
decimals: metadata.decimals
};
});
}

Token Validation#

async function validateTokenContract(coinType: string): Promise<boolean> {
try {
const info = await getCoinInfo(coinType);

// Basic validation checks
if (!info.name || !info.symbol) {
console.warn('Invalid token: missing name or symbol');
return false;
}

if (info.decimals < 0 || info.decimals > 18) {
console.warn('Invalid token: decimals out of range');
return false;
}

return true;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}

Caching Strategy#

class CoinInfoCache {
private cache = new Map<string, any>();
private ttl = 3600000; // 1 hour

async get(coinType: string): Promise<any> {
const cached = this.cache.get(coinType);

if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}

const info = await getCoinInfo(coinType);

this.cache.set(coinType, {
data: info,
timestamp: Date.now()
});

return info;
}

clear(): void {
this.cache.clear();
}
}

// Usage
const cache = new CoinInfoCache();
const info = await cache.get('0x2::sui::SUI'); // Fetches from network
const info2 = await cache.get('0x2::sui::SUI'); // Returns cached value

Best Practices#

Metadata Caching#

Coin metadata rarely changes, making it ideal for aggressive caching:

// Cache for entire application lifecycle
const METADATA_CACHE = new Map<string, any>();

async function getCachedCoinInfo(coinType: string): Promise<any> {
if (METADATA_CACHE.has(coinType)) {
return METADATA_CACHE.get(coinType);
}

const info = await getCoinInfo(coinType);
METADATA_CACHE.set(coinType, info);

return info;
}

Error Handling#

async function getCoinInfoSafe(coinType: string): Promise<any | null> {
try {
return await getCoinInfo(coinType);
} catch (error: any) {
if (error.code === grpc.status.NOT_FOUND) {
console.warn(`Coin type not found: ${coinType}`);
return null;
}

if (error.code === grpc.status.INVALID_ARGUMENT) {
console.error(`Invalid coin type format: ${coinType}`);
return null;
}

// Re-throw unexpected errors
throw error;
}
}

Decimal Conversion Utility#

function convertToHumanReadable(
rawAmount: string | bigint,
decimals: number
): string {
const amount = BigInt(rawAmount);
const divisor = BigInt(10 ** decimals);

const whole = amount / divisor;
const fraction = amount % divisor;

const fractionStr = fraction.toString().padStart(decimals, '0');

return `${whole}.${fractionStr}`;
}

// Usage with coin info
const info = await getCoinInfo('0x2::sui::SUI');
const readable = convertToHumanReadable('5000000000', info.decimals);
console.log(readable); // "5.000000000"

Performance Characteristics#

MetricValue
Typical Latency10-20ms
Response Size~200-500 bytes
Cache RecommendedYes (long TTL)
Rate Limit ImpactVery low

Common Errors#

Error CodeScenarioSolution
NOT_FOUNDCoin type doesn't existVerify coin type address
INVALID_ARGUMENTMalformed coin typeCheck type format: package::module::type
UNAUTHENTICATEDMissing/invalid tokenVerify x-api-key header

Need help? Contact support@dwellir.com or check the gRPC overview.