@rootzero/contracts 1.13.0 → 1.15.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 (86) hide show
  1. package/CHANGELOG.md +87 -3
  2. package/Codec.sol +21 -0
  3. package/Commands.sol +14 -0
  4. package/Core.sol +8 -6
  5. package/Endpoints.sol +2 -7
  6. package/Events.sol +1 -2
  7. package/README.md +70 -37
  8. package/Utils.sol +2 -4
  9. package/annotations/Action.sol +17 -0
  10. package/annotations/Label.sol +23 -0
  11. package/annotations/Schema.sol +53 -0
  12. package/codec/Blocks.sol +1643 -0
  13. package/codec/Buffers.sol +165 -0
  14. package/codec/Decoders.sol +570 -0
  15. package/codec/Descriptors.sol +124 -0
  16. package/{blocks → codec}/Keys.sol +10 -18
  17. package/codec/Readers.sol +114 -0
  18. package/{blocks → codec}/Schema.sol +43 -80
  19. package/codec/Specs.sol +240 -0
  20. package/codec/Writers.sol +485 -0
  21. package/commands/Allocate.sol +21 -20
  22. package/commands/Base.sol +83 -45
  23. package/commands/Burn.sol +23 -14
  24. package/commands/Credit.sol +21 -20
  25. package/commands/Debit.sol +25 -28
  26. package/commands/Deposit.sol +41 -36
  27. package/commands/Payout.sol +26 -16
  28. package/commands/Provision.sol +37 -38
  29. package/commands/Recover.sol +20 -19
  30. package/commands/Relay.sol +24 -22
  31. package/commands/Withdraw.sol +20 -19
  32. package/commands/admin/AllowAssets.sol +19 -16
  33. package/commands/admin/Allowance.sol +18 -13
  34. package/commands/admin/Annotate.sol +36 -0
  35. package/commands/admin/Appoint.sol +19 -16
  36. package/commands/admin/Authorize.sol +23 -14
  37. package/commands/admin/Base.sol +9 -2
  38. package/commands/admin/DenyAssets.sol +19 -16
  39. package/commands/admin/Dismiss.sol +19 -16
  40. package/commands/admin/Execute.sol +21 -19
  41. package/commands/admin/Unauthorize.sol +23 -14
  42. package/core/Access.sol +91 -49
  43. package/core/Calls.sol +30 -33
  44. package/core/Endpoint.sol +46 -143
  45. package/core/Host.sol +63 -21
  46. package/core/Pipeline.sol +16 -15
  47. package/core/Types.sol +1 -1
  48. package/docs/Schema.md +48 -31
  49. package/events/Annotation.sol +24 -0
  50. package/events/Endpoint.sol +2 -2
  51. package/events/Guardian.sol +2 -2
  52. package/events/Introduction.sol +3 -2
  53. package/execution/Budget.sol +40 -0
  54. package/execution/Execution.sol +1104 -0
  55. package/guards/Base.sol +11 -12
  56. package/guards/Revoke.sol +11 -9
  57. package/package.json +1 -1
  58. package/ports/AllowAssets.sol +12 -15
  59. package/ports/Allowance.sol +11 -9
  60. package/ports/Base.sol +35 -16
  61. package/ports/Credit.sol +11 -9
  62. package/ports/Debit.sol +11 -9
  63. package/ports/DenyAssets.sol +10 -13
  64. package/ports/Dispatch.sol +13 -15
  65. package/ports/Pipe.sol +14 -12
  66. package/ports/Redeem.sol +12 -9
  67. package/ports/Settle.sol +16 -10
  68. package/queries/Assets.sol +15 -15
  69. package/queries/Balances.sol +17 -17
  70. package/queries/Base.sol +26 -12
  71. package/utils/Accounts.sol +0 -23
  72. package/utils/Actions.sol +1 -0
  73. package/utils/Cursors.sol +367 -0
  74. package/utils/Lanes.sol +14 -0
  75. package/utils/Layout.sol +0 -2
  76. package/utils/Selectors.sol +2 -2
  77. package/utils/Utils.sol +46 -0
  78. package/Cursors.sol +0 -16
  79. package/blocks/Cursors.sol +0 -1529
  80. package/blocks/Writers.sol +0 -1036
  81. package/commands/admin/Label.sol +0 -32
  82. package/commands/admin/Schemas.sol +0 -32
  83. package/core/Payable.sol +0 -53
  84. package/events/Labeled.sol +0 -21
  85. package/events/Schema.sol +0 -23
  86. package/utils/Value.sol +0 -43
package/CHANGELOG.md CHANGED
@@ -3,6 +3,90 @@
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.15.0
7
+
8
+ ### Breaking Changes
9
+
10
+ - Replaced the monolithic `AccessControl` base with the composable
11
+ `CommanderAccess`, `AdminAccess`, `NodeAccess`, `GuardianAccess`,
12
+ `CallerAccess`, and `TrustAccess` capabilities. `CommandBase` now requires a
13
+ concrete caller policy and no longer inherits outbound `NodeCalls`.
14
+ - Split host composition into the commander-only `CommandHost`, the advanced
15
+ `Host`, and the optional `Admins` and `Guardians` feature bundles. Commands
16
+ that use trusted outbound calls must now inherit `NodeCalls` directly and
17
+ compose a `TrustAccess` implementation.
18
+ - Removed the guardian account subtype. Guardians are now ordinary user
19
+ accounts assigned a host-local role, so previously encoded guardian account
20
+ IDs are not compatible.
21
+ - Replaced the `Labeled` and `Schema` discovery events and their dedicated admin
22
+ commands with typed blocks in the generic `Annotation` event and the
23
+ `annotate` admin command.
24
+ - Added the origin user account to `Introduction`, changing its event signature
25
+ to `Introduction(uint indexed host, uint peer, bytes32 origin, uint blocknum)`.
26
+
27
+ ### Added
28
+
29
+ - Added opt-in `Label`, `Schema`, and `Action` annotation mixins together with
30
+ canonical `#label`, `#schema`, `#annotation`, and `#action` codec support.
31
+ - Added semantic action annotations to deposit, payable deposit, withdrawal,
32
+ burn, payout, and port settlement endpoints.
33
+
34
+ ### Changed
35
+
36
+ - Enabled the Solidity optimizer with 200 runs and pinned release testing to
37
+ the Cancun EVM target, the minimum target supporting the codec's `MCOPY` use.
38
+ - Guardians can revoke node access but remain unable to grant it; admin
39
+ commands continue to require both the immutable commander caller and its
40
+ derived admin account.
41
+
42
+ ### Upgrade Compatibility
43
+
44
+ - Existing deployments are not upgradeable and this release does not preserve
45
+ storage layout or guardian mapping keys for proxy upgrades. Deploy fresh host
46
+ contracts when adopting this version.
47
+
48
+ ## 1.14.0
49
+
50
+ ### Breaking Changes
51
+
52
+ - Replaced `CommandContext` and the separate payable value lifecycle with the
53
+ unified `Execution` context. Endpoint and command implementations now open an
54
+ execution directly from calldata, use its packed decoder and writer lanes,
55
+ and finish through the shared `close` helpers.
56
+ - Reworked the block codec around absolute-position `Blocks` primitives,
57
+ packed `Cursors`, cursor-backed `Decoders`, lazy `Buffers`, and thin
58
+ `Writers`. Several low-level cursor and writer APIs were renamed or removed.
59
+ - Redefined block specs and endpoint descriptors. Specs now encode key, minimum,
60
+ maximum, allocation hint, stride, and optional LIST container metadata;
61
+ descriptors use normalized lane specs and include a transaction stride.
62
+ - Replaced the `Cursors.sol` package entry point with `Codec.sol`, added the
63
+ command-authoring `Commands.sol` entry point, and reorganized exports across
64
+ the package barrels.
65
+ - Endpoint selectors are now derived from endpoint names. The configured name
66
+ must match the implementing function name, and descriptor values are
67
+ represented as `uint` throughout.
68
+ - Removed the AUTH and BOUNTY codec blocks, the obsolete `Payable`/`Values`
69
+ helpers, and superseded decoder, writer, schema, and descriptor overloads.
70
+
71
+ ### Added
72
+
73
+ - Added packed output and transaction writer lanes to `Execution`, including
74
+ semantic output helpers, queued credit/debit transactions, budget refunds,
75
+ and direct transaction finalization.
76
+ - Added detachable `Budget` values for pipeline-style consumers, shared lane
77
+ identifiers, spec-driven writer allocation, and optimized semantic block
78
+ readers, writers, and composite unpackers.
79
+ - Added a transaction-output example, command and codec barrel import examples,
80
+ and expanded coverage for packed cursors, buffers, descriptors, budgets,
81
+ execution output, and command flows.
82
+
83
+ ### Changed
84
+
85
+ - Standardized command, query, guard, and port implementations on the same
86
+ execution open/close lifecycle and renamed request terminology to input.
87
+ - Expanded NatSpec across the new execution and codec APIs and refreshed the
88
+ protocol, schema, indexing, and multi-chain documentation.
89
+
6
90
  ## 1.13.0
7
91
 
8
92
  ### Breaking Changes
@@ -88,7 +172,7 @@ breaking API changes. Breaking changes are called out explicitly.
88
172
  - Portal no longer emits `Recovered` when retrying an undelivered witness.
89
173
  - Renamed `Cursors.exit` to `ensureAt` and renamed its position argument to
90
174
  `pos`.
91
- - Removed the redundant `next` return value from both `Cursors.init` overloads;
175
+ - Removed the redundant `next` return value from both `Decoders.init` overloads;
92
176
  callers should use the returned cursor's `len` as the run boundary.
93
177
  - Changed `Cursors.list` to require the expected current cursor position as
94
178
  `pos` before entering the LIST block.
@@ -163,7 +247,7 @@ breaking API changes. Breaking changes are called out explicitly.
163
247
  as the discovery/event surface for dispatch tracking.
164
248
  - Added `ContextRecovery` schema/cursor support and context schema aliases for
165
249
  reusable nested block schemas.
166
- - Added `Values.drain`, `Payable.openValue`, and `Payable.closeValue` to make
250
+ - Added `Values.drain`, `Payable.openValue`, and `Payable.end` to make
167
251
  payable command budget lifecycles explicit.
168
252
 
169
253
  ## 1.6.0
@@ -223,7 +307,7 @@ breaking API changes. Breaking changes are called out explicitly.
223
307
 
224
308
  ### Breaking Changes
225
309
 
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)`.
310
+ - 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
311
  - Tightened command, query, and peer request parsing around the single-run convention used by current protocol endpoints.
228
312
 
229
313
  ### 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
@@ -4,18 +4,20 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports the core host, runtime, access, ledger, settlement, pipeline, node-call, and validation layer.
5
5
  // Import this file to bring the full rootzero host base layer into scope.
6
6
 
7
- import { AccessControl } from "./core/Access.sol";
7
+ import { Action } from "./annotations/Action.sol";
8
+ import { Label } from "./annotations/Label.sol";
9
+ import { Schema } from "./annotations/Schema.sol";
10
+ import { AdminAccess, CallerAccess, CommanderAccess, GuardianAccess, NodeAccess, TrustAccess } from "./core/Access.sol";
8
11
  import { Balances, InsufficientFunds } from "./core/Balances.sol";
9
12
  import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
10
13
  import { NativeAsset, Runtime } from "./core/Runtime.sol";
11
- import { Host, IHostIntroduction } from "./core/Host.sol";
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";
14
+ import { Admins, CommandHost, Guardians, Host, HostIntroduction, IHostIntroduction } from "./core/Host.sol";
15
+ import { CommandCalls, FailedCall, NodeCalls, PortCalls, RawNodeCalls } from "./core/Calls.sol";
16
+ import { EndpointBase } from "./core/Endpoint.sol";
15
17
  import { Pipeline } from "./core/Pipeline.sol";
18
+ import { Budget, Budgets } from "./execution/Budget.sol";
16
19
  import { CreditAccountHook, DebitAccountHook, Settlement } from "./core/Settlement.sol";
17
20
  import { Portal } from "./core/Portal.sol";
18
- import { RecoverHook } from "./commands/Recover.sol";
19
21
  import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
20
22
  import { Validator } from "./core/Validator.sol";
21
23
 
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
@@ -27,13 +23,12 @@ import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
27
23
  import { AdminBase } from "./commands/admin/Base.sol";
28
24
  import { AllowAssets, AllowAssetsHook } from "./commands/admin/AllowAssets.sol";
29
25
  import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
26
+ import { Annotate } from "./commands/admin/Annotate.sol";
30
27
  import { Appoint } from "./commands/admin/Appoint.sol";
31
28
  import { Authorize } from "./commands/admin/Authorize.sol";
32
29
  import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
33
30
  import { Dismiss } from "./commands/admin/Dismiss.sol";
34
31
  import { ExecutePayable } from "./commands/admin/Execute.sol";
35
- import { Label } from "./commands/admin/Label.sol";
36
- import { PublishSchema } from "./commands/admin/Schemas.sol";
37
32
  import { Unauthorize } from "./commands/admin/Unauthorize.sol";
38
33
 
39
34
  // Port endpoints
package/Events.sol CHANGED
@@ -4,6 +4,7 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports all event contracts.
5
5
  // Import this file to get access to every event emitter in one import.
6
6
 
7
+ import { AnnotationEvent } from "./events/Annotation.sol";
7
8
  import { AssetEvent, AssetStatusEvent } from "./events/Asset.sol";
8
9
  import { Actions } from "./utils/Actions.sol";
9
10
  import { BalanceEvent } from "./events/Balance.sol";
@@ -16,12 +17,10 @@ import { RecoveredEvent } from "./events/Recovered.sol";
16
17
  import { EventEmitter } from "./events/Emitter.sol";
17
18
  import { GuardianEvent } from "./events/Guardian.sol";
18
19
  import { IntroductionEvent } from "./events/Introduction.sol";
19
- import { LabeledEvent } from "./events/Labeled.sol";
20
20
  import { LockedEvent } from "./events/Locked.sol";
21
21
  import { NodeEvent } from "./events/Node.sol";
22
22
  import { RootedEvent } from "./events/Rooted.sol";
23
23
  import { RouteEvent } from "./events/Route.sol";
24
- import { SchemaEvent } from "./events/Schema.sol";
25
24
  import { SpentEvent } from "./events/Spent.sol";
26
25
  import { UndeliveredEvent } from "./events/Undelivered.sol";
27
26
  import { UnlockedEvent } from "./events/Unlocked.sol";
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
@@ -27,18 +27,18 @@ npx create-rootzero@latest my-app
27
27
  npm install @rootzero/contracts
28
28
  ```
29
29
 
30
- A minimal host composes the base `Host` with the endpoints it needs and
31
- implements their policy hooks:
30
+ A minimal commander-only host composes `CommandHost` with the endpoints it
31
+ needs and implements their policy hooks:
32
32
 
33
33
  ```solidity
34
34
  // SPDX-License-Identifier: GPL-3.0-only
35
35
  pragma solidity ^0.8.33;
36
36
 
37
- import { Host, Balances } from "@rootzero/contracts/Core.sol";
37
+ import { CommandHost, Balances } from "@rootzero/contracts/Core.sol";
38
38
  import { Deposit } from "@rootzero/contracts/Endpoints.sol";
39
39
 
40
- contract ExampleHost is Host, Balances, Deposit {
41
- constructor(address rootzero) Host(rootzero) {}
40
+ contract ExampleHost is CommandHost, Balances, Deposit {
41
+ constructor(address commander) CommandHost(commander) {}
42
42
 
43
43
  function deposit(bytes32 account, bytes32 asset, uint amount) internal override {
44
44
  uint balance = creditTo(account, asset, amount);
@@ -47,8 +47,31 @@ contract ExampleHost is Host, Balances, Deposit {
47
47
  }
48
48
  ```
49
49
 
50
+ `CommandHost` requires a nonzero commander and accepts command calls only from
51
+ that address. It has no built-in admin commands, peer registry, guardians,
52
+ inbound introduction endpoint, generic execution command, or native-token
53
+ receive function. Use `Host` instead when the application needs those advanced
54
+ facilities; its commands accept the commander, the host itself, and explicitly
55
+ authorized host callers.
56
+
57
+ Both host types introduce themselves during deployment when the commander is a
58
+ contract. That commander must implement `introduce(uint,uint)` and accept the
59
+ call, otherwise deployment reverts. EOA commanders do not receive an
60
+ introduction call.
61
+
62
+ Host contracts are designed for fresh deployment rather than proxy upgrades.
63
+ Releases may change inheritance storage layout, immutable configuration, and
64
+ encoded identity formats; storage compatibility across versions is not
65
+ supported.
66
+
67
+ Commands using trusted outbound `NodeCalls` also require a `TrustAccess`
68
+ implementation. The advanced `Host` supplies one through its composed node access, while
69
+ `CommandHost` deliberately does not. A minimal host can explicitly compose a
70
+ custom trust policy, or a command that intentionally targets arbitrary nodes
71
+ can inherit `RawNodeCalls` instead.
72
+
50
73
  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
74
+ directly. A input is a run of binary blocks — here, a single `#amount` block
52
75
  asking to deposit an asset (the encoders are a few lines each; see
53
76
  [`test/helpers/blocks.ts`](test/helpers/blocks.ts) for reference
54
77
  implementations):
@@ -57,8 +80,8 @@ implementations):
57
80
  const host = await ethers.deployContract("ExampleHost", [deployer.address]);
58
81
 
59
82
  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
83
+ const input = encodeAmountBlock(asset, 100n); // what to deposit
84
+ await host.deposit({ account, state: "0x", input: input }); // emits Balance
62
85
  ```
63
86
 
64
87
  The rest of this guide explains the ideas this example leans on — blocks, IDs,
@@ -66,7 +89,7 @@ hosts, commands — and the surfaces built on top of them.
66
89
 
67
90
  ## Blocks
68
91
 
69
- Every request, response, and piece of in-flight state is a stream of typed
92
+ Every input, response, and piece of in-flight state is a stream of typed
70
93
  blocks. A block is a four-byte key, a four-byte big-endian length, and a
71
94
  payload:
72
95
 
@@ -85,7 +108,7 @@ amount { bytes32 asset, uint amount }
85
108
  is 72 bytes on the wire: an 8-byte header followed by two big-endian 32-byte
86
109
  fields. There is no ABI encoding and no chain-specific type anywhere in the
87
110
  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
111
+ input built for an EVM host is byte-for-byte the input a CosmWasm or Solana
89
112
  port would parse; what differs per chain is how a host *resolves* the
90
113
  identifiers inside, never how the bytes are laid out.
91
114
 
@@ -96,11 +119,11 @@ paths give off-chain tooling presentation names without changing a single byte
96
119
  on the wire. The full schema language is specified in
97
120
  [`docs/Schema.md`](docs/Schema.md). The standard block schemas live in
98
121
  `Schemas` and their runtime keys in `Keys` (both via
99
- `@rootzero/contracts/Cursors.sol`).
122
+ `@rootzero/contracts/Codec.sol`).
100
123
 
101
124
  ## Batches
102
125
 
103
- A request is not a single struct; it is a run of blocks. One `#amount` block
126
+ A input is not a single struct; it is a run of blocks. One `#amount` block
104
127
  asks for one deposit, five blocks ask for five, and the code path is identical
105
128
  — every endpoint parses with a cursor and loops until the stream is exhausted.
106
129
  The descriptor lane key is the prime item: it is the block type that may repeat
@@ -117,15 +140,15 @@ Off-chain, building a batch is concatenation. Using the reference encoders from
117
140
  import { concat } from "ethers";
118
141
  import { encodeAmountBlock } from "./helpers/blocks";
119
142
 
120
- const request = concat([
143
+ const input = concat([
121
144
  encodeAmountBlock(usdc, 250_000_000n),
122
145
  encodeAmountBlock(dai, 250n * 10n ** 18n),
123
146
  ]);
124
- // deposit(request) returns two #balance blocks in its state output and an
147
+ // deposit(input) returns two #balance blocks in its state output and an
125
148
  // empty transaction output
126
149
  ```
127
150
 
128
- Everything downstream keeps this shape: commands loop over request blocks,
151
+ Everything downstream keeps this shape: commands loop over input blocks,
129
152
  settlement loops over transactions, pipelines loop over steps. Batching is
130
153
  never a special case.
131
154
 
@@ -164,7 +187,8 @@ Structured EVM IDs use:
164
187
  where `type` packs `[uint16 representation][uint8 category][uint8 subtype]`. A
165
188
  structured ID announces what it is (an account, an asset, a node) and which
166
189
  chain it lives on, and the payload usually embeds the underlying address. User
167
- accounts are chain-agnostic; admin and guardian accounts are chain-local.
190
+ accounts are chain-agnostic, while admin accounts are chain-local. Guardians
191
+ are normal user accounts assigned a host-specific role.
168
192
  Assets are unique IDs in the same single-word form as accounts and nodes.
169
193
  Nodes are hosts, commands, ports, queries, and guards.
170
194
 
@@ -186,13 +210,18 @@ bytes32 opaque = Ids.toKeccak(preimage); // 0x00-prefixed opaque ID
186
210
 
187
211
  A host is one contract assembled from mixins. The base `Host` brings access
188
212
  control and the admin surface (authorize, unauthorize, appoint, dismiss,
189
- label, executePayable) plus the guardian `revoke` action; you add the
213
+ annotate, executePayable) plus the guardian `revoke` action; you add the
190
214
  endpoints you need and the policy hooks they require. Keeping a ledger is
191
215
  optional: the `Balances` mixin provides one, but a host can just as well
192
216
  implement commands that hold no persistent state in the host at all —
193
217
  forwarding funds elsewhere, or operating only on the state threaded through a
194
218
  pipeline.
195
219
 
220
+ The built-in surface is also available as two independent feature bundles:
221
+ `Admins` provides annotate, authorize, unauthorize, and executePayable;
222
+ `Guardians` provides appoint, dismiss, and revoke. The full `Host` composes
223
+ both, while smaller hosts can inherit either bundle separately.
224
+
196
225
  Trust is explicit and minimal. Each host has an immutable **commander**
197
226
  address fixed at construction, from which its **admin account** is derived.
198
227
  Other contracts become callers only when their node ID is authorized into the
@@ -242,7 +271,7 @@ function deposit(
242
271
  output.appendBalance(asset, amount);
243
272
  }
244
273
 
245
- return (output.finish(), "");
274
+ return (end(output), "");
246
275
  }
247
276
  ```
248
277
 
@@ -252,16 +281,18 @@ lanes, derived group sizes, and flags, plus a human-readable label:
252
281
 
253
282
  ```solidity
254
283
  abstract contract MyCommand is CommandBase {
255
- bytes32 private immutable descriptor;
284
+ uint private immutable descriptor;
256
285
 
257
286
  constructor() {
258
- (, descriptor) = command("myCommand", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
287
+ (, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
259
288
  }
260
289
 
261
290
  function myCommand(
262
- CommandContext calldata c
291
+ bytes32 account,
292
+ bytes calldata state,
293
+ bytes calldata input
263
294
  ) external onlyCommand returns (bytes memory, bytes memory) {
264
- // parse c.input, loop, return the output state run and any transactions
295
+ // parse input, loop, return the output state run and any transactions
265
296
  }
266
297
  }
267
298
  ```
@@ -279,25 +310,25 @@ A single command is rarely the whole story. A pipeline is a run of `#step`
279
310
  blocks executed in order within one transaction:
280
311
 
281
312
  ```txt
282
- step { uint target, uint resources, #bytes as request }
313
+ step { uint cmd, uint resources, #bytes as input }
283
314
  ```
284
315
 
285
- Each step names a target command, the resources it may spend, and its request.
316
+ Each step names a command, the resources it may spend, and its input.
286
317
  The returned state threads into the next command and the final state must be
287
318
  empty. Returned transactions do not enter the state lane; the pipeline passes
288
319
  each decoded transaction to the shared settlement implementation before
289
320
  running the next step. This is the core of `Pipeline.pipe`:
290
321
 
291
322
  ```solidity
292
- while (input.i < input.len) {
293
- (uint target, uint resources, bytes calldata request) = input.unpackStep();
323
+ while (cur.more()) {
324
+ (uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
294
325
  Reader memory transactions;
295
326
  (state, transactions.source) = dispatch(
296
- target,
327
+ cmd,
297
328
  account,
298
329
  state,
299
- request,
300
- useValue(budget, resources)
330
+ input,
331
+ budget.use(resources)
301
332
  );
302
333
  while (transactions.more()) {
303
334
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = transactions.unpackTransaction();
@@ -308,7 +339,7 @@ if (state.length != 0) revert UnexpectedState();
308
339
  ```
309
340
 
310
341
  A transfer, for instance, is a two-step pipeline: `debitAccount` turns an
311
- `#amount` request into `#balance` state, and `payout` consumes that state
342
+ `#amount` input into `#balance` state, and `payout` consumes that state
312
343
  toward a recipient. Because a pipeline is just blocks, it is also the unit of
313
344
  command batching — and `resources` is a chain-specific word interpreted by the
314
345
  portal adapter (on EVM, the low 128 bits are native value in wei, drawn from a
@@ -316,13 +347,13 @@ shared budget), so the same pipeline bytes are meaningful to every port.
316
347
 
317
348
  ## Queries
318
349
 
319
- Queries are the read endpoints: view functions that take a block-stream request
350
+ Queries are the read endpoints: view functions that take a block-stream input
320
351
  and return a block-stream response, with the same batch shape as commands. The
321
352
  standard `getBalances` query takes a run of positions and answers each one in
322
353
  order:
323
354
 
324
355
  ```txt
325
- request: accountAsset { bytes32 account, bytes32 asset }
356
+ input: accountAsset { bytes32 account, bytes32 asset }
326
357
  response: accountAmount { bytes32 account, bytes32 asset, uint amount }
327
358
  ```
328
359
 
@@ -345,8 +376,8 @@ This is also the cross-portal mechanism. `relayPayable` (or `portDispatchPayable
345
376
  wraps a pipe and addresses it to a portal, commonly the destination host ID;
346
377
  a bridge adapter moves the **raw
347
378
  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
379
+ the same pipeline loop. Nothing in the payload is EVM-specific — step commands
380
+ are destination-local command IDs, and only the adapter boundary (native
350
381
  transfers, address resolution, signatures) is chain-specific. The parity rule
351
382
  for ports is strict: every chain's implementation must parse the same input
352
383
  bytes and produce the same output bytes for every endpoint.
@@ -377,10 +408,12 @@ Import from the package entry points rather than deep paths:
377
408
 
378
409
  - `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
379
410
  `Settlement`, `Pipeline`, `Portal`, validator
411
+ - `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, codec
412
+ helpers, and shared value types for authoring custom commands
380
413
  - `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
381
414
  mixins and their hooks
382
- - `@rootzero/contracts/Cursors.sol` — calldata `Cur`/`Cursors`, memory
383
- `Reader`/`Readers`, `Writers`, `Schemas`, `Keys`
415
+ - `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
416
+ `Reader`/`Readers`, `Writers`, `Schemas`, `Keys`, and `Specs`
384
417
  - `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
385
418
  layout and value helpers
386
419
  - `@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
 
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {AnnotationEvent} from "../events/Annotation.sol";
5
+ import {Blocks} from "../codec/Blocks.sol";
6
+
7
+ /// @title Action
8
+ /// @notice Emits a primary semantic action annotation for an entity.
9
+ /// @dev For a trusted emitter, the latest action replaces the earlier value.
10
+ abstract contract Action is AnnotationEvent {
11
+ /// @notice Attach a primary semantic action to `entity`.
12
+ /// @param entity Entity receiving the action annotation.
13
+ /// @param value Canonical action identifier, such as a value from `Actions`.
14
+ function action(uint entity, uint value) internal virtual {
15
+ emit Annotation(entity, Blocks.action(value));
16
+ }
17
+ }
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {AnnotationEvent} from "../events/Annotation.sol";
5
+ import {Blocks} from "../codec/Blocks.sol";
6
+
7
+ /// @title Label
8
+ /// @notice Emits standard label annotation blocks for entities.
9
+ /// @dev A label is identified by its entity and namespace. For a trusted
10
+ /// emitter, the latest label in a namespace replaces the earlier value.
11
+ abstract contract Label is AnnotationEvent {
12
+ /// @notice Attach a human-readable namespaced label to `entity`.
13
+ /// @param entity Entity receiving the label annotation.
14
+ /// @param namespace Label namespace.
15
+ /// @param name Human-readable name within the namespace.
16
+ function label(
17
+ uint entity,
18
+ bytes32 namespace,
19
+ string memory name
20
+ ) internal virtual {
21
+ emit Annotation(entity, Blocks.label(namespace, name));
22
+ }
23
+ }
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {Blocks} from "../codec/Blocks.sol";
5
+ import {Specs} from "../codec/Specs.sol";
6
+ import {AnnotationEvent} from "../events/Annotation.sol";
7
+ import {Runtime} from "../core/Runtime.sol";
8
+
9
+ /// @title Schema
10
+ /// @notice Emits standard block-schema annotations for the current host.
11
+ /// @dev Schema annotations accumulate for distinct block keys. For a trusted
12
+ /// emitter, the latest schema for the same block key replaces the earlier claim.
13
+ abstract contract Schema is Runtime, AnnotationEvent {
14
+ /// @notice Construct and publish a context-local block specification.
15
+ /// @param key Context-local key value.
16
+ /// @param min Minimum accepted payload length.
17
+ /// @param max Maximum accepted payload length; zero means unbounded.
18
+ /// @param hint Initial per-block payload capacity.
19
+ /// @param body Schema DSL string describing the block payload body.
20
+ /// @param name Schema alias name, or zero for unnamed schemas.
21
+ /// @return spec The context-local block specification.
22
+ function schema(
23
+ uint32 key,
24
+ uint32 min,
25
+ uint32 max,
26
+ uint32 hint,
27
+ string memory body,
28
+ bytes32 name
29
+ ) internal returns (uint spec) {
30
+ spec = Specs.create(key, min, max, hint);
31
+ return schema(spec, body, name);
32
+ }
33
+
34
+ /// @notice Construct and publish an exact-size context-local block specification.
35
+ /// @param key Context-local key value.
36
+ /// @param size Exact payload length and initial per-block payload capacity.
37
+ /// @param body Schema DSL string describing the block payload body.
38
+ /// @param name Schema alias name, or zero for unnamed schemas.
39
+ /// @return spec The context-local block specification.
40
+ function schema(uint32 key, uint32 size, string memory body, bytes32 name) internal returns (uint spec) {
41
+ return schema(Specs.create(key, size), body, name);
42
+ }
43
+
44
+ /// @notice Publish an already constructed block specification for the current host.
45
+ /// @param spec Packed block specification.
46
+ /// @param body Schema DSL string describing the block payload body.
47
+ /// @param name Schema alias name, or zero for unnamed schemas.
48
+ /// @return The published block specification.
49
+ function schema(uint spec, string memory body, bytes32 name) internal returns (uint) {
50
+ emit Annotation(host, Blocks.schema(spec, body, name));
51
+ return spec;
52
+ }
53
+ }