Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [1.3.0] — 2026-07-23

### Added
- `MultichainResolver` — parallel per-chain execution with `chain()` builder, `snapshot()` for atomicity, `runAll`/`runAllSettled` entry points, per-chain `blocks` overrides, and flattened duplicate-instance validation before any chain executes. On failure across multiple chains, the error from the lowest-chainId is thrown (deterministic). Single-T generic over result shape per call (mixed shapes require separate invocations).
- `MultichainRunOptions` — batch options wrapper with per-chain customization via `blocks` map (e.g. `{ blocks: { 1: { blockNumber }, 8453: { blockNumber } } }`).
- `examples/refinance.ts` — Aave v3 + Spark + Morpho Blue dynamic-target refinance comparison across Ethereum mainnet and Base (optional via `RPC_URL_8453`); demonstrates `defineTask`, `Presets.throughput`, `MultichainResolver`, and per-chain block pinning. Type-checked in CI against the built dist.

### Changed
- **Handler implementation** — ERC20 and ERC4626 handlers reimplemented on `defineTask` (internal structure change; public signatures identical, source-compatible).
- **Observable deltas** (all aligned with legacy behavior for backward compatibility):
- `runSettled` now retains per-call errors in task diagnostics for handler tasks (formerly dropped). Populated via `TaskDiagnostics.optionalFailures` only for `defineTask` output; legacy hand-written tasks return empty `[]`.
- Handler calls are now dedup-eligible (under `dedupe: true`) when built via `defineTask`; legacy tasks are unaffected (never merged).
- Malformed executor values still resolve to `undefined` (coercion parity with legacy).
- **Bundle size** — gzip ceiling now 15→18 KB (multichain feature class added; current ~17.2 KB measured).

### Internal
- Legacy handler oracle retained under `src/__tests__/fixtures/legacy-handlers/` for one release cycle, then removed.

### Deprecated
- (No new deprecations; see 1.1.0 and 1.0.x MIGRATION sections for prior changes.)

## [1.2.0] — 2026-07-23

### Added
Expand Down Expand Up @@ -118,6 +139,7 @@ First public release of `@halaprix/domino`.
- `position.assets` is `bigint | undefined` — correctly represents the case where `balanceOf` succeeds but `convertToAssets` reverts.
- CI now runs real `tsc --noEmit` (typecheck); build step runs before tests so `dist/` exists for bundle-size checks on a clean checkout.

[1.3.0]: https://github.com/halaprix/domino/releases/tag/v1.3.0
[1.2.0]: https://github.com/halaprix/domino/releases/tag/v1.2.0
[1.1.0]: https://github.com/halaprix/domino/releases/tag/v1.1.0
[1.0.1]: https://github.com/halaprix/domino/releases/tag/v1.0.1
Expand Down
53 changes: 52 additions & 1 deletion MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,55 @@
# Migration Guide — v0.1.0 → v1.0.0
# Migration Guide

## v1.2.x → v1.3.0

**Fully additive — no consumer action required.** All 1.2.x code keeps working unchanged.

### What's new

- **`MultichainResolver`** — parallel per-chain task execution with `runAll()`/`runAllSettled()` and optional atomic block snapshots via `snapshot()`. No changes to existing single-chain code.
- **Handler internals** — ERC20 and ERC4626 handlers reimplemented on `defineTask` (internal change; public signatures identical, behavior backward-compatible).
- Observable delta in `runSettled`: handler tasks now populate `TaskDiagnostics.optionalFailures` with per-call errors (formerly omitted).
- Handler calls are dedup-eligible under `dedupe: true` (newly, though the feature itself is unchanged).
- No consumer code needs updating.

### Example: multichain snapshot

```typescript
import { createPublicClient, http } from "viem"
import { mainnet, base } from "viem/chains"
import { Eip1193Executor, MultichainResolver, defineTask } from "@halaprix/domino"
import type { Address } from "@halaprix/domino"

declare const vaultAddress: Address
declare const ownerAddress: Address

const resolver = new MultichainResolver({
[mainnet.id]: new Eip1193Executor(createPublicClient({ chain: mainnet, transport: http() })),
[base.id]: new Eip1193Executor(createPublicClient({ chain: base, transport: http() })),
})

// Snapshot block numbers per chain (atomic)
const blocks = await resolver.snapshot()

// Execute the same task on each chain
const vaultAbi = [
"function balanceOf(address) view returns (uint256)",
] as const

const makeTasks = () => [
defineTask((t) => ({
balance: t.call({ target: vaultAddress, abi: vaultAbi, functionName: "balanceOf", args: [ownerAddress] }),
})),
]

// Execute tasks with pinned per-chain blocks
const results = await resolver.runAll({
[mainnet.id]: makeTasks(),
[base.id]: makeTasks(),
}, { blocks: Object.fromEntries(Object.entries(blocks).map(([cid, num]) => [cid, { blockNumber: num }])) })
```

## v0.1.0 → v1.0.0

## What changed

Expand Down
57 changes: 52 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ _| _| _| _| _| _| _| _| _| _| _| _|

[![CI](https://github.com/halaprix/domino/actions/workflows/ci.yml/badge.svg)](https://github.com/halaprix/domino/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@halaprix/domino)](https://www.npmjs.com/package/@halaprix/domino)
[![bundle size](https://img.shields.io/badge/gzip-16.8KB-brightgreen)](https://www.npmjs.com/package/@halaprix/domino)
[![bundle size](https://img.shields.io/badge/gzip-16.8KB-brightgreen)](https://www.npmjs.com/package/@halaprix/domino) (v1.3.0)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue)](https://www.typescriptlang.org/)
[![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Expand Down Expand Up @@ -235,6 +235,52 @@ const oldVault = await resolveErc4626Vault({

Works with `blockHash`, `blockTag`, or `blockNumber`. Even on chains where Multicall3 didn't exist yet — domino falls back to deployless multicall automatically.

## Multichain queries with `MultichainResolver`

Execute the same task graph across multiple chains in parallel, with atomic block snapshots per chain:

```typescript
import { createPublicClient, http } from "viem"
import { mainnet, base } from "viem/chains"
import { Eip1193Executor, MultichainResolver, defineTask } from "@halaprix/domino"
import type { Address } from "@halaprix/domino"

const resolver = new MultichainResolver({
[mainnet.id]: new Eip1193Executor(createPublicClient({ chain: mainnet, transport: http() })),
[base.id]: new Eip1193Executor(createPublicClient({ chain: base, transport: http() })),
})

// Snapshot block numbers per chain for atomicity
const blocks = await resolver.snapshot()

declare const vaultAddress: Address
declare const ownerAddress: Address

// Task factory — create a fresh task per chain (tasks are single-run)
const makeTask = () =>
defineTask((t) => ({
balance: t.call({
target: vaultAddress,
abi: [{ type: 'function', name: 'balanceOf', stateMutability: 'view', inputs: [{ type: 'address' }], outputs: [{ type: 'uint256' }] }],
functionName: 'balanceOf',
args: [ownerAddress],
}),
}))

// Run the same task graph on each chain with pinned blocks
const results = await resolver.runAll({
[mainnet.id]: [makeTask()],
[base.id]: [makeTask()],
}, {
blocks: Object.fromEntries(
Object.entries(blocks).map(([cid, num]) => [cid, { blockNumber: num }])
),
})
// results is { [mainnet.id]: [...], [base.id]: [...] }
```

For a complete example spanning Aave v3, Spark, and Morpho Blue across chains, see [`examples/refinance.ts`](examples/refinance.ts).

## When NOT to use it

- Pure batches (no dependencies) → plain `multicall` is simpler.
Expand All @@ -247,7 +293,8 @@ Works with `blockHash`, `blockTag`, or `blockNumber`. Even on chains where Multi
|--------|-----------|
| `defineTask()` | **Recommended.** Ref-graph task builder — `t.call`/`t.derive`, compiles to a `MultistepTask` |
| `runSettled()` | Per-task settlement — every task gets its own fulfilled/rejected outcome instead of one failure aborting the whole call |
| `MulticallResolver` | Convenience layer — call `run()`/`runSettled()` to execute a state machine |
| `MultichainResolver` | Parallel per-chain execution — `chain()`, `snapshot()`, `runAll()`/`runAllSettled()` with per-chain block pinning and atomicity |
| `MulticallResolver` | Single-chain convenience layer — call `run()`/`runSettled()` to execute a state machine |
| `Eip1193Executor` | Single engine — works with any EIP-1193 provider |
| `runMultistepTasks()` | Core FSM — bare-metal version of the resolver |
| `DominoCallError` | Structured error for a failed call — `kind` discriminates revert/decode/batch/skipped/derive |
Expand All @@ -263,10 +310,10 @@ Works with `blockHash`, `blockTag`, or `blockNumber`. Even on chains where Multi

## Documentation

- [API Reference](docs/api-reference.md) — full `defineTask`, `runSettled`, `MultichainResolver`, `Eip1193Executor` docs
- [Benchmarks](docs/benchmarks.md) — bundle size, RPC round-trip counts, `Presets.throughput` tuning
- [Refinance example](examples/refinance.ts) — Aave v3/Spark/Morpho across chains, dynamic `Ref<Address>` targets, per-chain block pinning
- [Architecture & AI Context](CLAUDE.md)
- [API Reference](docs/api-reference.md)
- [Benchmarks](docs/benchmarks.md)
- [Refinance example](examples/refinance.ts) — Aave v3/Spark/Morpho across chains, dynamic `Ref<Address>` targets, `Presets.throughput`, `MultichainResolver`
- [Migration Guide](MIGRATION.md)
- [Changelog](CHANGELOG.md)

Expand Down
6 changes: 5 additions & 1 deletion SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
* [Introduction](README.md)
* [Quick Start](README.md#quick-start)
* [API Reference](docs/api-reference.md)
* [defineTask](docs/api-reference.md#definetask--the-recommended-way-to-describe-a-task)
* [MultichainResolver](docs/api-reference.md#multichainresolver--parallel-per-chain-execution)
* [Performance Guidance](docs/api-reference.md#performance-guidance)
* [Benchmarks](docs/benchmarks.md)
* [Architecture](CLAUDE.md)
* [Contributing](CONTRIBUTING.md)
* [Changelog](CHANGELOG.md)
* [Changelog](CHANGELOG.md)
* [Migration Guide](MIGRATION.md)
Loading
Loading