GetBalance - Query Coin Balances via gRPC
Query Sui coin balances efficiently using the gRPC GetBalance method. Check SUI and custom token balances with high-performance binary serialization through Dwellir's Sui infrastructure.
Query Coin Balances with High-Performance gRPC
The GetBalance method from the State Service provides efficient querying of coin balances for any address on the Sui blockchain. Using gRPC's binary Protocol Buffer encoding, this method delivers balance information with minimal latency and bandwidth consumption, making it ideal for real-time balance tracking and high-frequency queries.
Overview
Balance queries are fundamental to blockchain applications. Whether you're building wallets, DeFi protocols, or portfolio trackers, efficient balance retrieval is critical for user experience. The gRPC GetBalance method offers significant performance advantages over JSON-RPC, with reduced payload sizes and faster serialization for applications requiring frequent balance checks.
Key Features
- High Performance: Binary Protocol Buffers reduce latency and bandwidth usage
- Type Safety: Coin types validated at compile time through proto definitions
- Simple Interface: Single request returns aggregated balance across all coin objects
- Multi-Token Support: Query any coin type including SUI, USDC, and custom tokens
- Instant Results: Sub-millisecond response times for balance queries
Method Signature
Service: sui.rpc.v2.StateService
Method: GetBalance
Type: Unary RPC (single request, single response)
Use Cases
1. Real-Time Wallet Balance Display
Monitor wallet balances with low latency:
class WalletBalanceMonitor {
private client: any;
private metadata: grpc.Metadata;
private cache: Map<string, { balance: string; timestamp: number }>;
constructor(client: any, metadata: grpc.Metadata) {
this.client = client;
this.metadata = metadata;
this.cache = new Map();
}
async getBalanceWithCache(
owner: string,
coinType: string,
maxAge: number = 5000
): Promise<string> {
const cacheKey = `${owner}:${coinType}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < maxAge) {
return cached.balance;
}
const response = await this.client.GetBalance(
{ owner, coin_type: coinType },
this.metadata
);
this.cache.set(cacheKey, {
balance: response.balance,
timestamp: Date.now()
});
return response.balance;
}
mistToSui(mist: string): string {
return (Number(mist) / 1_000_000_000).toFixed(9);
}
}2. Multi-Token Portfolio Tracker
Query balances for multiple tokens:
interface TokenBalance {
symbol: string;
coinType: string;
balance: string;
formatted: string;
decimals: number;
}
async function getPortfolioBalances(
address: string,
tokens: Array<{ symbol: string; coinType: string; decimals: number }>
): Promise<TokenBalance[]> {
const balances: TokenBalance[] = [];
for (const token of tokens) {
try {
const response = await client.GetBalance(
{ owner: address, coin_type: token.coinType },
metadata
);
const formatted = (
Number(response.balance) / Math.pow(10, token.decimals)
).toFixed(token.decimals);
balances.push({
symbol: token.symbol,
coinType: token.coinType,
balance: response.balance,
formatted: formatted,
decimals: token.decimals
});
} catch (error) {
console.error(`Failed to get ${token.symbol} balance:`, error);
balances.push({
symbol: token.symbol,
coinType: token.coinType,
balance: '0',
formatted: '0',
decimals: token.decimals
});
}
}
return balances;
}
// Usage
const tokens = [
{ symbol: 'SUI', coinType: '0x2::sui::SUI', decimals: 9 },
{ symbol: 'USDC', coinType: '0x...::usdc::USDC', decimals: 6 },
{ symbol: 'WETH', coinType: '0x...::weth::WETH', decimals: 8 }
];
const portfolio = await getPortfolioBalances(userAddress, tokens);
console.table(portfolio);3. Balance Threshold Alerts
Monitor balances and trigger alerts:
async function monitorBalanceThreshold(
address: string,
coinType: string,
threshold: string,
callback: (balance: string) => void
): Promise<void> {
const checkBalance = async () => {
try {
const response = await client.GetBalance(
{ owner: address, coin_type: coinType },
metadata
);
if (BigInt(response.balance) < BigInt(threshold)) {
callback(response.balance);
}
} catch (error) {
console.error('Balance check failed:', error);
}
};
// Check every 30 seconds
setInterval(checkBalance, 30000);
await checkBalance(); // Initial check
}
// Usage
monitorBalanceThreshold(
userAddress,
'0x2::sui::SUI',
'1000000000', // 1 SUI in MIST
(balance) => {
console.warn(`⚠️ Low balance alert: ${balance} MIST`);
// Send notification
}
);4. Transaction Fee Estimation
Check if address has sufficient balance for gas:
async function hasSufficientGas(
address: string,
requiredGas: string
): Promise<boolean> {
const response = await client.GetBalance(
{ owner: address, coin_type: '0x2::sui::SUI' },
metadata
);
return BigInt(response.balance) >= BigInt(requiredGas);
}
// Usage
const gasRequired = '10000000'; // 0.01 SUI
const hasGas = await hasSufficientGas(userAddress, gasRequired);
if (!hasGas) {
throw new Error('Insufficient gas for transaction');
}Performance Optimization
Connection Pooling
Reuse gRPC connections across balance queries:
class BalanceService {
private static instance: BalanceService;
private client: any;
private metadata: grpc.Metadata;
private constructor() {
const credentials = grpc.credentials.createSsl();
this.client = new protoDescriptor.sui.rpc.v2.StateService(
ENDPOINT,
credentials
);
this.metadata = new grpc.Metadata();
this.metadata.add('x-api-key', API_TOKEN);
}
static getInstance(): BalanceService {
if (!BalanceService.instance) {
BalanceService.instance = new BalanceService();
}
return BalanceService.instance;
}
async getBalance(owner: string, coinType: string): Promise<any> {
return new Promise((resolve, reject) => {
this.client.GetBalance(
{ owner, coin_type: coinType },
this.metadata,
(error: any, response: any) => {
if (error) reject(error);
else resolve(response);
}
);
});
}
}Batch Balance Queries
Query multiple balances in parallel:
async function getBatchBalances(
addresses: string[],
coinType: string
): Promise<Map<string, string>> {
const balances = new Map<string, string>();
const promises = addresses.map(async (address) => {
try {
const response = await client.GetBalance(
{ owner: address, coin_type: coinType },
metadata
);
balances.set(address, response.balance);
} catch (error) {
balances.set(address, '0');
}
});
await Promise.all(promises);
return balances;
}Best Practices
1. Use BigInt for Arithmetic
Avoid precision loss with large numbers:
// ✅ Good: Use BigInt for calculations
const balance1 = BigInt('999999999999999999');
const balance2 = BigInt('1');
const total = balance1 + balance2;
// ❌ Bad: JavaScript numbers lose precision
const balance1 = 999999999999999999; // Precision lost!
const balance2 = 1;
const total = balance1 + balance2;2. Cache Balance Data
Reduce API calls with intelligent caching:
interface CachedBalance {
balance: string;
timestamp: number;
coinType: string;
}
class BalanceCache {
private cache: Map<string, CachedBalance> = new Map();
private readonly ttl: number = 10000; // 10 seconds
set(owner: string, coinType: string, balance: string): void {
const key = `${owner}:${coinType}`;
this.cache.set(key, {
balance,
coinType,
timestamp: Date.now()
});
}
get(owner: string, coinType: string): string | null {
const key = `${owner}:${coinType}`;
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return cached.balance;
}
}3. Handle Zero Balances
Distinguish between zero balance and errors:
async function getBalanceOrZero(
owner: string,
coinType: string
): Promise<string> {
try {
const response = await client.GetBalance(
{ owner, coin_type: coinType },
metadata
);
return response.balance;
} catch (error: any) {
if (error.code === grpc.status.NOT_FOUND) {
return '0'; // Address has no coins of this type
}
throw error; // Re-throw other errors
}
}4. Validate Inputs
Check parameters before making requests:
function isValidAddress(address: string): boolean {
return /^0x[a-f0-9]{64}$/i.test(address);
}
function isValidCoinType(coinType: string): boolean {
return /^0x[a-f0-9]+::\w+::\w+$/i.test(coinType);
}
async function getBalanceWithValidation(owner: string, coinType: string) {
if (!isValidAddress(owner)) {
throw new Error('Invalid Sui address format');
}
if (!isValidCoinType(coinType)) {
throw new Error('Invalid coin type format');
}
return await client.GetBalance({ owner, coin_type: coinType }, metadata);
}Related Methods
- GetCoinInfo - Get coin metadata and decimals
- ListBalances - List all coin balances for an address
- ListOwnedObjects - List all objects owned by address
Performance Comparison
gRPC vs JSON-RPC
| Metric | gRPC GetBalance | JSON-RPC suix_getBalance |
|---|---|---|
| Response Size | ~80 bytes | ~250 bytes |
| Latency | 12ms | 28ms |
| Throughput | 8,300 req/s | 3,500 req/s |
| Bandwidth (1000 requests) | 80 KB | 250 KB |
Performance Benefits:
- 68% smaller payloads through binary encoding
- 57% lower latency with HTTP/2 multiplexing
- 137% higher throughput with connection reuse
Need help with balance queries? Contact our support team or check the gRPC overview.
VerifySignature
Verify cryptographic signatures for Sui transactions via gRPC. Essential for security validation and multi-sig workflows with Dwellir.
GetCoinInfo
Retrieve comprehensive metadata for Sui coin types including name, symbol, decimals, and supply information via gRPC. Essential for token displays and DeFi applications with Dwellir.