Decimais e Valores
Tokens SPL armazenam valores inteiros brutos on-chain. Decimais na mint definem como exibir valores legíveis por humanos. Erros de um por um em decimais estão entre os bugs mais caros em integrações de tokens.
Busque em todas as páginas da documentação
Tokens SPL armazenam valores inteiros brutos on-chain. Decimais na mint definem como exibir valores legíveis por humanos. Erros de um por um em decimais estão entre os bugs mais caros em integrações de tokens.
const UI = 1.5;
const raw = BigInt(Math.round(UI * 10 ** decimals));
const display = Number(raw) / 10 ** decimals;pub fn to_raw(ui: f64, decimals: u8) -> Result<u64> {
let factor = 10u64.pow(decimals as u32);
let raw = (ui * factor as f64).round() as u64;
Ok(raw)
}Quando usar isso:
transfer_checked on-chainimport { createSolanaRpc, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const mint = address("MINT_PUBKEY");
const { value } = await rpc.getAccountInfo(mint, { encoding: "jsonParsed" }).send();
const decimals = value?.data.parsed.info.decimals as number;
function toRawAmount(uiAmount: string, decimals: number): bigint {
const [whole, frac = ""] = uiAmount.split(".");
const padded = (frac + "0".repeat(decimals)).slice(0, decimals);
return BigInt(whole + padded);
}
function toUiAmount(raw: bigint, decimals: number): string {
const s = raw.toString().padStart(decimals + 1, "0");
const whole = s.slice(0, -decimals) || "0";
const frac = s.slice(-decimals).replace(/0+$/, "");
return frac ? `${whole}.${frac}` : whole;
}
const raw = toRawAmount("2.5", decimals);
console.log("raw:", raw.toString());
console.log("ui:", toUiAmount(raw, decimals));use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, TransferChecked, Mint};
#[derive(Accounts)]
pub struct SafeTransfer<'info> {
pub mint: Account<'info, Mint>,
#[account(mut)]
pub from: Account<'info, TokenAccount>,
#[account(mut)]
pub to: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn safe_transfer(ctx: Context<SafeTransfer>, amount: u64) -> Result<()> {
let decimals = ctx.accounts.mint.decimals;
let cpi = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
TransferChecked {
mint: ctx.accounts.mint.to_account_info(),
from: ctx.accounts.from.to_account_info(),
to: ctx.accounts.to.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
);
token::transfer_checked(cpi, amount, decimals)?;
Ok(())
}O que isso demonstra:
jsonParsed expõe tokenAmount.amount, decimals e uiAmounttransfer_checked vincula o valor aos decimais esperados nos programasdecimals: u8 da mint é imutável após a criação (0-9 típico; até 255 permitido)amount: u64 da conta de token são sempre unidades base brutastransfer_checked falha se os decimais da instrução != decimais da mint| Estilo do Ativo | Decimais | 1.0 token bruto |
|---|---|---|
| Semelhante a USDC | 6 | 1_000_000 |
| SOL-Wrapped | 9 | 1_000_000_000 |
| NFT de número inteiro | 0 | 1 |
use anchor_lang::solana_program::program_error::ProgramError;
pub fn mul_scaled(a: u64, b: u64) -> Result<u64, ProgramError> {
a.checked_mul(b).ok_or(ProgramError::InvalidArgument)
}1.1 * 1e6. Correção: conversão de string/BigInt como toRawAmount.transfer_checked em programas - ataques de confusão de decimais entre ativos incompatíveis. Correção: use transfer_checked com os decimais da mint.u64 em matemática off-chain - suprimentos enormes excedem o inteiro seguro do JS. Correção: use BigInt de ponta a ponta.spl-token - os argumentos de quantidade da CLI são valores de UI, então um valor "bruto" gasta mais do que o esperado por 10^decimals. Correção: passe 2.5, não 2500000; reserve unidades brutas para dados de instrução.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
uiAmount do RPC | Exibição rápida | Construir instruções (use string amount) |
| Constantes de decimal fixo | Aplicativo de ativo único | Portfólio multi-token |
Bibliotecas de decimal (decimal.js) | Precisão financeira | Carteira simples com BigInt suficiente |
Apenas na conta mint. Contas de token armazenam o valor bruto.
Não. Imutável na inicialização da mint.
Inteiro armazenado on-chain - o único valor que as instruções usam.
Convenção da indústria que corresponde à precisão da moeda fiduciária do emissor.
Transferência que inclui os decimais esperados - previne certos erros entre ativos em programas.
Sim. Comum para NFTs (suprimento 1, quantidade 1).
O oposto da codificação de instrução: os argumentos de quantidade do spl-token são valores de UI, e a CLI os escala pelos decimais da mint para você. spl-token transfer <MINT> 2.5 <RECIPIENT> envia 2.5 tokens; passar 2500000 enviaria 2.500.000 tokens. Unidades base brutas aparecem apenas nos dados da instrução (e na saída de spl-token display).
Mesmo campo de decimais no nível da mint e semântica de valor bruto.
~1.8e19 unidades brutas - combine com decimais para planejamento realista do limite da UI.
Decisão do produto - sempre documente. Pagamentos geralmente usam o piso; exibições podem arredondar.
Frequentemente brutos ou de ponto fixo com decimais explícitos no layout da conta - leia a documentação do protocolo.
Use BigInt para valores brutos na construção de transações do kit 7.0.0 quando os valores excederem Number.MAX_SAFE_INTEGER.
Versões do Stack: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0 e LiteSVM 0.6.x.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026