Skip to content

Commit 1fc82df

Browse files
authored
docs: record the JSON type a numeric attribute takes on each read path
A reader that decodes a chain response through @wharfkit/antelope and a reader that calls a hosted API receive different JSON types for the same stored value, and nothing in the corpus said so. In the raw get_table_rows body nodeos prints every float as a quoted decimal string of the widened double, the client library follows that convention, and the hosted APIs answer a number, except on values that reached the database through the ABI action path, where the indexer objectified the string form and stored it. The float32 half of that convention loses information: toFixed(7) keeps seven decimal places where a float32 carries about seven significant digits, so a value needing more than seven fractional decimals cannot be read back, and the two float32 values one and two units above 0.5 print alike. A short decimal literal survives, which is why the measured failure rates are stated as sample rates rather than as bounds on a decade. Every claim is cited to a live read or to the library source. The path rule comes from four responses rather than from indexer source, which the ledger row states, as does the discrepancy between the SDK page's declared numeric widths and the quoted decimal a raw table read answers.
1 parent 880dec0 commit 1fc82df

7 files changed

Lines changed: 98 additions & 6 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Start here. Find the outcome you are working toward below, read the file on that
3434
| Choose an attribute type, or find out why a value was rejected | `reference/atomicassets/custom-types.md` |
3535
| Decode an attribute blob read straight from a chain table | `reference/atomicassets/serialization.md` |
3636
| Decide which layer an attribute value comes from when template and asset disagree | `reference/atomicassets/data-precedence.md` |
37+
| Tell a JSON number from the quoted decimal the chain prints for the same value | `reference/numeric-values-in-json.md` |
3738
| Find out whether a chain runs V2 yet, and what V2 added | `reference/atomicassets/v2-upgrade.md` |
3839
| Handle a token-backed asset minted before backing was deprecated | `reference/atomicassets/backing-tokens.md` |
3940
| List an asset for sale and settle the purchase, oracle-priced sales included | `guides/sales.md` |

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
What each release of this corpus changed, one release per tag. `Corrected` comes first in every release, because a fact that was wrong is what a returning reader has to see before anything else. The other sections are `Added`, `Revalidated`, and `Removed`, in that order, and a section with nothing in it is left out.
44

5+
## 2026.08.4
6+
7+
### Added
8+
9+
- `reference/numeric-values-in-json.md` records what JSON type a numeric attribute value takes on each read path: nodeos widens every float to a double on construction and prints it as a quoted seventeen-place decimal, `@wharfkit/antelope` objectifies one to a string in its own form instead (`toFixed(7)` for a float32 at 1.x, `Number.toString` for a float64, and the same shortest-round-trip form for both widths from the 2.x commit that drops the `Float32` override), and a hosted API answers with a number wherever the value was decoded from serialized bytes and with a string in that client-library form wherever it entered through the ABI action path.
10+
- The same page records that `toFixed(7)` keeps seven decimal places rather than seven significant digits at 1.x, so a float32 value that needs more than seven fractional decimals does not survive the client library's string form there. A sample of 20,000 random values per decade found no failures at or above 1, climbing to 99 percent between 0.01 and 0.02 and 100 percent at or below 0.001, rates over a sample rather than a bound on the interval. It also records that nodeos's own seventeen fixed places bounds the string without making it lossless: a double under `5e-18` prints as zero, and a matching per-decade measurement of `toFixed(17)` round trips finds failures climbing from 27 percent to 91 percent as magnitude falls below 0.1.
11+
- The page attributes the string-versus-number split on the ABI action path to each indexer's own decode call site (`atomicassets-api`'s own `Serializer.objectify` call against `@atomichub/antelope-ship-utils`'s, which switches to `objectifyNumericFloats` at 2.0.0) rather than to the live responses alone, and states that the change reaches new writes only, with a repair pass for rows already stored. `reference/sdk/atomicmarket.md` and `reference/atomicassets/serialization.md` change alongside it: a typed table row is a declared shape, not a runtime conversion, and a native ABI float field decodes independently of a serialized attribute's own codec.
12+
- `reference/validation.md` pins `@atomichub/atomicassets` 2.2.0 for attribute decoding, `@atomichub/antelope-ship-utils` 1.0.1 and 2.0.0 for the decode call site that changed between them, `AntelopeIO/spring` v1.2.2 for the nodeos serialization source, and a `@wharfkit/antelope` 2.x commit for the `Float32` string-form change. The two SDK pages keep the `atomicassets-sdk` `v2.1.1` pin.
13+
514
## 2026.08.3
615

716
### Corrected

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ WAX mainnet still runs the V1 `atomicassets` and `atomicmarket` contracts while
4141

4242
| Directory | Contents |
4343
| --- | --- |
44-
| `reference/` | Facts: `atomicassets/`, `atomicmarket/`, `atomictools/`, and `sdk/` directories with per-topic pages (structure, actions, tables, fees, serialization, SDK surfaces), plus one file each for the indexer, API, API streaming, media conventions, chain, and client libraries |
44+
| `reference/` | Facts: `atomicassets/`, `atomicmarket/`, `atomictools/`, and `sdk/` directories with per-topic pages (structure, actions, tables, fees, serialization, SDK surfaces), plus one file each for the indexer, API, API streaming, media conventions, numeric JSON types, chain, and client libraries |
4545
| `guides/` | End-to-end workflows: asset lifecycle, offers, sales, auctions, buyoffers, deposits, claim links, notification integration, contract testing with VeRT, and querying the API |
4646
| `learning/` | The unverified tier: claims that have not been checked yet, and the gate they pass before promotion |
4747
| `skills/` | Agent skills. `atomic-integration` routes a coding agent to the reference file its task needs. `report` writes a sanitized report about these docs into the consuming project, and never edits this repository |

reference/atomicassets/serialization.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ An off-chain implementation needs three things, in this order:
6868
2. **The varint/zigzag codec.** Implement `toVarintBytes`/`unsignedFromVarintBytes` and `zigzagEncode`/`zigzagDecode` exactly as described above. These are not standard varints with a sign bit baked in; zigzag is a separate transform applied only to signed types before varint-encoding.
6969
3. **The per-type encode/decode table above**, including the array rule (varint count + repeated elements, one level only) and the identifier arithmetic (`position + 4` on encode, `identifier - 4` on decode).
7070

71-
To serialize an `ATTRIBUTE_MAP` for submission in a transaction (`mintasset`, `setassetdata`, `createtempl`, `setcoldata`, and similar actions all take `ATTRIBUTE_MAP` parameters that the contract itself serializes on execution; the caller passes attribute maps, not bytes), an off-chain caller does not need this codec at all: it constructs the `ATTRIBUTE_MAP` as ABI JSON per `reference/atomicassets/custom-types.md` and lets the contract's own `serialize` call do the encoding. This codec is needed off-chain specifically to decode `immutable_serialized_data` / `mutable_serialized_data` / collection `serialized_data` bytes read back from `get_table_rows`, or to reproduce the contract's stored bytes for verification. Decoding each layer with this codec is only the first step; combining template and asset layers into one effective attribute set is a separate concern covered in [Attribute data precedence](data-precedence.md).
71+
To serialize an `ATTRIBUTE_MAP` for submission in a transaction (`mintasset`, `setassetdata`, `createtempl`, `setcoldata`, and similar actions all take `ATTRIBUTE_MAP` parameters that the contract itself serializes on execution; the caller passes attribute maps, not bytes), an off-chain caller does not need this codec at all: it constructs the `ATTRIBUTE_MAP` as ABI JSON per `reference/atomicassets/custom-types.md` and lets the contract's own `serialize` call do the encoding. This codec is needed off-chain specifically to decode `immutable_serialized_data` / `mutable_serialized_data` / collection `serialized_data` bytes read back from `get_table_rows`, or to reproduce the contract's stored bytes for verification. Decoding each layer with this codec is only the first step; combining template and asset layers into one effective attribute set is a separate concern covered in [Attribute data precedence](data-precedence.md). The JSON type a decoded value then takes is a further concern, and this codec does not settle it: an attribute arrives as whatever type its decoder returns, which for the hosted APIs and the decoding SDKs is a number. A native ABI float column and a serialized attribute are not interchangeable at `get_table_rows`, and the two never meet at this codec: `market_fee` is a plain ABI `float64` field on the `collections` row, so `get_table_rows` decodes it itself and answers a quoted decimal string directly, no codec involved. `serialized_data` on that same row (`immutable_serialized_data` / `mutable_serialized_data` on a template or asset row) is this codec's own `vector<uint8_t>`, so `get_table_rows` cannot decode it and answers undecoded bytes; a reader gets an attribute's actual value out of it only by running this codec, not by asking nodeos. See [Numeric values in JSON](../numeric-values-in-json.md).
7272

7373
Source: `include/atomicassets.hpp:363-421` (`schemas_s`, `templates_s`, `template_mutables_s`, `assets_s` table shapes), `include/atomicassets.hpp:458` (`config_s.collection_format`)
7474

0 commit comments

Comments
 (0)