feat: migrate erc20/erc4626 handlers to defineTask, parity-gated (G1) - #26
Merged
Merged
Conversation
Reimplement buildErc20Task/buildErc4626Task internally on defineTask
(t.call/t.derive) — public signatures and return shapes unchanged. Every
call is optional: true, replicating 1.0's silent-undefined-per-field
semantics; convertToAssets takes the raw balanceOf call ref (not the
coerced value) as its arg, reproducing the old step-2 balance-gating
exactly. position's conditional-key shape (T11) is reproduced with
t.derive, returning undefined or an object whose maxWithdraw/maxRedeem
keys are present only when those calls resolved.
The pre-migration implementations move to
src/__tests__/fixtures/legacy-handlers/{erc20,erc4626}.ts as the
parity-test oracle (buildErc20TaskLegacy/buildErc4626TaskLegacy),
test-only, excluded from the runtime bundle. src/__tests__/parity-g1.test.ts
runs a scenario table (with/without owner, each field failing
individually, bulk batching/slicing, dedupe) through both implementations
via runMultistepTasks, asserting toStrictEqual. src/__tests__/live/
holds an opt-in live mainnet fork test gated on RPC_URL.
Two accepted, documented deltas: under runSettled, the new impl retains
diagnostics.optionalFailures (legacy always reported []); new calls are
dedup-eligible, so bulk + { dedupe: true } can now merge identical calls
across entries. Net bundle effect: -348 bytes gzip (17.53KB -> 17.19KB).
…eview P1) resolveAll's call-mode resolution treated an upstream 'u' (optional-failed) or 'f' (hard failure) position as unusable, but let a 'v' state whose VALUE happens to be undefined - a derive that legitimately computed undefined, most commonly a handler's own coercion derive demoting a malformed executor value - pass straight through as a real argument. A dependent call could then be dispatched with that bogus value; Eip1193Executor encodes args synchronously inside executeMulticall, so a bad argument there could reject the whole physical batch, not just the one call depending on it. resolveAll now treats a 'v'-with-undefined-value position the same as 'u'/'f' in call-mode only (derives are unaffected - they legitimately consume undefined), synthesizing a "argument resolved to undefined" cause when there's no real upstream error to forward. erc4626's convertToAssets now takes the coerced `balance` ref (post- asBigInt) instead of the raw balanceOf call ref, so a malformed-but- "successful" balance demotes to undefined and skip-chains the dependent call exactly like a real balanceOf failure - closing the gap for the one place a raw call ref fed another call's argument position. Added three core tests (derive-returning-undefined feeding a non-optional call -> rejects with a synthesized cause; the same feeding an optional call -> demotes + diagnostics; a second derive consuming v-undefined is unaffected), plus parity scenarios: per-field malformed-success values for both handlers, and - for the balanceOf case specifically - executor call-count parity with legacy (step 2 not dispatched), position undefined, fulfilled under both run() and runSettled, and diagnostics containing exactly the synthesized-cause skip (documented as an accepted-delta extension, not noise).
halaprix
added a commit
that referenced
this pull request
Jul 23, 2026
* chore: release v1.3.0 - Version bump 1.2.0 → 1.3.0 in package.json and npm-lock - CHANGELOG.md: [1.3.0] entry with facts verified against git log v1.2.0..HEAD (PRs #25, #26, #27): * Added: MultichainResolver, examples/refinance.ts * Changed: handlers reimplemented on defineTask (parity-gated, observable deltas documented) * Bundle size 15→18KB (current 16.8KB measured) - README.md: updated bundle badge, added MultichainResolver to API table, added Multichain section with compiling example and refinance.ts pointer - MIGRATION.md: appended v1.2.x → v1.3.0 section (additive, no consumer action required) - SUMMARY.md: updated TOC with new sections - docs/benchmarks.md: updated size figures (13.9 → 16.8 KB) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: fix API accuracy — multichain, pinBlock, ethers - README multichain example: replace task instance reuse with factory pattern (flattened duplicate validation throws before any execution) - api-reference MultichainResolver.chain(): returns MulticallResolver (cached), throws on unknown chainId (not undefined) - Remove fake per-chain onPin Record — onPin is single inherited callback from BatchOptions; each chain reports once - Remove fake chainId < 1 validation rule — real rule: lowest-chainId rejection on concurrent failures - docs/index.html: Fix ethers claim — "requires an adapter" (domino doesn't depend on ethers) - pinBlock performance: Clarify +1 RPC only for absent/tag blocks; explicit blockNumber/blockHash add none - CHANGELOG: Correct MultichainResolver description (blocks map, not per-chain onPin; deterministic rejection) - Verify all snippets compile and bundle badge matches measured size Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: halaprix <halaprix@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
halaprix
added a commit
that referenced
this pull request
Jul 24, 2026
…#26) * feat: migrate erc20/erc4626 handlers to defineTask, parity-gated (G1) Reimplement buildErc20Task/buildErc4626Task internally on defineTask (t.call/t.derive) — public signatures and return shapes unchanged. Every call is optional: true, replicating 1.0's silent-undefined-per-field semantics; convertToAssets takes the raw balanceOf call ref (not the coerced value) as its arg, reproducing the old step-2 balance-gating exactly. position's conditional-key shape (T11) is reproduced with t.derive, returning undefined or an object whose maxWithdraw/maxRedeem keys are present only when those calls resolved. The pre-migration implementations move to src/__tests__/fixtures/legacy-handlers/{erc20,erc4626}.ts as the parity-test oracle (buildErc20TaskLegacy/buildErc4626TaskLegacy), test-only, excluded from the runtime bundle. src/__tests__/parity-g1.test.ts runs a scenario table (with/without owner, each field failing individually, bulk batching/slicing, dedupe) through both implementations via runMultistepTasks, asserting toStrictEqual. src/__tests__/live/ holds an opt-in live mainnet fork test gated on RPC_URL. Two accepted, documented deltas: under runSettled, the new impl retains diagnostics.optionalFailures (legacy always reported []); new calls are dedup-eligible, so bulk + { dedupe: true } can now merge identical calls across entries. Net bundle effect: -348 bytes gzip (17.53KB -> 17.19KB). * fix: skip-chain calls fed a v-undefined arg, not just u/f (external review P1) resolveAll's call-mode resolution treated an upstream 'u' (optional-failed) or 'f' (hard failure) position as unusable, but let a 'v' state whose VALUE happens to be undefined - a derive that legitimately computed undefined, most commonly a handler's own coercion derive demoting a malformed executor value - pass straight through as a real argument. A dependent call could then be dispatched with that bogus value; Eip1193Executor encodes args synchronously inside executeMulticall, so a bad argument there could reject the whole physical batch, not just the one call depending on it. resolveAll now treats a 'v'-with-undefined-value position the same as 'u'/'f' in call-mode only (derives are unaffected - they legitimately consume undefined), synthesizing a "argument resolved to undefined" cause when there's no real upstream error to forward. erc4626's convertToAssets now takes the coerced `balance` ref (post- asBigInt) instead of the raw balanceOf call ref, so a malformed-but- "successful" balance demotes to undefined and skip-chains the dependent call exactly like a real balanceOf failure - closing the gap for the one place a raw call ref fed another call's argument position. Added three core tests (derive-returning-undefined feeding a non-optional call -> rejects with a synthesized cause; the same feeding an optional call -> demotes + diagnostics; a second derive consuming v-undefined is unaffected), plus parity scenarios: per-field malformed-success values for both handlers, and - for the balanceOf case specifically - executor call-count parity with legacy (step 2 not dispatched), position undefined, fulfilled under both run() and runSettled, and diagnostics containing exactly the synthesized-cause skip (documented as an accepted-delta extension, not noise). --------- Co-authored-by: halaprix <halaprix@users.noreply.github.com>
halaprix
added a commit
that referenced
this pull request
Jul 24, 2026
* chore: release v1.3.0 - Version bump 1.2.0 → 1.3.0 in package.json and npm-lock - CHANGELOG.md: [1.3.0] entry with facts verified against git log v1.2.0..HEAD (PRs #25, #26, #27): * Added: MultichainResolver, examples/refinance.ts * Changed: handlers reimplemented on defineTask (parity-gated, observable deltas documented) * Bundle size 15→18KB (current 16.8KB measured) - README.md: updated bundle badge, added MultichainResolver to API table, added Multichain section with compiling example and refinance.ts pointer - MIGRATION.md: appended v1.2.x → v1.3.0 section (additive, no consumer action required) - SUMMARY.md: updated TOC with new sections - docs/benchmarks.md: updated size figures (13.9 → 16.8 KB) * docs: fix API accuracy — multichain, pinBlock, ethers - README multichain example: replace task instance reuse with factory pattern (flattened duplicate validation throws before any execution) - api-reference MultichainResolver.chain(): returns MulticallResolver (cached), throws on unknown chainId (not undefined) - Remove fake per-chain onPin Record — onPin is single inherited callback from BatchOptions; each chain reports once - Remove fake chainId < 1 validation rule — real rule: lowest-chainId rejection on concurrent failures - docs/index.html: Fix ethers claim — "requires an adapter" (domino doesn't depend on ethers) - pinBlock performance: Clarify +1 RPC only for absent/tag blocks; explicit blockNumber/blockHash add none - CHANGELOG: Correct MultichainResolver description (blocks map, not per-chain onPin; deterministic rejection) - Verify all snippets compile and bundle badge matches measured size --------- Co-authored-by: halaprix <halaprix@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
halaprix
added a commit
that referenced
this pull request
Jul 24, 2026
…#26) * feat: migrate erc20/erc4626 handlers to defineTask, parity-gated (G1) Reimplement buildErc20Task/buildErc4626Task internally on defineTask (t.call/t.derive) — public signatures and return shapes unchanged. Every call is optional: true, replicating 1.0's silent-undefined-per-field semantics; convertToAssets takes the raw balanceOf call ref (not the coerced value) as its arg, reproducing the old step-2 balance-gating exactly. position's conditional-key shape (T11) is reproduced with t.derive, returning undefined or an object whose maxWithdraw/maxRedeem keys are present only when those calls resolved. The pre-migration implementations move to src/__tests__/fixtures/legacy-handlers/{erc20,erc4626}.ts as the parity-test oracle (buildErc20TaskLegacy/buildErc4626TaskLegacy), test-only, excluded from the runtime bundle. src/__tests__/parity-g1.test.ts runs a scenario table (with/without owner, each field failing individually, bulk batching/slicing, dedupe) through both implementations via runMultistepTasks, asserting toStrictEqual. src/__tests__/live/ holds an opt-in live mainnet fork test gated on RPC_URL. Two accepted, documented deltas: under runSettled, the new impl retains diagnostics.optionalFailures (legacy always reported []); new calls are dedup-eligible, so bulk + { dedupe: true } can now merge identical calls across entries. Net bundle effect: -348 bytes gzip (17.53KB -> 17.19KB). * fix: skip-chain calls fed a v-undefined arg, not just u/f (external review P1) resolveAll's call-mode resolution treated an upstream 'u' (optional-failed) or 'f' (hard failure) position as unusable, but let a 'v' state whose VALUE happens to be undefined - a derive that legitimately computed undefined, most commonly a handler's own coercion derive demoting a malformed executor value - pass straight through as a real argument. A dependent call could then be dispatched with that bogus value; Eip1193Executor encodes args synchronously inside executeMulticall, so a bad argument there could reject the whole physical batch, not just the one call depending on it. resolveAll now treats a 'v'-with-undefined-value position the same as 'u'/'f' in call-mode only (derives are unaffected - they legitimately consume undefined), synthesizing a "argument resolved to undefined" cause when there's no real upstream error to forward. erc4626's convertToAssets now takes the coerced `balance` ref (post- asBigInt) instead of the raw balanceOf call ref, so a malformed-but- "successful" balance demotes to undefined and skip-chains the dependent call exactly like a real balanceOf failure - closing the gap for the one place a raw call ref fed another call's argument position. Added three core tests (derive-returning-undefined feeding a non-optional call -> rejects with a synthesized cause; the same feeding an optional call -> demotes + diagnostics; a second derive consuming v-undefined is unaffected), plus parity scenarios: per-field malformed-success values for both handlers, and - for the balanceOf case specifically - executor call-count parity with legacy (step 2 not dispatched), position undefined, fulfilled under both run() and runSettled, and diagnostics containing exactly the synthesized-cause skip (documented as an accepted-delta extension, not noise). --------- Co-authored-by: halaprix <halaprix@users.noreply.github.com>
halaprix
added a commit
that referenced
this pull request
Jul 24, 2026
* chore: release v1.3.0 - Version bump 1.2.0 → 1.3.0 in package.json and npm-lock - CHANGELOG.md: [1.3.0] entry with facts verified against git log v1.2.0..HEAD (PRs #25, #26, #27): * Added: MultichainResolver, examples/refinance.ts * Changed: handlers reimplemented on defineTask (parity-gated, observable deltas documented) * Bundle size 15→18KB (current 16.8KB measured) - README.md: updated bundle badge, added MultichainResolver to API table, added Multichain section with compiling example and refinance.ts pointer - MIGRATION.md: appended v1.2.x → v1.3.0 section (additive, no consumer action required) - SUMMARY.md: updated TOC with new sections - docs/benchmarks.md: updated size figures (13.9 → 16.8 KB) * docs: fix API accuracy — multichain, pinBlock, ethers - README multichain example: replace task instance reuse with factory pattern (flattened duplicate validation throws before any execution) - api-reference MultichainResolver.chain(): returns MulticallResolver (cached), throws on unknown chainId (not undefined) - Remove fake per-chain onPin Record — onPin is single inherited callback from BatchOptions; each chain reports once - Remove fake chainId < 1 validation rule — real rule: lowest-chainId rejection on concurrent failures - docs/index.html: Fix ethers claim — "requires an adapter" (domino doesn't depend on ethers) - pinBlock performance: Clarify +1 RPC only for absent/tag blocks; explicit blockNumber/blockHash add none - CHANGELOG: Correct MultichainResolver description (blocks map, not per-chain onPin; deterministic rejection) - Verify all snippets compile and bundle badge matches measured size --------- Co-authored-by: halaprix <halaprix@users.noreply.github.com>
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.
Summary
Implements spec G1 (1.3.0):
defineTask, public signatures byte-identical: every contract calloptional: true(silent-undefined parity), call-creation order matching the legacy step layout, and — replicating the legacy accessor coercions — every consumed value passes through step-transparent coercion derives (asBigInt-style), so a successful-but-malformed executor value shapes toundefinedexactly as 1.x always did, and a malformed balance gatesconvertToAssetsoff instead of reaching the encoder.undefined(e.g. a derive returning undefined) now skip-chains instead of leaking into the executor's encoder where it could reject a whole shared batch.src/__tests__/parity-g1.test.ts— 28 scenariostoStrictEqualagainst the legacy oracle (moved tosrc/__tests__/fixtures/legacy-handlers/, excluded from the runtime bundle; deleted after one minor per spec), covering per-field failures, step-2 gating, bulk slicing, absent-vs-undefined position keys, and per-field malformed-success values. Documented accepted deltas:runSettleddiagnostics now retain executor errors (kind/datatoBe-identical, cause preserved); handler calls are dedup-eligible (bulk +dedupe: truemerges).describe.runIf(RPC_URL)) comparing both impls on sDAI/USDC — self-skips in CI (no RPC secret); offline fixtures are the merge gate per plan.Test evidence
338/338 (+31: 28 parity + 3 core undefined-arg tests), compat-vs-dist 38/38, full gate build-first. Bundle ~neutral (17.2KB gzip).
Spec:
spec.md§ 1.3.0 G1.