Skip to content

Commit 07e4357

Browse files
claudetuler
authored andcommitted
feat!: track the rollups-node JSON-RPC API changes
Mirror the JSON-RPC API changes of the Cartesi node in @cartesi/rpc, and bubble them down to @cartesi/client and @cartesi/react. Derived from cartesi/rollups-node#793, which has since merged into next/2.0 as 7558af6, so this is no longer a forward-looking port. It was verified against the merged jsonrpc-discover.json rather than the pull request diff: @cartesi/rpc declares exactly the 28 methods the merged specification defines, and every schema and parameter change between the pre-793 base (9bdd988) and merged next/2.0 is reflected here. Cross-checked against internal/jsonrpc/api/params.go, internal/jsonrpc/jsonrpc.go and internal/model/models.go. New methods: - cartesi_getEpochByVirtualIndex, fetching an epoch by its dense insertion rank (getEpochByVirtualIndex / useEpochByVirtualIndex) - cartesi_getExecutedOutputCount and cartesi_getPendingExecutableOutputCount (getExecutedOutputCount / useExecutedOutputCount, getPendingExecutableOutputCount / usePendingExecutableOutputCount). The executed count is monotone and meant to be polled for change detection; the pending count is a gauge and is not. - cartesi_getNodeInfo, returning the chain id, the node version and the node's default block tag in one call (getNodeInfo / useNodeInfo). It replaces cartesi_getChainId and cartesi_getNodeVersion, which the node deprecated and which are now marked @deprecated here too. New listing filters: - from/to inclusive index ranges on listEpochs, listInputs, listOutputs and listReports - a list of statuses on listEpochs, and a list of output types plus the new executed flag on listOutputs The node rejects an empty status or output_type list with invalid params, so the list-valued filters are typed as NonEmptyArray<T> rather than T[]: `status: []` and `outputType: []` are compile errors instead of failed requests. listOutputs maps the output types to selectors through the head of the list separately, so the result stays non-empty for the type checker, which Array.prototype.map would widen back to Hex[]. The constraint is guarded by a *.test-d.ts suite in @cartesi/client, which needed vitest type testing enabled there — CI runs `pnpm test` but no `tsc --noEmit` over the test files, so without it the constraint would go unchecked. Input completion changes, which landed on next/2.0 alongside but not as part of #793: - InputStatus loses its resource-limit members. The node collapsed OUTPUTS_LIMIT_EXCEEDED, REPORTS_LIMIT_EXCEEDED, CYCLE_LIMIT_EXCEEDED, TIME_LIMIT_EXCEEDED and PAYLOAD_LENGTH_LIMIT_EXCEEDED into the remaining outcomes, leaving NONE, ACCEPTED, REJECTED, EXCEPTION and MACHINE_HALTED. waitForInput listed four of them under rejectErrors; it now aborts on EXCEPTION, MACHINE_HALTED and REJECTED, which is every terminal status other than ACCEPTED — and no longer omits a failure status the way the old list omitted REPORTS_LIMIT_EXCEEDED. - Input gains exception_data / exceptionData, the raw guest-provided CMIO exception payload, non-null only when the status is EXCEPTION and empty-encoded as 0x. The bytes are passed through undecoded, matching how raw_data is handled. Breaking changes: - cartesi_getMatchAdvanced is now cartesi_getMatchAdvance, so the getMatchAdvanced action is getMatchAdvance, the useMatchAdvanced hook is useMatchAdvance and the GetMatchAdvanced* types are GetMatchAdvance* - the node's application-level error codes moved out of the JSON-RPC reserved range (-31001/-31002 instead of -32001/-32002); they are now exported from @cartesi/rpc as errorCodes, along with the new batch (-32040), timeout (-32070), response-size-limit (-31003) and batch-list-work (-31004) codes, plus the maxBatchSize (100), maxBatchListWork (10000) and defaultListLimit (50) constants that bound a batch - InputStatus shrank, as described above Batch requests needed no transport change — the underlying json-rpc-2.0 client already batches — so they are documented rather than implemented, including the two budgets that apply beyond the entry count: the response-size budget closes once exhausted, so every later entry gets -31003 even if its response would still have fit, and the list-work budget rejects the whole batch with a single -31004 before dispatching anything. Neither meters the COUNT queries behind pagination nor offset traversal, so a deep offset over a broad filter can still make the database scan and discard rows before the requested page. Two OpenRPC typing fixes needed no code change: prev_randao and voucher value are now UnsignedInteger256 in the specification, and both were already 256-bit-safe here. Also reformats four @cartesi/react hooks that biome was already reporting as unformatted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UJpa3KXxvUarBGpHvW8Qdb
1 parent 7771392 commit 07e4357

70 files changed

Lines changed: 1662 additions & 173 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/tricky-pianos-shake.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
"@cartesi/rpc": major
3+
"@cartesi/client": major
4+
"@cartesi/react": major
5+
---
6+
7+
Track the JSON-RPC API changes of rollups-node.
8+
9+
New methods:
10+
11+
- `cartesi_getEpochByVirtualIndex` — fetch an epoch by its dense insertion rank, exposed as `getEpochByVirtualIndex` / `useEpochByVirtualIndex`.
12+
- `cartesi_getExecutedOutputCount` and `cartesi_getPendingExecutableOutputCount`, exposed as `getExecutedOutputCount` / `useExecutedOutputCount` and `getPendingExecutableOutputCount` / `usePendingExecutableOutputCount`. The executed count is monotone and meant to be polled for change detection; the pending count is a gauge and is not.
13+
- `cartesi_getNodeInfo` — chain ID, node version and the node's default block tag in one call, exposed as `getNodeInfo` / `useNodeInfo`. It replaces `getChainId` and `getNodeVersion`, which the node deprecated and which are now marked `@deprecated`.
14+
15+
New filters on the listing methods:
16+
17+
- `from` and `to`, an inclusive index range, on `listEpochs`, `listInputs`, `listOutputs` and `listReports`.
18+
- `listEpochs` takes a list of statuses (`status?: EpochStatus | NonEmptyArray<EpochStatus>`), so unsettled epochs can be watched by filtering on the non-terminal ones.
19+
- `listOutputs` takes a list of output types (`outputType?: OutputType | NonEmptyArray<OutputType>`) and a new `executed?: boolean` filter. Because executions happen out of index order, `executed` must not be used to build a resume cursor keyed on the output index — poll `getExecutedOutputCount` instead.
20+
21+
The node rejects an empty filter list with invalid params, so both list-valued filters use the new `NonEmptyArray` type exported by `@cartesi/rpc` (and re-exported by `@cartesi/client`): `status: []` and `outputType: []` are compile errors rather than failed requests.
22+
23+
Input completion changes:
24+
25+
- `InputStatus` lost its resource-limit members. The node collapsed `OUTPUTS_LIMIT_EXCEEDED`, `REPORTS_LIMIT_EXCEEDED`, `CYCLE_LIMIT_EXCEEDED`, `TIME_LIMIT_EXCEEDED` and `PAYLOAD_LENGTH_LIMIT_EXCEEDED` into the remaining outcomes, so the union is now `NONE | ACCEPTED | REJECTED | EXCEPTION | MACHINE_HALTED`. Code that switches on the removed members no longer compiles; `waitForInput` with `rejectErrors` now aborts on `EXCEPTION`, `MACHINE_HALTED` and `REJECTED`, which is every terminal status other than `ACCEPTED`.
26+
- `Input` gained `exceptionData: Hex | null` (`exception_data` on the wire), the raw guest-provided CMIO exception payload. It is non-null only when `status` is `EXCEPTION`, and an empty payload is `0x`. The bytes are passed through undecoded.
27+
28+
Breaking changes:
29+
30+
- `cartesi_getMatchAdvanced` was renamed to `cartesi_getMatchAdvance`, following the node. The `getMatchAdvanced` action is now `getMatchAdvance`, the `useMatchAdvanced` hook is now `useMatchAdvance` (with `matchAdvanceOptions` / `matchAdvanceQueryKey`), and `GetMatchAdvancedParams` / `GetMatchAdvancedReturnType` are now `GetMatchAdvanceParams` / `GetMatchAdvanceReturnType`.
31+
- The node's application-level error codes moved out of the JSON-RPC reserved range: application not found is now `-31002` (was `-32002`) and resource not found is now `-31001` (was `-32001`). `@cartesi/rpc` exports them as `errorCodes`, along with the new batch (`-32040`), timeout (`-32070`), response-size-limit (`-31003`) and batch-list-work (`-31004`) codes, plus the `maxBatchSize`, `maxBatchListWork` and `defaultListLimit` constants that bound a batch.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# getEpochByVirtualIndex
2+
3+
Fetches a single epoch by its virtual index, which is the epoch's dense
4+
insertion rank — 0, 1, 2, … with no gaps by construction. Use it to walk the
5+
epochs of an application contiguously, where the epoch index itself can skip
6+
values.
7+
8+
## Usage
9+
10+
```ts twoslash
11+
import { http } from "viem";
12+
import { createCartesiPublicClient } from "@cartesi/client";
13+
14+
const publicClientL2 = createCartesiPublicClient({
15+
transport: http("http://127.0.0.1:6751/rpc"),
16+
});
17+
18+
const epoch = await publicClientL2.getEpochByVirtualIndex({
19+
application: "0x...",
20+
virtualIndex: 42n,
21+
});
22+
```
23+
24+
## Parameters
25+
26+
```ts twoslash
27+
import type { GetEpochByVirtualIndexParams } from "@cartesi/client";
28+
// {
29+
// application: Address | string;
30+
// virtualIndex: bigint;
31+
// }
32+
```
33+
34+
## Return Type
35+
36+
```ts twoslash
37+
import type { GetEpochByVirtualIndexReturnType, Epoch } from "@cartesi/client";
38+
// GetEpochByVirtualIndexReturnType = Epoch;
39+
```
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# getExecutedOutputCount
2+
3+
Number of outputs of an application that were already executed.
4+
5+
The count is monotone, so an unchanged value means no new executions have been
6+
observed and it is a sound change signal to poll. When it changes, re-query the
7+
bounded executable-output working set with
8+
[`listOutputs`](/client/listOutputs) using `executed: false` and
9+
`outputType: ["Voucher", "DelegateCallVoucher"]`, then diff that pending set
10+
against the previous result to identify the executions.
11+
12+
Neither this count, nor an output index, nor a pagination offset is a valid
13+
resume cursor: executions are observed out of output-index order, so a late
14+
execution can land behind one. The node documents a race-free execution cursor
15+
as expected in a future ingestion API.
16+
17+
## Usage
18+
19+
```ts twoslash
20+
import { http } from "viem";
21+
import { createCartesiPublicClient } from "@cartesi/client";
22+
23+
const publicClientL2 = createCartesiPublicClient({
24+
transport: http("http://127.0.0.1:6751/rpc"),
25+
});
26+
27+
const executedOutputCount = await publicClientL2.getExecutedOutputCount({
28+
application: "0x...",
29+
});
30+
```
31+
32+
## Parameters
33+
34+
```ts twoslash
35+
import type { GetExecutedOutputCountParams } from "@cartesi/client";
36+
// {
37+
// application: Address | string;
38+
// }
39+
```
40+
41+
## Return Type
42+
43+
```ts twoslash
44+
import type { GetExecutedOutputCountReturnType } from "@cartesi/client";
45+
// GetExecutedOutputCountReturnType = bigint;
46+
```

apps/docs/pages/client/getMatchAdvanced.mdx renamed to apps/docs/pages/client/getMatchAdvance.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# getMatchAdvanced
1+
# getMatchAdvance
22

33
## Usage
44

@@ -10,7 +10,7 @@ const publicClientL2 = createCartesiPublicClient({
1010
transport: http("http://127.0.0.1:6751/rpc"),
1111
});
1212

13-
const matchAdvanced = await publicClientL2.getMatchAdvanced({
13+
const matchAdvance = await publicClientL2.getMatchAdvance({
1414
application: "0x...",
1515
epochIndex: 1n,
1616
tournamentAddress: "0x...",
@@ -22,7 +22,7 @@ const matchAdvanced = await publicClientL2.getMatchAdvanced({
2222
## Parameters
2323

2424
```ts twoslash
25-
import type { GetMatchAdvancedParams } from "@cartesi/client";
25+
import type { GetMatchAdvanceParams } from "@cartesi/client";
2626
// {
2727
// application: Address | string;
2828
// epochIndex: bigint;
@@ -35,6 +35,6 @@ import type { GetMatchAdvancedParams } from "@cartesi/client";
3535
## Return Type
3636

3737
```ts twoslash
38-
import type { GetMatchAdvancedReturnType, MatchAdvanced } from "@cartesi/client";
39-
// GetMatchAdvancedReturnType = MatchAdvanced;
38+
import type { GetMatchAdvanceReturnType, MatchAdvanced } from "@cartesi/client";
39+
// GetMatchAdvanceReturnType = MatchAdvanced;
4040
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# getNodeInfo
2+
3+
Fetches the chain ID, the semantic node version and the blockchain block tag the
4+
node reads the base layer at, in a single call.
5+
6+
It replaces `getChainId` and `getNodeVersion`, which are both deprecated.
7+
8+
## Usage
9+
10+
```ts twoslash
11+
import { http } from "viem";
12+
import { createCartesiPublicClient } from "@cartesi/client";
13+
14+
const publicClientL2 = createCartesiPublicClient({
15+
transport: http("http://127.0.0.1:6751/rpc"),
16+
});
17+
18+
const { chainId, version, defaultBlock } = await publicClientL2.getNodeInfo();
19+
```
20+
21+
## Return Type
22+
23+
```ts twoslash
24+
import type { GetNodeInfoReturnType, NodeInfo } from "@cartesi/client";
25+
// NodeInfo = {
26+
// chainId: number;
27+
// version: string;
28+
// defaultBlock: DefaultBlock; // FINALIZED | SAFE | LATEST | PENDING
29+
// }
30+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# getPendingExecutableOutputCount
2+
3+
Number of executable outputs of an application that are still pending execution.
4+
5+
This is a gauge, not a change signal or a resume cursor: it grows with new
6+
executable outputs and shrinks with executions, so it can return to a previous
7+
value while work happened in between. Poll
8+
[`getExecutedOutputCount`](/client/getExecutedOutputCount) instead and diff the
9+
pending set on change.
10+
11+
## Usage
12+
13+
```ts twoslash
14+
import { http } from "viem";
15+
import { createCartesiPublicClient } from "@cartesi/client";
16+
17+
const publicClientL2 = createCartesiPublicClient({
18+
transport: http("http://127.0.0.1:6751/rpc"),
19+
});
20+
21+
const pending = await publicClientL2.getPendingExecutableOutputCount({
22+
application: "0x...",
23+
});
24+
```
25+
26+
## Parameters
27+
28+
```ts twoslash
29+
import type { GetPendingExecutableOutputCountParams } from "@cartesi/client";
30+
// {
31+
// application: Address | string;
32+
// }
33+
```
34+
35+
## Return Type
36+
37+
```ts twoslash
38+
import type { GetPendingExecutableOutputCountReturnType } from "@cartesi/client";
39+
// GetPendingExecutableOutputCountReturnType = bigint;
40+
```

apps/docs/pages/client/listEpochs.mdx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { data, pagination } = await publicClientL2.listEpochs({
1414
application: "0x...",
1515
limit: 10,
1616
offset: 0,
17-
// status (optional)
17+
// status, from, to (optional)
1818
});
1919
```
2020

@@ -24,7 +24,9 @@ const { data, pagination } = await publicClientL2.listEpochs({
2424
import type { ListEpochsParams } from "@cartesi/client";
2525
// {
2626
// application: Address | string;
27-
// status?: EpochStatus;
27+
// status?: EpochStatus | NonEmptyArray<EpochStatus>;
28+
// from?: bigint;
29+
// to?: bigint;
2830
// limit?: number;
2931
// offset?: number;
3032
// descending?: boolean;
@@ -40,3 +42,7 @@ import type { ListEpochsReturnType, Epoch, Pagination } from "@cartesi/client";
4042
// pagination: Pagination;
4143
// }
4244
```
45+
46+
The node rejects an empty status list with invalid params, so `status` is typed
47+
as a non-empty array — `status: []` is a compile error rather than a failed
48+
request.

apps/docs/pages/client/listInputs.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { data, pagination } = await publicClientL2.listInputs({
1414
application: "0x...",
1515
limit: 10,
1616
offset: 0,
17-
// epochIndex, sender (optional)
17+
// epochIndex, sender, from, to (optional)
1818
});
1919
```
2020

@@ -27,6 +27,8 @@ import type { ListInputsParams } from "@cartesi/client";
2727
// epochIndex?: bigint;
2828
// sender?: Address;
2929
// transactionHash?: Hash;
30+
// from?: bigint;
31+
// to?: bigint;
3032
// limit?: number;
3133
// offset?: number;
3234
// descending?: boolean;

apps/docs/pages/client/listOutputs.mdx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { data, pagination } = await publicClientL2.listOutputs({
1414
application: "0x...",
1515
limit: 10,
1616
offset: 0,
17-
// epochIndex, inputIndex, outputType, voucherAddress (optional)
17+
// epochIndex, inputIndex, outputType, voucherAddress, from, to, executed (optional)
1818
});
1919
```
2020

@@ -26,14 +26,45 @@ import type { ListOutputsParams } from "@cartesi/client";
2626
// application: Address | string;
2727
// epochIndex?: bigint;
2828
// inputIndex?: bigint;
29-
// outputType?: OutputType;
29+
// outputType?: OutputType | NonEmptyArray<OutputType>;
3030
// voucherAddress?: Address;
31+
// from?: bigint;
32+
// to?: bigint;
33+
// executed?: boolean;
3134
// limit?: number;
3235
// offset?: number;
3336
// descending?: boolean;
3437
// }
3538
```
3639

40+
`outputType` accepts a list, and `executed` filters by execution status, so the
41+
outputs still waiting to be executed can be fetched in one call. The node
42+
rejects an empty list with invalid params, so the list is typed as a non-empty
43+
array — `outputType: []` is a compile error rather than a failed request:
44+
45+
```ts twoslash
46+
import { http } from "viem";
47+
import { createCartesiPublicClient } from "@cartesi/client";
48+
49+
const publicClientL2 = createCartesiPublicClient({
50+
transport: http("http://127.0.0.1:6751/rpc"),
51+
});
52+
// ---cut---
53+
const { data: pending } = await publicClientL2.listOutputs({
54+
application: "0x...",
55+
outputType: ["Voucher", "DelegateCallVoucher"],
56+
executed: false,
57+
});
58+
```
59+
60+
::::warning
61+
Executions happen out of index order — an old voucher can be executed long after
62+
newer ones — so a resume cursor keyed on the output index over this filter
63+
silently skips those late executions. Poll
64+
[`getExecutedOutputCount`](/client/getExecutedOutputCount) for change detection
65+
instead.
66+
::::
67+
3768
## Return Type
3869

3970
```ts twoslash

apps/docs/pages/client/listReports.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { data, pagination } = await publicClientL2.listReports({
1414
application: "0x...",
1515
limit: 10,
1616
offset: 0,
17-
// epochIndex, inputIndex (optional)
17+
// epochIndex, inputIndex, from, to (optional)
1818
});
1919
```
2020

@@ -26,6 +26,8 @@ import type { ListReportsParams } from "@cartesi/client";
2626
// application: Address | string;
2727
// epochIndex?: bigint;
2828
// inputIndex?: bigint;
29+
// from?: bigint;
30+
// to?: bigint;
2931
// limit?: number;
3032
// offset?: number;
3133
// descending?: boolean;

0 commit comments

Comments
 (0)