# Decimals

Arc's quote asset is a **6-decimal** ERC-20 USDC at
`0x3600000000000000000000000000000000000000`. Every launched memecoin is **18 decimals**.
Almost every arithmetic bug in an integration comes from mixing the two.

## Which side is which

| Quantity | Decimals |
|---|---|
| `quoteIn`, `minQuoteOut`, `launchFee`, `phantomQuote`, `graduationThreshold`, escrow balances | **6** |
| `minTokensOut`, `tokensIn`, `supply`, token balances, curve token reserves | **18** |

So `5e6` is five dollars, and `5e18` is five tokens. A launch fee of `500000` is 0.50 USDC.

## The native asset is a different thing

Arc uses USDC for gas, and viem's chain definition describes that native asset as
`{ symbol: "USDC", decimals: 18 }`. That is **not** the ERC-20 you trade with. They report
the same balance through two different interfaces at two different scales. Reading the native
balance and passing it as `quoteIn` is a 10^12 error.

Always read the traded balance from the ERC-20 at `0x3600…0000`.

## Never round-trip through a float

`Number(raw) / 1e18` loses precision above about 2^53. On a real balance the loss is large
enough to matter:

```
balance          714275814275814275814275815
via a float      714275814275814175605773926
lost                         100208501889 wei
```

That is a hundred billion wei of dust that a "sell max" can never reach, because the amount
submitted was never quite the amount held. Use `formatUnits` and `parseUnits`, which are
exact inverses, and keep bigints end to end.

## sqrtPriceX96 carries the skew

The graduated pool's `sqrtPriceX96` is derived from **raw amount ratios**, so it embeds the
10^12 difference between a 6-decimal and an 18-decimal side. It is not a human price. Adjust
for decimals before displaying anything derived from it.

## Currency ordering is by address, not by role

A V4 `PoolKey` sorts its two currencies by raw address. USDC (`0x36…`) is `currency0`
only when the memecoin's address happens to sort above it — which is most of the time, but
not always, because launch addresses are CREATE2 and effectively random.

**Compute the ordering per launch.** Assuming USDC is always `currency0` will silently
invert `zeroForOne` on some tokens and swap the wrong direction.

```ts
const memecoinIsCurrency0 = token.toLowerCase() < usdc.toLowerCase();
const currency0 = memecoinIsCurrency0 ? token : usdc;
const currency1 = memecoinIsCurrency0 ? usdc : token;
```