JSON Integer Precision
CCIP fields like chainSelector, token amounts, and fees are uint64/uint256 values, bigger
than the 53 bits of integer precision a JSON number can safely hold in most languages.
--format json and jsonStringify emit these as plain JSON numbers. Python, Postgres JSONB,
and Go with json.Number read them at full precision. JavaScript does not: JSON.parse
represents every JSON number as an IEEE-754 double, which holds 53 bits of integer precision, so
larger values come back rounded, with no error raised. This is a deliberate design choice, not a
bug. The requirement is on the reader.
The failure
// CLI/SDK output, correct on the wire
const output = '{"chainSelector":11344663589394136015}'
JSON.parse(output).chainSelector
// => 11344663589394135000 (expected 11344663589394136015)
This affects every --format json command (show, search, parse, send --only-get-fee,
lane, token, …) and any bigint field above 2^53 (~9 × 10^15). Round token amounts like 1e18
happen to survive because they're exactly representable. Use a non-round value when testing your
own pipeline.
Reading the output in JavaScript
The SDK exports jsonParse, which reads large integers as bigint. Pass a type parameter for the
shape you expect; it returns unknown by default:
import { jsonParse } from '@chainlink/ccip-sdk'
const output = '{"chainSelector":11344663589394136015}'
jsonParse<{ chainSelector: bigint }>(output).chainSelector
// => 11344663589394136015n
If you're parsing your own object with jsonStringify, jsonParse is the matching reader:
import { jsonParse, jsonStringify } from '@chainlink/ccip-sdk'
const encoded = jsonStringify({ amount: 1234567890123456789n, note: 'not a bigint' })
// => '{"amount":1234567890123456789,"note":"not a bigint"}'
jsonParse<{ amount: bigint; note: string }>(encoded)
// => { amount: 1234567890123456789n, note: 'not a bigint' }
jsonParse distinguishes a bigint (bare integer) from a plain number integer (tagged with a
.0 suffix by jsonStringify, e.g. 2.0), so {"a":1,"b":2.0} round-trips as { a: 1n, b: 2 },
not { a: 1n, b: 2n }.
Other consumers
- Python:
json.loadsparses JSON integers as arbitrary-precisionintnatively. No special handling needed. - Go: use
json.Decoder.UseNumber()(or unmarshal into a*big.Int/stringfield) to avoid Go's defaultfloat64decoding, which has the same precision limit as JS. - Postgres: a
JSONBcolumn stores JSON numbers at full precision. Cast tonumericon read, keeping the extraction in parentheses.payload->>'amount'::numericis a precedence error and fails withinvalid input syntax for type numeric, since::binds tighter than->>. Never cast tofloat8; it rounds the value the same wayJSON.parsedoes:SQLSELECT (payload->>'amount')::numeric FROM messages; - jq: jq 1.7 and later preserve big integers as literal text through pure passthrough or field
selection (
jq .,jq '{amount: .amount}'). Any arithmetic (jq '.amount + 0') forces the value through adoubleand corrupts it the same wayJSON.parsedoes. jq 1.6 and earlier round these values even on plainjq ., so checkjq --versionbefore relying on passthrough. Treat these fields as opaque text in jq pipelines; use| tostringrather than any numeric operation.
Keep CCIP integer fields as text or bigint in every language. Converting them to a native
floating-point number loses precision.