Documentation
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 weiThat 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.
const memecoinIsCurrency0 = token.toLowerCase() < usdc.toLowerCase();
const currency0 = memecoinIsCurrency0 ? token : usdc;
const currency1 = memecoinIsCurrency0 ? usdc : token;