Solana getTokenAccountBalance devuelve cuatro vistas de un mismo saldo de token SPL: amount (cadena de entero sin procesar en unidades base), decimals (los decimales del mint), uiAmount (un número JSON escalado por los decimales) y uiAmountString (el mismo valor escalado como una cadena decimal exacta). El saldo legible para humanos es amount dividido entre 10^decimals. Como los números JSON son de punto flotante, uiAmount puede perder precisión con importes grandes o tokens con muchos decimales, por lo que uiAmountString es el campo en el que se debe confiar para el dinero. Esta guía explica el modelo de cuenta de token, muestra ejemplos ejecutables en Node.js que derivan una cuenta de token asociada y recalculan el importe humano, y proporciona una tabla de resultados para verificar el comportamiento contra tu propio endpoint RPC.
El modelo de cuenta de token SPL detrás de getTokenAccountBalance
En Solana, un saldo de token SPL no vive en una billetera. Vive en una cuenta de token: una cuenta separada en la cadena que contiene un importe entero sin procesar en unidades base y una referencia al mint que define el token. El mint es la autoridad para los decimales, el número de unidades base que componen un token completo. Por lo tanto, el saldo legible para humanos es amount / 10^decimals, una división que debes realizar tú mismo o dejar que el RPC haga por ti. La documentación de tokens de Solana describe esta relación mint-decimales-unidades base y la derivación de la cuenta de token asociada (ATA) que vincula a un propietario y un mint con una dirección de cuenta de token determinista.
Esta separación importa porque getTokenAccountBalance toma una única dirección de cuenta de token, no una dirección de billetera. Una dirección de billetera es una cuenta del sistema que contiene lamports; una cuenta de token es un tipo de cuenta diferente que contiene un importe de token SPL. Pasar una dirección de billetera a getTokenAccountBalance es un error común que produce un error o una cuenta inesperada. Para leer el saldo de una billetera para un mint específico, primero deriva la ATA a partir del propietario y el mint, y luego consulta esa cuenta de token. Para un tratamiento más amplio de los tipos de cuenta y el alquiler, consulta Lectura de cuentas de Solana y saldos de tokens.
- Cuenta de token: contiene un importe entero sin procesar en unidades base más una referencia al mint.
- Mint: define los decimales, el número de unidades base por token completo.
- Saldo legible para humanos: amount / 10^decimals.
- ATA: cuenta de token determinista derivada de propietario + mint.
El contrato de respuesta de getTokenAccountBalance
La referencia de Solana getTokenAccountBalance documenta el método como aquel que toma una única dirección de cuenta de token y devuelve un objeto de valor con amount, decimals, uiAmount y uiAmountString, envuelto en el sobre de respuesta estándar JSON-RPC 2.0 con un slot de contexto. La especificación JSON-RPC 2.0 define ese sobre: una versión jsonrpc, un id y un result o un error. El slot de contexto te indica qué slot del ledger usó el nodo para responder, lo que te permite razonar sobre la frescura.
Los cuatro campos son cuatro vistas de un mismo saldo. amount es el entero sin procesar como cadena, en unidades base. decimals son los decimales del mint. uiAmount es un número JSON igual a amount escalado por los decimales. uiAmountString es el mismo valor escalado expresado como una cadena decimal. El RPC devuelve amount como cadena porque los importes de token sin procesar pueden superar el rango de enteros seguros de muchos lenguajes; devuelve uiAmountString por la misma razón. El método lee exactamente una cuenta de token, por lo que no es una consulta de cartera.
- amount: cadena de entero sin procesar, unidades base.
- decimals: decimales del mint.
- uiAmount: número JSON, amount escalado por los decimales.
- uiAmountString: cadena decimal exacta, amount escalado por los decimales.
- context.slot: slot del ledger usado para responder.
Por qué uiAmountString es el campo en el que confiar para el dinero
Los números JSON son de punto flotante en la mayoría de los entornos de ejecución, incluido JavaScript. Un valor como 1234567.89 no siempre se puede representar exactamente, y el problema crece con importes grandes o tokens con muchos decimales. uiAmount es un número JSON, por lo que puede perder precisión en tránsito o en tu analizador. uiAmountString es una cadena decimal, por lo que conserva todos los dígitos que produjo el nodo. Para contabilidad, conciliación o cualquier comparación que deba ser exacta, analiza uiAmountString como una cadena decimal o un tipo de número grande en lugar de confiar en uiAmount.
Esto no es una peculiaridad específica de Solana; es una propiedad de los números JSON. El patrón seguro es tratar amount y uiAmountString como los campos autoritativos y tratar uiAmount como una conveniencia solo para visualización. Si debes usar uiAmount, redondéalo deliberadamente y nunca lo uses como clave ni en una comprobación de igualdad. La referencia de Solana getTokenAccountBalance documenta ambos campos, así que la elección es tuya, pero la garantía de precisión pertenece a la cadena.
- uiAmount es un número JSON y puede perder precisión.
- uiAmountString conserva los dígitos exactos.
- Usa amount + decimals para matemáticas exactas.
- Usa uiAmount solo para visualización.
Derivar la cuenta de token asociada antes de consultar
Como getTokenAccountBalance espera una cuenta de token, el primer paso para una consulta de billetera y mint es derivar la ATA. La ATA es una dirección derivada de programa calculada a partir del propietario, el programa de tokens y el mint. La documentación de tokens de Solana cubre esta derivación. Si la ATA aún no existe, la consulta fallará o devolverá un resultado vacío; es posible que necesites crearla o manejar el caso de cuenta faltante. Para billeteras con muchas cuentas de token, enumerarlas es una tarea separada cubierta en Paginación de getTokenAccountsByOwner para billeteras grandes.
Un punto sutil: una billetera puede tener múltiples cuentas de token para el mismo mint si se crearon fuera de la convención ATA. La ATA es la canónica, pero no es la única posible. Si tu saldo parece incorrecto, confirma que estás consultando la cuenta de token que crees. La Guía de la API de Solana (RPC Assistant) es un complemento útil para preguntas a nivel de endpoint.
- Deriva la ATA a partir de propietario + mint antes de consultar.
- Maneja explícitamente el caso de cuenta faltante.
- Una billetera puede tener más de una cuenta de token por mint.
- Confirma la dirección de la cuenta de token antes de confiar en el saldo.
Ejemplo ejecutable en Node.js con @solana/web3.js
El ejemplo siguiente deriva la ATA para un propietario y un mint, llama a getTokenAccountBalance, imprime los cuatro campos y recalcula el importe humano a partir de amount y decimals para mostrar que los valores coinciden. Usa @solana/web3.js y un marcador de posición de endpoint RPC público. Reemplaza el endpoint y los valores de propietario/mint por los tuyos. El recálculo usa un enfoque seguro para enteros grandes, por lo que no hereda el problema del float.
Ejecútalo con Node.js después de instalar @solana/web3.js. La salida debería mostrar amount, decimals, uiAmount, uiAmountString y un importe recalculado que coincida con uiAmountString. Si la ATA no existe, la llamada lanza una excepción; captura ese caso y repórtalo en lugar de asumir un saldo cero.
// npm install @solana/web3.js
const { Connection, PublicKey, getAssociatedTokenAddress } = require('@solana/web3.js');
async function main() {
const endpoint = 'https://api.mainnet-beta.solana.com';
const connection = new Connection(endpoint, 'confirmed');
const owner = new PublicKey('REPLACE_WITH_OWNER_WALLET');
const mint = new PublicKey('REPLACE_WITH_MINT');
const ata = await getAssociatedTokenAddress(mint, owner);
console.log('ATA:', ata.toBase58());
try {
const res = await connection.getTokenAccountBalance(ata);
const { amount, decimals, uiAmount, uiAmountString } = res.value;
console.log('amount:', amount);
console.log('decimals:', decimals);
console.log('uiAmount:', uiAmount);
console.log('uiAmountString:', uiAmountString);
// Recompute human amount from raw amount and decimals without floats.
const raw = BigInt(amount);
const scale = BigInt(10) ** BigInt(decimals);
const whole = raw / scale;
const frac = raw % scale;
const fracStr = frac.toString().padStart(decimals, '0').replace(/0+$/, '');
const recomputed = fracStr.length ? `${whole}.${fracStr}` : whole.toString();
console.log('recomputed:', recomputed);
console.log('match:', recomputed === uiAmountString);
} catch (err) {
console.error('Query failed (missing ATA or bad account?):', err.message);
}
}
main();Ejemplo de JSON-RPC sin procesar con fetch
Si prefieres no agregar una dependencia, la misma consulta funciona con JSON-RPC sin procesar. El cuerpo de la solicitud sigue la especificación JSON-RPC 2.0: una versión jsonrpc, un id, un método y params. El método es getTokenAccountBalance y el único parámetro es la dirección de la cuenta de token. La respuesta contiene los mismos cuatro campos más el slot de contexto. Este ejemplo usa fetch e imprime el valor analizado.
Usa esta forma cuando quieras ver la respuesta exacta en el cable, incluido el slot de contexto y cualquier objeto de error. También es la forma más sencilla de probar un endpoint RPC específico antes de integrarlo en una aplicación. Reemplaza el endpoint y la dirección de la cuenta de token por los tuyos.
// Node.js 18+ (global fetch)
async function getBalance(tokenAccount) {
const endpoint = 'https://api.mainnet-beta.solana.com';
const body = {
jsonrpc: '2.0',
id: 1,
method: 'getTokenAccountBalance',
params: [tokenAccount]
};
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const json = await res.json();
if (json.error) {
console.error('RPC error:', json.error);
return;
}
const { amount, decimals, uiAmount, uiAmountString } = json.result.value;
console.log('slot:', json.result.context.slot);
console.log('amount:', amount);
console.log('decimals:', decimals);
console.log('uiAmount:', uiAmount);
console.log('uiAmountString:', uiAmountString);
}
getBalance('REPLACE_WITH_TOKEN_ACCOUNT');Tabla de resultados para verificar tu propio endpoint
Como el comportamiento del proveedor y las versiones del nodo pueden variar, verifica el contrato de respuesta contra tu propio endpoint en lugar de confiar en un solo ejemplo. Completa la tabla a continuación con valores reales de tu proveedor RPC. El importe recalculado debería coincidir exactamente con uiAmountString; si no es así, revisa tu manejo de decimales o tu analizador. Registra el slot de contexto para poder razonar sobre la frescura.
Esta tabla es un método de medición, no un benchmark. No afirma la latencia ni el rendimiento de ningún proveedor. Simplemente te permite confirmar que amount, decimals, uiAmount y uiAmountString son consistentes para las cuentas que te interesan.
- mint: la dirección del mint del token.
- cuenta de token: la cuenta de token que consultaste.
- importe sin procesar: el campo amount tal como se devolvió.
- decimals: el campo decimals tal como se devolvió.
- uiAmount: el número JSON tal como se devolvió.
- uiAmountString: la cadena decimal tal como se devolvió.
- importe recalculado: amount / 10^decimals calculado por ti.
- coincidencia: si el recalculado es igual a uiAmountString.
- slot de contexto: el slot de la respuesta.
getTokenAccountBalance vs getTokenSupply vs getBalance
Estos tres métodos responden a preguntas diferentes y son fáciles de confundir. getBalance devuelve lamports para una cuenta de sistema nativa, no un saldo de token SPL. getTokenAccountBalance devuelve el saldo de una cuenta de token SPL. getTokenSupply devuelve el suministro total de un mint, no el saldo de ningún tenedor individual. Si quieres las tenencias de una billetera en muchos mints, ninguno de estos por sí solo es suficiente; necesitas getTokenAccountsByOwner, cubierto en Paginación de getTokenAccountsByOwner para billeteras grandes.
Una regla práctica: usa getBalance para SOL, getTokenAccountBalance para una cuenta de token específica y getTokenSupply para totales a nivel de mint. Confundirlos es una fuente frecuente de números incorrectos. Para mints de Token-2022, las tarifas de transferencia y los importes retenidos pueden complicar aún más lo que un tenedor controla realmente, lo cual se analiza en Tarifas de transferencia y tarifas retenidas de Token-2022.
- getBalance: lamports para una cuenta nativa.
- getTokenAccountBalance: una cuenta de token SPL.
- getTokenSupply: suministro total de un mint.
- getTokenAccountsByOwner: todas las cuentas de token de una billetera.
Los decimales varían según el mint y deben leerse, nunca asumirse
Los decimales provienen del mint y no son universales. USDC usa 6 decimales, muchos tokens de Solana usan 9 y otros difieren. Asumir 9 para cada token producirá saldos legibles para humanos incorrectos. La única fuente autoritativa es la cuenta del mint, y getTokenAccountBalance convenientemente devuelve los decimales junto con el importe, por lo que no necesitas una segunda llamada en el caso común. Aun así, trata los decimales como datos, no como una constante.
Si almacenas en caché los decimales, invalida la caché cuando cambie el mint o cuando cambies de token. Un valor de decimales obsoleto corrompe silenciosamente cada saldo derivado. La documentación de tokens de Solana es la referencia sobre cómo se almacenan y usan los decimales.
- USDC: 6 decimales.
- Muchos tokens de Solana: 9 decimales.
- Lee siempre los decimales del mint o de la respuesta.
- Invalida los decimales en caché cuando cambie el mint.
Limitaciones y compensaciones
getTokenAccountBalance lee exactamente una cuenta de token. No es un método de cartera y no enumerará las tenencias de una billetera. Para eso, usa getTokenAccountsByOwner. El campo decimals es autoritativo solo cuando refleja el mint; si derivas los decimales en otro lugar, asumes ese riesgo. La precisión de uiAmount no está garantizada porque es un número JSON, por lo que uiAmountString o amount más decimals es la ruta segura.
La frescura es otra compensación. Una transacción recién enviada puede no reflejarse en el nivel de compromiso que consultas. Usa un compromiso adecuado y, si necesitas esperar, sondea o suscríbete en lugar de asumir que la primera lectura es definitiva. Para actualizaciones basadas en suscripción, consulta Codificación de accountSubscribe: base64 vs jsonParsed. Por último, el comportamiento del proveedor para las formas de error y los campos de contexto está documentado, pero puede variar según el proveedor, así que prueba contra el endpoint que realmente usas.
- Lee solo una cuenta de token.
- Decimals es autoritativo solo desde el mint.
- La precisión de uiAmount no está garantizada.
- La frescura depende del compromiso y el momento.
- Las formas de error pueden variar según el proveedor.
Solución de problemas: tipo de cuenta incorrecto, confusión en la interfaz y lecturas obsoletas
Tipo de cuenta incorrecto: si pasas una dirección de billetera, el nodo puede devolver un error o los datos de otra cuenta. Deriva primero la ATA a partir del propietario y el mint, y confirma que la dirección que consultas es una cuenta de token. Confusión en la interfaz: si tu interfaz muestra un número diferente al del RPC, comprueba si la interfaz está usando uiAmount (float) o uiAmountString (exacto), y si aplicó los decimales correctos. Una discrepancia generalmente significa un redondeo de float o una suposición incorrecta de decimales.
Lecturas obsoletas: si falta una transferencia reciente, revisa el slot de contexto y el nivel de compromiso. Una lectura con un compromiso más bajo puede ir con retraso. Vuelve a consultar con un compromiso más alto o espera la confirmación. Si la ATA no existe, la llamada falla en lugar de devolver cero; maneja ese caso explícitamente para que tu interfaz no muestre un saldo engañoso. Para problemas a nivel de endpoint, las páginas Guía de la API de Solana (RPC Assistant) y Servicio de API son referencias útiles.
- Tipo de cuenta incorrecto: deriva y verifica la ATA.
- Confusión en la interfaz: prefiere uiAmountString y decimales correctos.
- Lecturas obsoletas: revisa el slot de contexto y el compromiso.
- ATA faltante: maneja el error, no asumas cero.
Próximos pasos y lecturas relacionadas
Ahora que puedes decodificar getTokenAccountBalance, el siguiente paso natural es enumerar las tenencias de una billetera con getTokenAccountsByOwner, cubierto en Paginación de getTokenAccountsByOwner para billeteras grandes. Si necesitas contexto a nivel de cuenta, como el alquiler y los tipos de cuenta, lee Lectura de cuentas de Solana y saldos de tokens. Para detalles específicos de Token-2022, consulta Tarifas de transferencia y tarifas retenidas de Token-2022.
Para elegir un endpoint y entender el comportamiento a nivel de proveedor, comienza con la página de la red Solana y el centro de aprendizaje de OnFinality. Si estás planificando tráfico de producción, revisa Precios de RPC y la descripción general del Servicio de API. Para preguntas interactivas sobre endpoints, la Guía de la API de Solana (RPC Assistant) es un buen complemento.
- Enumerar tenencias: getTokenAccountsByOwner.
- Contexto de cuenta: getAccountInfo y alquiler.
- Token-2022: tarifas de transferencia y tarifas retenidas.
- Endpoints y precios: /en/networks/solana y /en/pricing/rpc.