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#
| Parameter | Type | Required | Description |
|---|---|---|---|
coin_type | string | Yes | Full coin type identifier (e.g., 0x2::sui::SUI) |
Common Coin Types#
| Coin | Type Identifier |
|---|---|
| SUI | 0x2::sui::SUI |
| USDC | 0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN |
| USDT | 0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::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#
- TypeScript
- Python
- Go
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);
import grpc
import livedata_service_pb2
import livedata_service_pb2_grpc
ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com'
API_TOKEN = 'your_api_token_here'
def get_coin_info(coin_type: str):
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel(ENDPOINT, credentials)
client = livedata_service_pb2_grpc.LiveDataServiceStub(channel)
request = livedata_service_pb2.GetCoinInfoRequest(
coin_type=coin_type
)
metadata = [('x-api-key', API_TOKEN)]
response = client.GetCoinInfo(request, metadata=metadata)
print(f'Name: {response.name}')
print(f'Symbol: {response.symbol}')
print(f'Decimals: {response.decimals}')
print(f'Description: {response.description}')
channel.close()
return response
# Usage
sui_info = get_coin_info('0x2::sui::SUI')
package main
import (
"context"
"fmt"
"log"
"time"
"sui-grpc-client/config"
pb "sui-grpc-client/sui/rpc/v2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)
func main() {
// Load configuration from .env file
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Create TLS credentials
creds := credentials.NewClientTLSFromCert(nil, "")
// Connect to Dwellir
conn, err := grpc.NewClient(
cfg.Endpoint,
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
// Create state service client
client := pb.NewStateServiceClient(conn)
// Add authentication
ctx := metadata.AppendToOutgoingContext(
context.Background(),
"x-api-key", cfg.APIKey,
)
// Set timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// SUI coin type: 0x2::sui::SUI
coinType := "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI"
request := &pb.GetCoinInfoRequest{
CoinType: &coinType,
}
response, err := client.GetCoinInfo(ctx, request)
if err != nil {
log.Fatalf("Failed to get coin info: %v", err)
}
// Display coin information
fmt.Println("Coin Information:")
fmt.Println("=================")
fmt.Printf("Coin Type: %s\n", response.GetCoinType())
// Display metadata if available
if metadata := response.GetMetadata(); metadata != nil {
fmt.Println("\nMetadata:")
fmt.Printf(" ID: %s\n", metadata.GetId())
fmt.Printf(" Decimals: %d\n", metadata.GetDecimals())
fmt.Printf(" Name: %s\n", metadata.GetName())
fmt.Printf(" Symbol: %s\n", metadata.GetSymbol())
fmt.Printf(" Description: %s\n", metadata.GetDescription())
if iconUrl := metadata.GetIconUrl(); iconUrl != "" {
fmt.Printf(" Icon URL: %s\n", iconUrl)
}
}
// Display treasury information if available
if treasury := response.GetTreasury(); treasury != nil {
fmt.Println("\nTreasury:")
fmt.Printf(" ID: %s\n", treasury.GetId())
fmt.Printf(" Total Supply: %d\n", treasury.GetTotalSupply())
fmt.Printf(" Supply State: %s\n", treasury.GetSupplyState())
}
// Display regulated metadata if available
if regulated := response.GetRegulatedMetadata(); regulated != nil {
fmt.Println("\nRegulated Metadata:")
fmt.Printf(" Regulation State: %s\n", regulated.GetCoinRegulatedState())
if id := regulated.GetId(); id != "" {
fmt.Printf(" ID: %s\n", id)
}
fmt.Printf(" Allow Global Pause: %v\n", regulated.GetAllowGlobalPause())
}
}
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#
| Metric | Value |
|---|---|
| Typical Latency | 10-20ms |
| Response Size | ~200-500 bytes |
| Cache Recommended | Yes (long TTL) |
| Rate Limit Impact | Very low |
Common Errors#
| Error Code | Scenario | Solution |
|---|---|---|
NOT_FOUND | Coin type doesn't exist | Verify coin type address |
INVALID_ARGUMENT | Malformed coin type | Check type format: package::module::type |
UNAUTHENTICATED | Missing/invalid token | Verify x-api-key header |
Related Methods#
- GetBalance - Query coin balances
- ListBalances - List all balances for an address
Need help? Contact support@dwellir.com or check the gRPC overview.