Skip to content

fix(deployless)!: respond in the shape the paged abi declares - #51

Merged
haydenshively merged 2 commits into
mainfrom
feat/paged-abi-shape
Aug 25, 2026
Merged

fix(deployless)!: respond in the shape the paged abi declares#51
haydenshively merged 2 commits into
mainfrom
feat/paged-abi-shape

Conversation

@haydenshively

Copy link
Copy Markdown
Collaborator

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 reassembled
the chunks and handed back a bare U[], discarding skipped. That made paged lenses
unreadable through readContract, decodeFunctionResult, and contract instances —
the entire viem contract layer — because the response no longer matched the fragment:

readContract(client, { abi: [pageAbi], ... })
// Position `129` is out of bounds (`0 < position < 128`)

call2 existed to work around this, handing back { data, missing } from a thrown
error 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 —
results is everything served, skipped is everything that wasn't, rebased from
chunk-local to caller-global indices (and expanded across deduplicated inputs). Nothing
is lost, and readContract works:

const [results, skipped] = await readContract(client, {
  abi: [pageAbi], functionName: 'page', args: [inputs],
  factory, factoryData, address: to,
  stateOverride: [policy({ abi: pageAbi, paged: true })],
})

Unpaged lenses are untouched — no skipped array to report into, so an unservable
element still throws exactly as before.

What this deletes

Everything built to route the dropped array around viem:

Removed Why it existed
call2 catch the error carrying the dropped array
DeploylessPartialResultError be that error
TERMINAL_ERROR / isTerminalError stop failover re-running on it
failover terminal check + telemetry same
code = -32099 stop viem retrying it

A partial result is now a successful response, so failover never sees it and viem
cannot retry it — both resolved structurally instead of by classification. Net
−368 lines.

Trade-offs, accepted deliberately

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 elements the lens declined with elements that exhausted the frame
even alone. The second depends on the node's eth_call gas cap, so another provider
might serve them, whereas a decline is a property of the element. Called out in the
README and the policy TSDoc.

Verification

492 tests pass, typecheck / build / biome clean. New coverage: readContract against
a 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 partial
result now assert "ok"elements_missing still stamps, and still matches the
skipped array the caller receives.

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>
@haydenshively haydenshively self-assigned this Aug 25, 2026
@haydenshively
haydenshively requested a review from jonator August 25, 2026 15:07
@haydenshively
haydenshively marked this pull request as ready for review August 25, 2026 15:07
@haydenshively

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 1804d799e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@haydenshively
haydenshively merged commit 0df02a9 into main Aug 25, 2026
3 checks passed
@haydenshively
haydenshively deleted the feat/paged-abi-shape branch August 25, 2026 17:12
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

APPS-1267

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants