Summary
TokenRecord.Amount is declared uint64 (token/services/storage/db/driver/token.go:50-51):
// Amount is the Quantity converted to decimal
Amount uint64
while the backing column is amount NUMERIC(78, 0) NOT NULL (token/services/storage/db/sql/common/tokens.go:1394-1424) — 78 decimal digits, chosen to hold a 256-bit value. uint64 tops out at 20 digits.
The narrowing happens in the production write path, AppendToken (token/services/tokens/storage.go:210):
Amount: q.ToBigInt().Uint64(),
big.Int.Uint64() is documented as undefined when the value does not fit in 64 bits — it returns the low 64 bits with no error and no panic.
Consequences
For any token whose quantity exceeds 2^64-1, the amount column silently receives a wrapped value while the authoritative hex quantity column stays correct. The two columns then disagree, with no error raised at write time.
Quantities above uint64 are representable elsewhere in the stack: token.ToQuantity (token/token/quantity.go:67-85) returns a BigQuantity backed by big.Int whenever precision is not 64, so a driver configured with a larger precision can legitimately produce such values.
Today the damage is limited because no query path reads amount — the selector reads the hex quantity column instead (token/services/storage/db/sql/common/tokens.go:392-406). That also means the corruption is currently invisible.
Why this needs fixing before amount-aware selection
Any work that filters, orders or range-scans on amount (see the companion issue on the missing index) makes this column load-bearing. A wrapped amount would then produce incorrect selections — a large token appearing as a small one — rather than merely sitting unread.
Suggested direction
Widen the field to *big.Int (or a decimal string) and drop the Uint64() conversion, so the stored value matches the column's declared range and the hex quantity. Worth checking whether any consumer of TokenRecord.Amount relies on the uint64 type, and adding a round-trip test with a value above 2^64-1 to lock the behaviour in.
Related: #2017.
Summary
TokenRecord.Amountis declareduint64(token/services/storage/db/driver/token.go:50-51):while the backing column is
amount NUMERIC(78, 0) NOT NULL(token/services/storage/db/sql/common/tokens.go:1394-1424) — 78 decimal digits, chosen to hold a 256-bit value.uint64tops out at 20 digits.The narrowing happens in the production write path,
AppendToken(token/services/tokens/storage.go:210):big.Int.Uint64()is documented as undefined when the value does not fit in 64 bits — it returns the low 64 bits with no error and no panic.Consequences
For any token whose quantity exceeds 2^64-1, the
amountcolumn silently receives a wrapped value while the authoritative hexquantitycolumn stays correct. The two columns then disagree, with no error raised at write time.Quantities above
uint64are representable elsewhere in the stack:token.ToQuantity(token/token/quantity.go:67-85) returns aBigQuantitybacked bybig.Intwhenever precision is not 64, so a driver configured with a larger precision can legitimately produce such values.Today the damage is limited because no query path reads
amount— the selector reads the hexquantitycolumn instead (token/services/storage/db/sql/common/tokens.go:392-406). That also means the corruption is currently invisible.Why this needs fixing before amount-aware selection
Any work that filters, orders or range-scans on
amount(see the companion issue on the missing index) makes this column load-bearing. A wrappedamountwould then produce incorrect selections — a large token appearing as a small one — rather than merely sitting unread.Suggested direction
Widen the field to
*big.Int(or a decimal string) and drop theUint64()conversion, so the stored value matches the column's declared range and the hexquantity. Worth checking whether any consumer ofTokenRecord.Amountrelies on theuint64type, and adding a round-trip test with a value above 2^64-1 to lock the behaviour in.Related: #2017.