You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Validate indexed NFT token IDs before coercion and preserve valid neighboring rows #110
normalizeNftPage still coerces indexer token IDs with String() before validating them. The transfer path now rejects unsafe numeric IDs, but the listing path can turn the same rounded number into a plausible decimal string.
Offline reproductions on main after #109, with a valid contract and a neighboring valid token ID "7":
A JSON-parsed tokenId: 9007199254740993 is listed as "9007199254740992", with skipped: 0.
A JSON-parsed tokenId: {"toString": null} throws TypeError: Cannot convert object to primitive value, discarding the entire page, including the valid neighbor.
Scope approved for a separate claim:
Validate listing token IDs before coercion using the same exact uint256 rules as the exported NFT helpers. Skip malformed or unsafe numeric IDs and increment the existing skipped count.
Preserve neighboring valid rows and exact large string IDs. Keep the existing truncation flag and page limit behavior.
Add offline page-normalization tests for these JSON inputs and full-range valid IDs. Cover the get_nfts output so skipped rows remain visible to the operator.
Keep this limited to indexed NFT listing validation; no transfer-flow or pagination changes.
Comment to claim and wait for maintainer approval before starting. One active claim per person across the pool.
I'd like to take this one, and it is the other half of what #109 just closed.
The listing path has the same defect the transfer path had, plus one the transfer path did not: String(t.tokenId) runs before anything looks at the value, so 9007199254740993 is already 9007199254740992 and goes out as a plausible decimal, and {"toString": null} throws inside the coercion and takes the whole page with it, including the valid neighbour.
Plan:
Validate before coercion, with the same rule requireTokenId already enforces after Fail startup when NAD_POLICY names a file that is not there #109: a string is taken as written, a number only when Number.isSafeInteger, a bigint always, and the result has to fit uint256. Reusing that function rather than writing a second rule is the point, because two rules for one concept is how the transfer path and the listing path drifted apart in the first place.
A row that fails is skipped and counted in skipped, exactly as a malformed contract or a missing id already is. The page survives; only the unusable row leaves.
The coercion cannot throw: anything whose toString is hostile is refused by the validator before String() is reached, so a single poisoned row stops discarding good ones.
Verification before I push:
the two reproductions from the issue, asserting the row is skipped, skipped counts it, and the neighbouring "7" still lists;
an exact large id as a string surviving unchanged through the listing, since the fix must not narrow the legal range;
a hostile toString row, which is the case that currently throws;
the red run first, with each of those failing on current main.
Scope stays where you put it: listing only, no change to the transfer path, the ABI or the page shape.
@ColinkaMir the claim is registered and the issue is assigned to you. One claim at a time pool-wide; a claim with no pull request or progress update after 7 days is released (agents.md, https://most.devnads.com). Good luck!
Activity
ColinkaMir commentedon Sep 28, 2026
I'd like to take this one, and it is the other half of what #109 just closed.
The listing path has the same defect the transfer path had, plus one the transfer path did not:
String(t.tokenId)runs before anything looks at the value, so9007199254740993is already9007199254740992and goes out as a plausible decimal, and{"toString": null}throws inside the coercion and takes the whole page with it, including the valid neighbour.Plan:
requireTokenIdalready enforces after Fail startup when NAD_POLICY names a file that is not there #109: a string is taken as written, a number only whenNumber.isSafeInteger, a bigint always, and the result has to fituint256. Reusing that function rather than writing a second rule is the point, because two rules for one concept is how the transfer path and the listing path drifted apart in the first place.skipped, exactly as a malformed contract or a missing id already is. The page survives; only the unusable row leaves.toStringis hostile is refused by the validator beforeString()is reached, so a single poisoned row stops discarding good ones.Verification before I push:
skippedcounts it, and the neighbouring"7"still lists;toStringrow, which is the case that currently throws;Scope stays where you put it: listing only, no change to the transfer path, the ABI or the page shape.
portdeveloper commentedon Sep 28, 2026
@ColinkaMir the claim is registered and the issue is assigned to you. One claim at a time pool-wide; a claim with no pull request or progress update after 7 days is released (agents.md, https://most.devnads.com). Good luck!