Kaspalytics Learn is under (heavy) construction. Check back regularly!
Updated: Aug 4, 2026

Units (KAS & Sompi)

Kaspa's units - sompi, KAS, the mass units used for fees, and the SDK helpers that convert between them.

Sompi

The base unit of Kaspa is the sompi (named for Yonatan Sompolinsky). All protocol-level amounts - transaction outputs, fees, RPC values, SDK APIs - are integers denominated in sompi.

1 KAS = 100,000,000 sompi (10^8)
AmountIn KAS
1 sompi0.00000001 KAS
100 sompi0.000001 KAS
10,000 sompi0.0001 KAS
1,000,000 sompi0.01 KAS
100,000,000 sompi1 KAS
10,000,000,000 sompi100 KAS

“KAS” is purely a display convention - the protocol never deals in fractional values. When writing software against a node or SDK, always work in sompi and convert to KAS only at the UI edge.

Converting

Every SDK ships the same three helpers, built from the same Rust functions: parse KAS into sompi, turn sompi back into KAS, and format sompi as a string with the network’s ticker.

use kaspa_wallet_core::utils::{
    kaspa_to_sompi, sompi_to_kaspa, sompi_to_kaspa_string_with_suffix,
    try_kaspa_str_to_sompi,
};
use kaspa_consensus_core::network::NetworkType;

// Parsing user input: take the string, not a float. See the warning below.
let sompi = try_kaspa_str_to_sompi("8.2")?.unwrap();   // 820_000_000

// Display only.
let kas: f64 = sompi_to_kaspa(499_922_100);            // 4.999221
let label = sompi_to_kaspa_string_with_suffix(499_922_100, &NetworkType::Mainnet);
// "4.999221 KAS"   (testnet gives "TKAS", simnet "SKAS", devnet "DKAS")

// The f64 form exists, and has the precision problem below.
let lossy = kaspa_to_sompi(8.2);                       // 819_999_999
const { kaspaToSompi, sompiToKaspaString, sompiToKaspaStringWithSuffix } = kaspa;

// kaspaToSompi takes a *string* and returns a BigInt. That is deliberate:
// it parses the decimal digits rather than routing them through a float.
const sompi = kaspaToSompi('8.2'); // 820000000n

// Display only.
console.log(sompiToKaspaString(499922100n)); // "4.999221"
console.log(sompiToKaspaStringWithSuffix(499922100n, 'mainnet')); // "4.999221 KAS"
from kaspa import kaspa_to_sompi, sompi_to_kaspa, sompi_to_kaspa_string_with_suffix

# Note the argument is a float here, not a string. See the warning below.
print(kaspa_to_sompi(100.833))  # 10083300000

# Display only.
print(sompi_to_kaspa(499_922_100))  # 4.999221
print(sompi_to_kaspa_string_with_suffix(499_922_100, "mainnet"))  # "4.999221 KAS"
print(sompi_to_kaspa_string_with_suffix(1_234_567_890_123, "mainnet"))
# "12,345.67890123 KAS"   — thousands separators, so this is a display string,
#                           not something to parse back

Never route an amount through a float

8.2 is not representable in binary floating point. Multiply the nearest double by 100,000,000 and truncate, and you land one sompi short:

kaspa_to_sompi(8.2)         -> 819,999,999      (should be 820,000,000)
kaspa_to_sompi(1234567.89)  -> 123,456,788,999,999

That is a real off-by-one in a balance, and it compounds across a batch of outputs. The fix is to parse the decimal string directly, which is what try_kaspa_str_to_sompi (Rust) and kaspaToSompi (WASM) already do — both take a string for exactly this reason.

The Python binding only exposes the f64 form, so parse it yourself when the number came from a user:

from decimal import Decimal

SOMPI_PER_KAS = 100_000_000

def kas_str_to_sompi(text: str) -> int:
    """Exact KAS -> sompi. Use this for anything a person typed."""
    return int(Decimal(text) * SOMPI_PER_KAS)

kas_str_to_sompi("8.2")          # 820000000
kas_str_to_sompi("1234567.89")   # 123456789000000

The same applies coming back the other way. sompi_to_kaspa returns an f64, and above ~90 million KAS (2^53 sompi) a double can no longer hold every sompi — an amount near the 28.7 billion KAS supply cap silently rounds. Format from the integer when it has to be exact:

def sompi_to_kas_str(sompi: int) -> str:
    whole, frac = divmod(sompi, SOMPI_PER_KAS)
    return f"{whole}.{frac:08d}"

sompi_to_kas_str(2_870_000_000_000_000_001)   # "28700000000.00000001"
sompi_to_kaspa(2_870_000_000_000_000_001)     # 28700000000.0  — the last sompi is gone

The rule that falls out of all this: integers on the wire, strings at the edges, floats never.

Mass (grams)

Transaction size/cost is measured in mass, denominated in grams. A transaction is constrained along three mass dimensions - compute mass, storage mass, and transient mass - with blocks capped at 500,000 grams per dimension (the transient dimension was raised to 1,000,000 in the June 2026 Toccata hardfork). See Network Parameters.

Fees

Fees are paid implicitly, UTXO-style: fee = sum(inputs) - sum(outputs), in sompi.

The expected fee for a transaction is its fee rate × mass, where fee rate is in sompi per gram. Nodes suggest fee rate buckets (priority / normal / low) via the GetFeeEstimate RPC. The minimum relay fee rate is 100 sompi per gram (raised from 1 at the Toccata hardfork), so a typical ~2,000-3,000 gram payment transaction costs a few hundred thousand sompi (~0.002-0.003 KAS) - still a tiny fraction of a cent.

Which mass? Not the sum of the three. The mempool normalizes the dimensions against each other and prices the largest one, so your fee is set by your worst dimension and the other two are free. A transaction that is heavy on storage mass but light on compute pays for the storage; padding it with extra compute up to that same level costs nothing more. The relay-fee floor above is narrower still - it applies only to compute and transient mass, on the grounds that storage growth is already bounded by the block limits.

Kaspalytics strives to provide accurate data - our highest level of effort is given to data validation and maintenance. However, we cannot guarantee 100% accuracy. Data is subject to inaccuracies and change.

Contact | © 2026 Kaspalytics