Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Encoding & Types

What this teaches: base-unit vs human-readable amounts, when to use Decimal vs bigint vs number, and how the SDK's JSON serialization handles snake_case.

Base units, always

Every amount the chain returns or accepts is in base units, as a string. dango has 6 decimals — 1 DANGO is "1000000". bridge/usdc has 6 decimals as well.

const oneDango = "1000000"        // base units
const twoBridge = "2000000"       // base units

Never parse amounts into number for math:

// BAD — silent precision loss above 2^53
const total = Number("9007199254740993") + Number("1")
 
// GOOD — keep as string, do math with bigint or Decimal
import { Decimal } from "@left-curve/utils"
const total = Decimal("9007199254740993").add("1")

All on-chain amount fields are exchanged as base-unit string. Convert to BigInt (integer) or Decimal (fractional) at the boundary — never to number.

When to use which

TypeUse for
bigintOn-chain integer math, atomic amounts, nonces
DecimalPrices, rates, anything with a non-integer dimension
numberBlock heights, indices, viewport math, timers
stringThe wire format for all on-chain amounts
import { Decimal, formatUnits, parseUnits } from "@left-curve/utils"
 
// Display: base units → human-readable string
const display = formatUnits("1234567", 6) // "1.234567"
 
// Submit: human-readable string → base units
const atomic = parseUnits("1.5", 6) // "1500000"
 
// Math: keep as Decimal until you serialize
const price = Decimal("100.25")
const qty = Decimal("3")
const notional = price.mul(qty).toFixed(6) // "300.750000"

JSON shape on the wire

Contract messages are snake_case on the wire and camelCase in TypeScript. The SDK converts automatically via snakeCaseJsonSerialization and camelCaseJsonDeserialization from @left-curve/encoding. Write camelCase in your code:

await client.execute({
  sender,
  execute: {
    contract,
    msg: { batchUpdateOrders: { creates: [...] } }, // camelCase
  },
})

Branded types

Address, Denom, Hex, Base64, and others are branded types from @left-curve/types. They are structurally strings but the brand prevents passing a denom where an address is expected:

import type { Address, Denom } from "@left-curve/sdk"
 
const addr = "0x1234567890abcdef1234567890abcdef12345678" as Address
const denom = "dango" as Denom
 
// type error — Address is not assignable to Denom
// transfer({ denom: addr, ... })

Next