# Precision & Units

Most integration bugs are unit bugs. This page is the single reference — every other page links back
here.

## Quick table

| Field | Unit | Example |
| :--- | :--- | :--- |
| `qty` | base asset amount, **1e10** | 0.001 BTC → `10000000` |
| `price`, `stopLoss`, `takeProfit`, `triggerPrice`, `entryPrice` | USD, **1e18** | $100,000 → `100000000000000000000000` |
| `amountIn` | `tokenIn` decimals | 10 USDC → `10000000` |
| `extraFee` | `tokenIn` decimals | see [Brokers & Referrals](/introduction/brokers#extrafee) |
| `margin`, `openFee`, `closeFee`, `holdingFee`, `fundingFee`, `pnl` | `lvToken` decimals | LVUSD and LVMON are both 18 |
| `antiDdosFee` | fee token decimals | see [Execution Fee](/gasless/fees) |
| `nonce` | milliseconds since epoch, `uint64` | `1785312000123` |
| `deadline` | seconds since epoch, `uint48` | `1785312300` |
| Percentage fields ending in `P` | **1e4** unless noted | `50` → 0.5% |
| `holdingFeeRate` | **1e12** | |
| `fundingFeeRate`, `longAccFundingFeePerShare` | **1e18**, signed | |
| `shareP`, `minCloseFeeP`, `lvTokenDiscountP` | **1e5** | |

## `qty` is the trap

`qty` is **not** notional USD and **not** wei. It is the amount of the *base* asset, scaled by 1e10 —
the same scale for every pair, regardless of the asset's own decimals.

To convert from a USD position size:

```ts
import { parseUnits } from 'viem'

// $100 of BTC at $100,000
const notionalUsd = 100
const price = 100_000

const qty = parseUnits((notionalUsd / price).toFixed(10), 10)
// 0.001 BTC -> 10_000_000n
```

Going the other way, when reading a position:

```ts
import { formatUnits } from 'viem'

const baseAmount = formatUnits(position.qty, 10)          // "0.001"
const notionalUsd =
  (Number(position.qty) / 1e10) * (Number(position.entryPrice) / 1e18)
```

::: warning
`.toFixed(10)` before `parseUnits` is not optional. `100 / 100000` in JavaScript is
`0.001`, but many other pairs produce values with more than 10 decimal places, and `parseUnits`
throws on excess precision.
:::

## Prices are always 1e18

Every price field in the protocol — the acceptable price on an open, stop-loss, take-profit, trigger
prices on TP/SL orders, and the entry/close prices you read back — is USD at 18 decimals. This holds
regardless of the collateral token's decimals.

```ts
const price = parseUnits('100000', 18)  // $100,000
```

## Amounts follow the token

`amountIn` is denominated in `tokenIn`'s own decimals. USDC is 6; WMON, LVUSD and LVMON are 18. Read
`decimals()` rather than assuming.

Values *inside* a position (`margin`, fees, PnL) are denominated in the **`lvToken`**, not `tokenIn`.
A position funded with 10 USDC (`amountIn` at 6 decimals) reports its margin in LVUSD at 18
decimals — the two are not the same scale. See
[Collateral and lvToken](/introduction/concepts#collateral-and-lvtoken).

## Percentages

Fields whose name ends in `P` are integer percentages with an implied scale. The common case is 1e4,
where `10000` = 100%:

```ts
const slippagePercent = Number(config.slippageLongP) / 100  // 50 -> 0.5%
```

The exceptions are listed in the table above — `shareP`, `minCloseFeeP` and `lvTokenDiscountP` use
1e5, and `holdingFeeRate` uses 1e12.

## Signed values

`fundingFee`, `accruedFundingFee`, `pnl` and `fundingFeeRate` are signed. A positive funding fee
means the trader **receives**; negative means the trader **pays**. Decode them as `int256`/`int96`,
not `uint`.

## JSON transport

REST API responses and 1CT request bodies carry large integers as **strings**, not JSON numbers.
`nonce` in particular exceeds `Number.MAX_SAFE_INTEGER` and will silently lose precision if you let
`JSON.stringify` write it as a number.

```ts
body: JSON.stringify({
  nonce: nonce.toString(),          // correct
  antiDdosFee: antiDdosFee.toString(),
})
```
