fix(deployless)!: respond in the shape the paged abi declares - #51
Merged
Conversation
A paged lens declares `(U[] results, uint256[] skipped)`, and the transport was
dropping the second array to hand back a bare `U[]`. That made paged lenses
unreadable through `readContract`, `decodeFunctionResult`, and contract
instances — the whole viem contract layer — for no gain: the honest aggregate of
N chunk-pages *is* a page, just rebased from chunk-local to caller-global
indices.
Returning it collapses everything built to work around its absence. `call2`
existed only to catch the error that carried the dropped array;
`DeploylessPartialResultError`, the `TERMINAL_ERROR` brand, the `failover`
terminal check, and the non-retryable `code` all existed only to get that error
through viem intact. A partial result is now a successful response, so failover
never sees it and viem cannot retry it — both resolved structurally rather than
by classification.
A caller who ignores `skipped` silently gets fewer elements where they'd
previously have caught an exception; the abi forces the array to exist, and
re-throwing is one `if` at the call site. `skipped` merges declines with
elements that exhausted the frame alone, which the README and TSDoc now call
out.
BREAKING CHANGE: `call2` is removed — use viem's `call` or `readContract`. A
paged `policy({ paged: true })` response is now the declared two-output tuple
rather than a bare `U[]`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
jonator
approved these changes
Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #49, which shipped paged lenses but had the transport drop half the
lens's declared return value.
The problem
A paged lens declares
(U[] results, uint256[] skipped). The transport reassembledthe chunks and handed back a bare
U[], discardingskipped. That made paged lensesunreadable through
readContract,decodeFunctionResult, and contract instances —the entire viem contract layer — because the response no longer matched the fragment:
call2existed to work around this, handing back{ data, missing }from a thrownerror because the real shape had nowhere to go.
The fix
Return the tuple the abi declares. The honest aggregate of N chunk-pages is a page —
resultsis everything served,skippedis everything that wasn't, rebased fromchunk-local to caller-global indices (and expanded across deduplicated inputs). Nothing
is lost, and
readContractworks:Unpaged lenses are untouched — no
skippedarray to report into, so an unservableelement still throws exactly as before.
What this deletes
Everything built to route the dropped array around viem:
call2DeploylessPartialResultErrorTERMINAL_ERROR/isTerminalErrorfailoverre-running on itfailoverterminal check + telemetrycode = -32099A partial result is now a successful response, so
failovernever sees it and viemcannot retry it — both resolved structurally instead of by classification. Net
−368 lines.
Trade-offs, accepted deliberately
A caller who ignores
skippedsilently gets fewer elements where they'd previouslyhave caught an exception. The abi forces the array to exist, and re-throwing is one
ifat the call site.skippedmerges elements the lens declined with elements that exhausted the frameeven alone. The second depends on the node's
eth_callgas cap, so another providermight serve them, whereas a decline is a property of the element. Called out in the
README and the
policyTSDoc.Verification
492 tests pass, typecheck / build / biome clean. New coverage:
readContractagainsta paged lens through a real client, the aggregated tuple shape, and the empty-input
page. The two observability tests from #45 that asserted
status: "error"on a partialresult now assert
"ok"—elements_missingstill stamps, and still matches theskippedarray the caller receives.