All Kimia math is fixed-point integer. There is no floating-point anywhere
in the programs, and client code should follow the same convention.
Precision scales
Converting in and out
Never multiply then divide through Number. Use bigint arithmetic end to
end if any intermediate exceeds Number.MAX_SAFE_INTEGER (2^53 − 1).
Rounding convention
Everything rounds down in the user’s favor (i.e. against the trader on the
protocol’s side). This is how the Rust crates do it:
In TS, replicate via integer bigint math:
Unrealized PnL example
Note: quote_entry is negative for longs and positive for shorts. Signs
capture direction.
Funding rate precision
Funding rates are 9-decimal signed i128. A rate of 5_000_000 ≈ 0.005
= 0.5% per period. Over a year of hourly periods, that’s ≈4,380%, an extreme
but expressible bound.
Cumulative rates live in two separate i128 accumulators
(cumulative_funding_rate_long, _short) to preserve V2 asymmetric-funding
headroom.
Time
- Unix seconds everywhere.
- Stored as
i64 for consistency with Solana’s Clock::unix_timestamp.
- In TS,
BigInt(Math.floor(Date.now() / 1000)).
BPS vs fixed-point
Don’t mix them. Margin ratios and fees use BPS (4 decimals, denominator 10k).
Funding and yield math use 9-decimal fixed point. Convert explicitly:
Overflow protection
All Rust mul_div calls intermediate through u128 and return Option<u64>.
In TS, bigint handles arbitrary size natively, overflow is impossible
unless you explicitly cast to Number.
Every on-chain instruction that overflows returns MathOverflow (error code
6070 on kimia-perp, similar codes on the other programs).