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
5 changes: 5 additions & 0 deletions .changeset/sequencer-user-ops-decoder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@deroll/decoder": minor
---

Add the `userOp` decode method and the `UserOp` record: the payload of one user operation inside a Cartesi sequencer batch, with the batch envelope and the recovered signer already decoded by the explorer.
23 changes: 20 additions & 3 deletions apps/docs/pages/explorer/decoders.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ optional); each method receives the full API record:

| method | record | bytes decoded |
| --- | --- | --- |
| `input` | `Input` | `input.decodedData.payload` — the advance payload sent by the user. Never called for portal deposits: the explorer decodes those itself |
| `input` | `Input` | `input.decodedData.payload` — the advance payload sent by the user. Never called for portal deposits or sequencer batches: the explorer decodes those itself |
| `deposit` | `Deposit` | the app-specific data attached to a portal deposit (`execLayerData`, and `baseLayerData` on the NFT portals; `"0x"` when absent). The deposit envelope — asset, amounts, sender — arrives already decoded |
| `output` | `Output` | `output.decodedData.payload` — the payload of a Notice, Voucher or DelegateCallVoucher |
| `report` | `Report` | `report.rawData` — the full report body (inspect responses, error messages, …) |
| `withdrawalAccount` | `Withdrawal` | `withdrawal.account` — the account encoding produced by the app's `WithdrawalOutputBuilder` |
| `withdrawalOutput` | `Withdrawal` | `withdrawal.output` — the raw output blob emitted for that account |
| `userOp` | `UserOp` | `userOp.data` — the payload of one user operation inside a sequencer batch. The batch envelope — frames, nonces, fees, recovered signer — arrives already decoded |

An exported method is also the capability signal: the explorer only calls what you
export and falls back to its hex/UTF-8 view for everything else, so new payload sources
Expand All @@ -32,6 +33,22 @@ and the structured deposit natively — with or without a registered decoder. Yo
`deposit` method only decodes the app-specific bytes riding inside, and its result is
shown alongside the native deposit view.

## Sequencer user operations

With a Cartesi sequencer, users sign operations off-chain and a batch submitter posts them to the InputBox in batches. The batch envelope is protocol-defined, so once the application's **batch submitter** address is set on its **Overview** page, the explorer recognizes batches by sender, decodes them, recovers each operation's signer and unfolds the operations under their input — `input` is never called for a batch. Each operation's `data` is application-defined, and that is what your `userOp` method decodes:

```ts
import type { UserOpDecoder } from "@deroll/decoder";

export const userOp: UserOpDecoder = (op, context) => {
// op.signer is the recovered signer (null when unrecoverable), and
// op.nonce, op.maxFee, op.frameIndex, … describe the operation.
return { summary: `${op.signer ?? "unknown signer"} · ${op.data}` };
};
```

A decoded operation was **submitted**, not necessarily executed: the scheduler may skip a whole batch or drop an operation (unrecoverable signature, fee or application validation), and none of that is visible in the batch bytes alone. Don't word a summary as if the operation took effect.

## What the kit gives you

The [`@deroll/decoder`](https://www.npmjs.com/package/@deroll/decoder) package is
Expand Down Expand Up @@ -117,8 +134,8 @@ export const deposit: DepositDecoder = (deposit, context) => {
```

Each method has a named type — `InputDecoder`, `DepositDecoder`, `OutputDecoder`,
`ReportDecoder`, `WithdrawalAccountDecoder`, `WithdrawalOutputDecoder` — and the
`Decoder` interface types a whole module.
`ReportDecoder`, `WithdrawalAccountDecoder`, `WithdrawalOutputDecoder`,
`UserOpDecoder` — and the `Decoder` interface types a whole module.

## Loading from a GitHub source (no publish)

Expand Down
6 changes: 4 additions & 2 deletions apps/explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"lint": "biome check"
"lint": "biome check",
"test": "vitest"
},
"dependencies": {
"@cartesi/client": "2.0.0-alpha.38",
Expand All @@ -29,6 +30,7 @@
"@vitejs/plugin-react": "^6.1.2",
"tailwindcss": "^4.3.3",
"typescript": "^7.0.2",
"vite": "^8.3.4"
"vite": "^8.3.4",
"vitest": "^5.0.4"
}
}
1 change: 1 addition & 0 deletions apps/explorer/scripts/fixturegen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
Loading
Loading