Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kynesys.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Amounts & Denominations

DEMOS uses two denominations for native tokens:
DenominationDescription
DEMHuman-readable unit. 1 DEM is what users see in wallets and explorers.
OSSmallest indivisible unit on the wire. 1 DEM = 10^9 OS.
The conversion factor is exposed as OS_PER_DEM = 10n ** 9n.
The OS denomination ships behind the osDenomination fork. Pre-fork nodes accept only whole-DEM amounts on the wire (legacy number shape). Post-fork nodes accept full sub-DEM precision as bigint OS. The SDK detects the active wire format automatically — see Fork detection below.

Passing amounts to transfer and pay

Demos.transfer(to, amount) and its alias Demos.pay(to, amount) accept either:
  • bigint (preferred) — the amount in OS
  • number (legacy, deprecated) — the amount in whole DEM, auto-converted to OS internally
import { Demos } from "@kynesyslabs/demosdk/websdk"
import { denomination } from "@kynesyslabs/demosdk"

const demos = new Demos()
await demos.connect("https://node2.demos.sh")
await demos.connectWallet(mnemonic)

// Preferred: bigint OS, full precision
await demos.pay("0x...", 100_000_000_000n)            // 100 DEM in OS
await demos.transfer("0x...", denomination.demToOs("1.5"))  // 1.5 DEM

// Legacy: number DEM (will be removed in v4)
await demos.pay("0x...", 100)                         // 100 DEM
denomination.demToOs(input) accepts a number or string and returns a bigint in OS.
The number overload is deprecated. New code should pass bigint OS amounts. The legacy path remains for v2 callers and will be removed in v4.

The denomination module

The SDK exposes a small set of helpers under denomination:
HelperPurpose
demToOs(dem: number | string): bigintConvert DEM (human-readable) to OS (bigint).
osToDem(os: bigint): stringConvert OS to a human-readable DEM string.
parseOsString(osString: string): bigintParse a wire-format OS string into a bigint.
toOsString(os: bigint): stringSerialize an OS bigint to its wire string form.
formatDem(os: bigint): stringFormat OS as a display string with DEM suffix.
OS_PER_DEMConstant 10n ** 9n.
OS_DECIMALSConstant 9.
import { denomination } from "@kynesyslabs/demosdk"

const os = denomination.demToOs("1.500000001")  // 1500000001n
const back = denomination.osToDem(os)           // "1.500000001"
const display = denomination.formatDem(os)      // "1.500000001 DEM"

Fork detection

The SDK exposes the node’s fork-activation status through Demos.getNetworkInfo():
const info = await demos.getNetworkInfo()
// {
//   forks: {
//     osDenomination: {
//       activationHeight: 12345 | null,
//       activated: true | false,
//       currentHeight: 67890
//     }
//   }
// }
The first call hits the node’s getNetworkInfo RPC. The result is cached on the Demos instance for its lifetime, keyed by the active RPC URL. To re-detect after a node upgrade, construct a fresh Demos instance. Failure modes:
  • If the node is unreachable or returns a malformed response, getNetworkInfo() returns null.
  • The SDK assumes pre-fork wire format in that case and emits a one-time console.warn recommending the operator upgrade the target node.
  • Transient outages are recovered via a 30-second TTL on the failure memo, so the SDK retries automatically.
You don’t normally need to call getNetworkInfo() yourself — transfer/pay/sign consult the cached fork status automatically. Calling it is useful when you want to display fork status to your users or branch your application logic.

Sub-DEM precision guard

When the connected node is pre-fork and your code submits a bigint OS amount that carries sub-DEM precision (i.e. amount % OS_PER_DEM !== 0n), the SDK refuses to sign the transaction and throws SubDemPrecisionError instead. This prevents silent truncation on the legacy DEM-number wire.
import { Demos } from "@kynesyslabs/demosdk/websdk"
import { denomination } from "@kynesyslabs/demosdk"

const demos = new Demos()
await demos.connect(rpc)
await demos.connectWallet(mnemonic)

try {
    // 1.5 DEM = 1_500_000_000n OS — half a DEM is sub-DEM precision
    await demos.pay("0x...", 1_500_000_000n)
} catch (err) {
    if (err instanceof denomination.SubDemPrecisionError) {
        console.warn(
            `Cannot send sub-DEM amount on a pre-fork node. ` +
            `OS amount: ${err.amountOs}, sub-DEM remainder: ${err.subDemRemainderOs}`,
        )
        // Round to whole DEM, or upgrade the target node.
    } else {
        throw err
    }
}
SubDemPrecisionError exposes:
  • amountOs: bigint — the OS amount the caller passed
  • subDemRemainderOs: bigint — the portion smaller than 1 DEM (i.e. amount % OS_PER_DEM)
The guard is inert on post-fork nodes — full sub-DEM precision is accepted natively.

Reading balances

Demos.getAddressInfo(address) returns the balance in OS as a bigint on post-fork nodes. The SDK widens the wire balance field to number | string and normalizes it internally, so you always receive a bigint from the public API.
const info = await demos.getAddressInfo(address)
console.log("balance (OS):", info.balance)                       // bigint
console.log("balance (DEM):", denomination.osToDem(info.balance)) // string

Migration cheat-sheet

You used to write…Now write…
demos.pay(to, 100)demos.pay(to, denomination.demToOs(100)) or demos.pay(to, 100_000_000_000n)
demos.transfer(to, 1.5)demos.transfer(to, denomination.demToOs("1.5"))
Compare balance to a numeric DEMinfo.balance >= denomination.demToOs("1.0") (bigint comparison)
Display a balancedenomination.formatDem(info.balance)"1.5 DEM"
The legacy number API still works against pre-fork nodes for whole-DEM amounts. Sub-DEM precision and post-fork wire format require the bigint OS shape.