@rootzero/contracts 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +45 -3
  2. package/Codec.sol +21 -0
  3. package/Commands.sol +14 -0
  4. package/Core.sol +2 -3
  5. package/Endpoints.sol +1 -5
  6. package/README.md +34 -30
  7. package/Utils.sol +2 -4
  8. package/codec/Blocks.sol +1606 -0
  9. package/codec/Buffers.sol +165 -0
  10. package/codec/Decoders.sol +558 -0
  11. package/codec/Descriptors.sol +124 -0
  12. package/{blocks → codec}/Keys.sol +4 -16
  13. package/codec/Readers.sol +114 -0
  14. package/{blocks → codec}/Schema.sol +38 -80
  15. package/codec/Specs.sol +218 -0
  16. package/codec/Writers.sol +487 -0
  17. package/commands/Allocate.sol +21 -20
  18. package/commands/Base.sol +71 -37
  19. package/commands/Burn.sol +18 -13
  20. package/commands/Credit.sol +21 -20
  21. package/commands/Debit.sol +25 -28
  22. package/commands/Deposit.sol +34 -35
  23. package/commands/Payout.sol +21 -15
  24. package/commands/Provision.sol +37 -38
  25. package/commands/Recover.sol +20 -19
  26. package/commands/Relay.sol +24 -22
  27. package/commands/Withdraw.sol +15 -18
  28. package/commands/admin/AllowAssets.sol +19 -16
  29. package/commands/admin/Allowance.sol +18 -13
  30. package/commands/admin/Appoint.sol +17 -15
  31. package/commands/admin/Authorize.sol +23 -14
  32. package/commands/admin/Base.sol +1 -1
  33. package/commands/admin/DenyAssets.sol +19 -16
  34. package/commands/admin/Dismiss.sol +17 -15
  35. package/commands/admin/Execute.sol +20 -19
  36. package/commands/admin/Label.sol +17 -13
  37. package/commands/admin/Schemas.sol +18 -14
  38. package/commands/admin/Unauthorize.sol +23 -14
  39. package/core/Calls.sol +3 -11
  40. package/core/Endpoint.sol +42 -127
  41. package/core/Pipeline.sol +16 -15
  42. package/core/Types.sol +1 -1
  43. package/docs/Schema.md +35 -29
  44. package/events/Endpoint.sol +2 -2
  45. package/events/Schema.sol +5 -5
  46. package/execution/Budget.sol +40 -0
  47. package/execution/Execution.sol +1083 -0
  48. package/guards/Base.sol +8 -9
  49. package/guards/Revoke.sol +11 -9
  50. package/package.json +1 -1
  51. package/ports/AllowAssets.sol +12 -15
  52. package/ports/Allowance.sol +11 -9
  53. package/ports/Base.sol +18 -11
  54. package/ports/Credit.sol +11 -9
  55. package/ports/Debit.sol +11 -9
  56. package/ports/DenyAssets.sol +10 -13
  57. package/ports/Dispatch.sol +13 -15
  58. package/ports/Pipe.sol +14 -12
  59. package/ports/Redeem.sol +12 -9
  60. package/ports/Settle.sol +11 -9
  61. package/queries/Assets.sol +15 -15
  62. package/queries/Balances.sol +17 -17
  63. package/queries/Base.sol +11 -12
  64. package/utils/Actions.sol +1 -0
  65. package/utils/Cursors.sol +308 -0
  66. package/utils/Lanes.sol +14 -0
  67. package/utils/Selectors.sol +2 -2
  68. package/utils/Utils.sol +46 -0
  69. package/Cursors.sol +0 -16
  70. package/blocks/Cursors.sol +0 -1529
  71. package/blocks/Writers.sol +0 -1036
  72. package/core/Payable.sol +0 -53
  73. package/utils/Value.sol +0 -43
package/CHANGELOG.md CHANGED
@@ -3,6 +3,48 @@
3
3
  Until the protocol reaches integration-stable status, minor versions may include
4
4
  breaking API changes. Breaking changes are called out explicitly.
5
5
 
6
+ ## 1.14.0
7
+
8
+ ### Breaking Changes
9
+
10
+ - Replaced `CommandContext` and the separate payable value lifecycle with the
11
+ unified `Execution` context. Endpoint and command implementations now open an
12
+ execution directly from calldata, use its packed decoder and writer lanes,
13
+ and finish through the shared `close` helpers.
14
+ - Reworked the block codec around absolute-position `Blocks` primitives,
15
+ packed `Cursors`, cursor-backed `Decoders`, lazy `Buffers`, and thin
16
+ `Writers`. Several low-level cursor and writer APIs were renamed or removed.
17
+ - Redefined block specs and endpoint descriptors. Specs now encode key, minimum,
18
+ maximum, allocation hint, stride, and optional LIST container metadata;
19
+ descriptors use normalized lane specs and include a transaction stride.
20
+ - Replaced the `Cursors.sol` package entry point with `Codec.sol`, added the
21
+ command-authoring `Commands.sol` entry point, and reorganized exports across
22
+ the package barrels.
23
+ - Endpoint selectors are now derived from endpoint names. The configured name
24
+ must match the implementing function name, and descriptor values are
25
+ represented as `uint` throughout.
26
+ - Removed the AUTH and BOUNTY codec blocks, the obsolete `Payable`/`Values`
27
+ helpers, and superseded decoder, writer, schema, and descriptor overloads.
28
+
29
+ ### Added
30
+
31
+ - Added packed output and transaction writer lanes to `Execution`, including
32
+ semantic output helpers, queued credit/debit transactions, budget refunds,
33
+ and direct transaction finalization.
34
+ - Added detachable `Budget` values for pipeline-style consumers, shared lane
35
+ identifiers, spec-driven writer allocation, and optimized semantic block
36
+ readers, writers, and composite unpackers.
37
+ - Added a transaction-output example, command and codec barrel import examples,
38
+ and expanded coverage for packed cursors, buffers, descriptors, budgets,
39
+ execution output, and command flows.
40
+
41
+ ### Changed
42
+
43
+ - Standardized command, query, guard, and port implementations on the same
44
+ execution open/close lifecycle and renamed request terminology to input.
45
+ - Expanded NatSpec across the new execution and codec APIs and refreshed the
46
+ protocol, schema, indexing, and multi-chain documentation.
47
+
6
48
  ## 1.13.0
7
49
 
8
50
  ### Breaking Changes
@@ -88,7 +130,7 @@ breaking API changes. Breaking changes are called out explicitly.
88
130
  - Portal no longer emits `Recovered` when retrying an undelivered witness.
89
131
  - Renamed `Cursors.exit` to `ensureAt` and renamed its position argument to
90
132
  `pos`.
91
- - Removed the redundant `next` return value from both `Cursors.init` overloads;
133
+ - Removed the redundant `next` return value from both `Decoders.init` overloads;
92
134
  callers should use the returned cursor's `len` as the run boundary.
93
135
  - Changed `Cursors.list` to require the expected current cursor position as
94
136
  `pos` before entering the LIST block.
@@ -163,7 +205,7 @@ breaking API changes. Breaking changes are called out explicitly.
163
205
  as the discovery/event surface for dispatch tracking.
164
206
  - Added `ContextRecovery` schema/cursor support and context schema aliases for
165
207
  reusable nested block schemas.
166
- - Added `Values.drain`, `Payable.openValue`, and `Payable.closeValue` to make
208
+ - Added `Values.drain`, `Payable.openValue`, and `Payable.end` to make
167
209
  payable command budget lifecycles explicit.
168
210
 
169
211
  ## 1.6.0
@@ -223,7 +265,7 @@ breaking API changes. Breaking changes are called out explicitly.
223
265
 
224
266
  ### Breaking Changes
225
267
 
226
- - Simplified `Cursors.init` to parse a single run from the start of a calldata slice. Callers that previously passed an offset must slice first or use `Cursors.open(source, i)`.
268
+ - Simplified `Decoders.init` to parse a single run from the start of a calldata slice. Callers that previously passed an offset must slice first or use `Decoders.open(source, i)`.
227
269
  - Tightened command, query, and peer request parsing around the single-run convention used by current protocol endpoints.
228
270
 
229
271
  ### Added
package/Codec.sol ADDED
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ // Aggregator: re-exports the complete block encoding and decoding surface.
5
+ // Import this file for low-level codec extensions and direct stream processing.
6
+
7
+ import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
8
+ import { Keys } from "./codec/Keys.sol";
9
+ import { Sizes, Specs } from "./codec/Specs.sol";
10
+ import { Descriptors } from "./codec/Descriptors.sol";
11
+ import { Schemas } from "./codec/Schema.sol";
12
+ import { Decoders } from "./codec/Decoders.sol";
13
+ import { Cursors, Cur } from "./utils/Cursors.sol";
14
+ import { Readers, Reader } from "./codec/Readers.sol";
15
+ import { Blocks } from "./codec/Blocks.sol";
16
+ import { Buffers } from "./codec/Buffers.sol";
17
+ import { Writer, Writers } from "./codec/Writers.sol";
18
+
19
+
20
+
21
+
package/Commands.sol ADDED
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ // Aggregator: re-exports the types and helpers needed to author commands.
5
+ // Import this file for both standard Execution-based commands and custom decoders.
6
+
7
+ import {CommandBase} from "./commands/Base.sol";
8
+ import {Execution, Executions} from "./execution/Execution.sol";
9
+ import {Lanes} from "./utils/Lanes.sol";
10
+ import {Blocks} from "./codec/Blocks.sol";
11
+ import {Sizes, Specs} from "./codec/Specs.sol";
12
+ import {Decoders} from "./codec/Decoders.sol";
13
+ import {Cursors, Cur} from "./utils/Cursors.sol";
14
+ import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx} from "./core/Types.sol";
package/Core.sol CHANGED
@@ -10,12 +10,11 @@ import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
10
10
  import { NativeAsset, Runtime } from "./core/Runtime.sol";
11
11
  import { Host, IHostIntroduction } from "./core/Host.sol";
12
12
  import { CommandCalls, FailedCall, NodeCalls, PortCalls } from "./core/Calls.sol";
13
- import { EndpointBase, Lane } from "./core/Endpoint.sol";
14
- import { Payable } from "./core/Payable.sol";
13
+ import { EndpointBase } from "./core/Endpoint.sol";
15
14
  import { Pipeline } from "./core/Pipeline.sol";
15
+ import { Budget, Budgets } from "./execution/Budget.sol";
16
16
  import { CreditAccountHook, DebitAccountHook, Settlement } from "./core/Settlement.sol";
17
17
  import { Portal } from "./core/Portal.sol";
18
- import { RecoverHook } from "./commands/Recover.sol";
19
18
  import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
20
19
  import { Validator } from "./core/Validator.sol";
21
20
 
package/Endpoints.sol CHANGED
@@ -4,11 +4,7 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports command, admin, port, guard, and query endpoint abstractions.
5
5
  // Import this file to inherit from the full rootzero callable host surface without managing individual paths.
6
6
 
7
- // Shared helpers
8
- import { Keys } from "./blocks/Keys.sol";
9
- import { CommandBase, CommandContext } from "./commands/Base.sol";
10
- import { EndpointBase, Lane } from "./core/Endpoint.sol";
11
- import { Payable } from "./core/Payable.sol";
7
+ // Shared endpoint hooks
12
8
  import { CreditAccountHook, DebitAccountHook } from "./core/Settlement.sol";
13
9
 
14
10
  // Commands
package/README.md CHANGED
@@ -10,7 +10,7 @@ of the protocol: the base contracts, block codecs, and helpers that rootzero
10
10
  applications compose.
11
11
 
12
12
  Two decisions shape everything below. First, all data that crosses a host
13
- boundary is encoded in one binary block format, so a request means the same
13
+ boundary is encoded in one binary block format, so a input means the same
14
14
  bytes on every chain. Second, every surface operates on *runs* of blocks rather
15
15
  than single values, so batching is the default, not a feature added later. This
16
16
  guide introduces the protocol bottom-up: blocks, then identities, then hosts
@@ -48,7 +48,7 @@ contract ExampleHost is Host, Balances, Deposit {
48
48
  ```
49
49
 
50
50
  Deploy it with your own address as commander and you can call its commands
51
- directly. A request is a run of binary blocks — here, a single `#amount` block
51
+ directly. A input is a run of binary blocks — here, a single `#amount` block
52
52
  asking to deposit an asset (the encoders are a few lines each; see
53
53
  [`test/helpers/blocks.ts`](test/helpers/blocks.ts) for reference
54
54
  implementations):
@@ -57,8 +57,8 @@ implementations):
57
57
  const host = await ethers.deployContract("ExampleHost", [deployer.address]);
58
58
 
59
59
  const account = encodeUserAccount(user.address); // receiving account
60
- const request = encodeAmountBlock(asset, 100n); // what to deposit
61
- await host.deposit({ account, state: "0x", input: request }); // emits Balance
60
+ const input = encodeAmountBlock(asset, 100n); // what to deposit
61
+ await host.deposit({ account, state: "0x", input: input }); // emits Balance
62
62
  ```
63
63
 
64
64
  The rest of this guide explains the ideas this example leans on — blocks, IDs,
@@ -66,7 +66,7 @@ hosts, commands — and the surfaces built on top of them.
66
66
 
67
67
  ## Blocks
68
68
 
69
- Every request, response, and piece of in-flight state is a stream of typed
69
+ Every input, response, and piece of in-flight state is a stream of typed
70
70
  blocks. A block is a four-byte key, a four-byte big-endian length, and a
71
71
  payload:
72
72
 
@@ -85,7 +85,7 @@ amount { bytes32 asset, uint amount }
85
85
  is 72 bytes on the wire: an 8-byte header followed by two big-endian 32-byte
86
86
  fields. There is no ABI encoding and no chain-specific type anywhere in the
87
87
  format — field types are chain-neutral integers, bytes, and booleans. A deposit
88
- request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
88
+ input built for an EVM host is byte-for-byte the input a CosmWasm or Solana
89
89
  port would parse; what differs per chain is how a host *resolves* the
90
90
  identifiers inside, never how the bytes are laid out.
91
91
 
@@ -96,11 +96,11 @@ paths give off-chain tooling presentation names without changing a single byte
96
96
  on the wire. The full schema language is specified in
97
97
  [`docs/Schema.md`](docs/Schema.md). The standard block schemas live in
98
98
  `Schemas` and their runtime keys in `Keys` (both via
99
- `@rootzero/contracts/Cursors.sol`).
99
+ `@rootzero/contracts/Codec.sol`).
100
100
 
101
101
  ## Batches
102
102
 
103
- A request is not a single struct; it is a run of blocks. One `#amount` block
103
+ A input is not a single struct; it is a run of blocks. One `#amount` block
104
104
  asks for one deposit, five blocks ask for five, and the code path is identical
105
105
  — every endpoint parses with a cursor and loops until the stream is exhausted.
106
106
  The descriptor lane key is the prime item: it is the block type that may repeat
@@ -117,15 +117,15 @@ Off-chain, building a batch is concatenation. Using the reference encoders from
117
117
  import { concat } from "ethers";
118
118
  import { encodeAmountBlock } from "./helpers/blocks";
119
119
 
120
- const request = concat([
120
+ const input = concat([
121
121
  encodeAmountBlock(usdc, 250_000_000n),
122
122
  encodeAmountBlock(dai, 250n * 10n ** 18n),
123
123
  ]);
124
- // deposit(request) returns two #balance blocks in its state output and an
124
+ // deposit(input) returns two #balance blocks in its state output and an
125
125
  // empty transaction output
126
126
  ```
127
127
 
128
- Everything downstream keeps this shape: commands loop over request blocks,
128
+ Everything downstream keeps this shape: commands loop over input blocks,
129
129
  settlement loops over transactions, pipelines loop over steps. Batching is
130
130
  never a special case.
131
131
 
@@ -242,7 +242,7 @@ function deposit(
242
242
  output.appendBalance(asset, amount);
243
243
  }
244
244
 
245
- return (output.finish(), "");
245
+ return (end(output), "");
246
246
  }
247
247
  ```
248
248
 
@@ -252,16 +252,18 @@ lanes, derived group sizes, and flags, plus a human-readable label:
252
252
 
253
253
  ```solidity
254
254
  abstract contract MyCommand is CommandBase {
255
- bytes32 private immutable descriptor;
255
+ uint private immutable descriptor;
256
256
 
257
257
  constructor() {
258
- (, descriptor) = command("myCommand", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
258
+ (, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
259
259
  }
260
260
 
261
261
  function myCommand(
262
- CommandContext calldata c
262
+ bytes32 account,
263
+ bytes calldata state,
264
+ bytes calldata input
263
265
  ) external onlyCommand returns (bytes memory, bytes memory) {
264
- // parse c.input, loop, return the output state run and any transactions
266
+ // parse input, loop, return the output state run and any transactions
265
267
  }
266
268
  }
267
269
  ```
@@ -279,25 +281,25 @@ A single command is rarely the whole story. A pipeline is a run of `#step`
279
281
  blocks executed in order within one transaction:
280
282
 
281
283
  ```txt
282
- step { uint target, uint resources, #bytes as request }
284
+ step { uint cmd, uint resources, #bytes as input }
283
285
  ```
284
286
 
285
- Each step names a target command, the resources it may spend, and its request.
287
+ Each step names a command, the resources it may spend, and its input.
286
288
  The returned state threads into the next command and the final state must be
287
289
  empty. Returned transactions do not enter the state lane; the pipeline passes
288
290
  each decoded transaction to the shared settlement implementation before
289
291
  running the next step. This is the core of `Pipeline.pipe`:
290
292
 
291
293
  ```solidity
292
- while (input.i < input.len) {
293
- (uint target, uint resources, bytes calldata request) = input.unpackStep();
294
+ while (cur.more()) {
295
+ (uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
294
296
  Reader memory transactions;
295
297
  (state, transactions.source) = dispatch(
296
- target,
298
+ cmd,
297
299
  account,
298
300
  state,
299
- request,
300
- useValue(budget, resources)
301
+ input,
302
+ budget.use(resources)
301
303
  );
302
304
  while (transactions.more()) {
303
305
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = transactions.unpackTransaction();
@@ -308,7 +310,7 @@ if (state.length != 0) revert UnexpectedState();
308
310
  ```
309
311
 
310
312
  A transfer, for instance, is a two-step pipeline: `debitAccount` turns an
311
- `#amount` request into `#balance` state, and `payout` consumes that state
313
+ `#amount` input into `#balance` state, and `payout` consumes that state
312
314
  toward a recipient. Because a pipeline is just blocks, it is also the unit of
313
315
  command batching — and `resources` is a chain-specific word interpreted by the
314
316
  portal adapter (on EVM, the low 128 bits are native value in wei, drawn from a
@@ -316,13 +318,13 @@ shared budget), so the same pipeline bytes are meaningful to every port.
316
318
 
317
319
  ## Queries
318
320
 
319
- Queries are the read endpoints: view functions that take a block-stream request
321
+ Queries are the read endpoints: view functions that take a block-stream input
320
322
  and return a block-stream response, with the same batch shape as commands. The
321
323
  standard `getBalances` query takes a run of positions and answers each one in
322
324
  order:
323
325
 
324
326
  ```txt
325
- request: accountAsset { bytes32 account, bytes32 asset }
327
+ input: accountAsset { bytes32 account, bytes32 asset }
326
328
  response: accountAmount { bytes32 account, bytes32 asset, uint amount }
327
329
  ```
328
330
 
@@ -345,8 +347,8 @@ This is also the cross-portal mechanism. `relayPayable` (or `portDispatchPayable
345
347
  wraps a pipe and addresses it to a portal, commonly the destination host ID;
346
348
  a bridge adapter moves the **raw
347
349
  bytes**; the destination host parses them with the same cursor rules and runs
348
- the same pipeline loop. Nothing in the payload is EVM-specific — step targets
349
- are destination-local node IDs, and only the adapter boundary (native
350
+ the same pipeline loop. Nothing in the payload is EVM-specific — step commands
351
+ are destination-local command IDs, and only the adapter boundary (native
350
352
  transfers, address resolution, signatures) is chain-specific. The parity rule
351
353
  for ports is strict: every chain's implementation must parse the same input
352
354
  bytes and produce the same output bytes for every endpoint.
@@ -377,10 +379,12 @@ Import from the package entry points rather than deep paths:
377
379
 
378
380
  - `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
379
381
  `Settlement`, `Pipeline`, `Portal`, validator
382
+ - `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, codec
383
+ helpers, and shared value types for authoring custom commands
380
384
  - `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
381
385
  mixins and their hooks
382
- - `@rootzero/contracts/Cursors.sol` — calldata `Cur`/`Cursors`, memory
383
- `Reader`/`Readers`, `Writers`, `Schemas`, `Keys`
386
+ - `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
387
+ `Reader`/`Readers`, `Writers`, `Schemas`, `Keys`, and `Specs`
384
388
  - `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
385
389
  layout and value helpers
386
390
  - `@rootzero/contracts/Events.sol` — protocol event contracts
package/Utils.sol CHANGED
@@ -1,10 +1,10 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- // Aggregator: re-exports all utility libraries (Keys, Accounts, Actions, Assets, ECDSA, Ids, Nodes, Selectors, Layout, Utils, Value).
4
+ // Aggregator: re-exports protocol identifier, asset, account, cryptographic,
5
+ // layout, and general-purpose utility helpers.
5
6
  // Import this file to access the full utility surface without managing individual paths.
6
7
 
7
- import { Keys } from "./blocks/Keys.sol";
8
8
  import { Accounts } from "./utils/Accounts.sol";
9
9
  import { Actions } from "./utils/Actions.sol";
10
10
  import { Amounts, Assets } from "./utils/Assets.sol";
@@ -13,9 +13,7 @@ import { Ids } from "./utils/Ids.sol";
13
13
  import { Nodes } from "./utils/Nodes.sol";
14
14
  import { Selectors } from "./utils/Selectors.sol";
15
15
  import { Layout } from "./utils/Layout.sol";
16
- import { Schemas } from "./blocks/Schema.sol";
17
16
  import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, retryTicket, toLocalBase, toUnspecifiedBase, ValueOverflow } from "./utils/Utils.sol";
18
- import { Budget, Values } from "./utils/Value.sol";
19
17
 
20
18
 
21
19