@rootzero/contracts 1.19.0 → 1.21.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 (57) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -0
  4. package/Endpoints.sol +5 -4
  5. package/Events.sol +3 -2
  6. package/README.md +18 -12
  7. package/Utils.sol +0 -1
  8. package/codec/Decoders.sol +9 -1
  9. package/codec/Descriptors.sol +28 -28
  10. package/codec/Schema.sol +5 -5
  11. package/codec/Specs.sol +9 -27
  12. package/commands/Allocate.sol +1 -1
  13. package/commands/Base.sol +4 -10
  14. package/commands/Burn.sol +1 -1
  15. package/commands/Credit.sol +3 -3
  16. package/commands/Debit.sol +3 -3
  17. package/commands/Deposit.sol +3 -10
  18. package/commands/Payout.sol +1 -1
  19. package/commands/Provision.sol +3 -3
  20. package/commands/Recover.sol +3 -3
  21. package/commands/Relay.sol +20 -15
  22. package/commands/Repay.sol +3 -3
  23. package/commands/Settle.sol +5 -5
  24. package/commands/Withdraw.sol +1 -1
  25. package/commands/admin/AllowAssets.sol +2 -2
  26. package/commands/admin/Allowance.sol +2 -2
  27. package/commands/admin/Annotate.sol +2 -2
  28. package/commands/admin/Appoint.sol +2 -2
  29. package/commands/admin/Authorize.sol +2 -2
  30. package/commands/admin/Base.sol +1 -1
  31. package/commands/admin/DenyAssets.sol +2 -8
  32. package/commands/admin/Dismiss.sol +2 -2
  33. package/commands/admin/Execute.sol +2 -2
  34. package/commands/admin/Unauthorize.sol +2 -2
  35. package/core/Portal.sol +11 -8
  36. package/docs/Schema.md +53 -29
  37. package/events/Relay.sol +20 -0
  38. package/events/Resolved.sol +17 -0
  39. package/events/Unresolved.sol +18 -0
  40. package/execution/Execution.sol +10 -1
  41. package/guards/Base.sol +1 -2
  42. package/package.json +1 -1
  43. package/ports/AllowAssets.sol +1 -1
  44. package/ports/Allowance.sol +1 -1
  45. package/ports/Base.sol +4 -5
  46. package/ports/Credit.sol +1 -1
  47. package/ports/Debit.sol +1 -1
  48. package/ports/DenyAssets.sol +1 -1
  49. package/ports/Dispatch.sol +19 -7
  50. package/ports/Pipe.sol +2 -1
  51. package/ports/Post.sol +1 -1
  52. package/ports/Redeem.sol +1 -1
  53. package/queries/Base.sol +1 -2
  54. package/utils/Nodes.sol +21 -16
  55. package/events/Recovered.sol +0 -17
  56. package/events/Undelivered.sol +0 -18
  57. package/utils/Selectors.sol +0 -49
package/CHANGELOG.md CHANGED
@@ -3,6 +3,79 @@
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.21.0
7
+
8
+ ### Breaking Changes
9
+
10
+ - Repacked endpoint descriptors as `[state key:4][stride:1]`,
11
+ `[input key:4][stride:1]`,
12
+ `[output key:4][min:4][max:4][hint:3][stride:1]`, four reserved bytes,
13
+ one transaction-count byte, and one flags byte. The input child-key field was
14
+ removed; indexers and request builders must decode the new layout.
15
+ - Removed container metadata and the `many` and `keys` helpers from `Specs`.
16
+ Top-level lists are now emitted custom schemas whose context-local key is the
17
+ endpoint input key; nested lists continue to use the generic `#list` key.
18
+ - Replaced the `funded` and `admin` boolean parameters on command helpers and
19
+ the `funded` boolean parameter on port helpers with a packed `uint8 flags`
20
+ argument. Endpoint flags now live in the dedicated `Flags` library.
21
+ - Renamed the portal lifecycle events from `Undelivered` and `Recovered` to
22
+ `Unresolved` and `Resolved`, and renamed `Portal.retry` to `Portal.resolve`.
23
+ `Portal` now composes the node-access, runtime, and lifecycle-event
24
+ capabilities; concrete implementations remain responsible for explicitly
25
+ emitting lifecycle events.
26
+
27
+ ### Added
28
+
29
+ - Added custom-spec overloads for `Decoders.list(cur, spec)` and
30
+ `Executions.list(exec, spec, lane)` so custom-keyed top-level lists can use
31
+ the same cursor model as generic nested lists.
32
+ - Added `Specs.lane` for canonical descriptor lane packing and exported
33
+ `Flags` from the codec, command, and endpoint package entry points.
34
+
35
+ ### Changed
36
+
37
+ - Updated schema and indexing documentation and the list example for the
38
+ custom-keyed top-level list convention.
39
+
40
+ ## 1.20.0
41
+
42
+ ### Breaking Changes
43
+
44
+ - `Nodes.toCommand`, `toPort`, `toQuery`, and `toGuard` now take an endpoint
45
+ name instead of a precomputed selector. The `Selectors` library was removed;
46
+ endpoint node IDs now derive their canonical selectors internally.
47
+ - Renamed the memory-backed pipeline adapters from `InternalDebitAccount`,
48
+ `InternalCreditAccount`, and `InternalSettle` to `DebitAccountInternal`,
49
+ `CreditAccountInternal`, and `SettleInternal`.
50
+ - Replaced `RelayPayableHook.relayTo(portal, resources, payload, funds)` with
51
+ `relay(portal, resources, account, state, input, funds)`. Relay commands now
52
+ pass their semantic context fields to the host instead of encoding a
53
+ `CONTEXT` payload before invoking the hook.
54
+ - `DispatchPayablePort` now uses the dedicated
55
+ `DispatchPayableHook.dispatchTo(portal, resources, payload, funds)` hook
56
+ instead of sharing `RelayPayableHook`.
57
+
58
+ ### Added
59
+
60
+ - Added `RelayEvent`, with account, portal, resources, correlation key, and
61
+ digest fields for recording outbound relay references.
62
+
63
+ ### Changed
64
+
65
+ - Relay hooks retain state and nested relay input as calldata until an
66
+ implementation chooses to encode or otherwise consume the destination
67
+ context.
68
+
69
+ ### Upgrade Compatibility
70
+
71
+ - Pass endpoint names directly to the `Nodes.to*` helpers and remove imports of
72
+ `Selectors`.
73
+ - Update inherited internal adapter names and imports to the new postfix
74
+ convention.
75
+ - Implement the structured `RelayPayableHook.relay` hook for relay commands and
76
+ `DispatchPayableHook.dispatchTo` for opaque dispatch payloads. Hosts that
77
+ support both surfaces must implement both hooks.
78
+
6
79
  ## 1.19.0
7
80
 
8
81
  ### Breaking Changes
package/Codec.sol CHANGED
@@ -7,7 +7,7 @@ pragma solidity ^0.8.33;
7
7
  import { AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
8
8
  import { Keys } from "./codec/Keys.sol";
9
9
  import { Sizes, Specs } from "./codec/Specs.sol";
10
- import { Descriptors } from "./codec/Descriptors.sol";
10
+ import { Descriptors, Flags } from "./codec/Descriptors.sol";
11
11
  import { Schemas } from "./codec/Schema.sol";
12
12
  import { Decoders } from "./codec/Decoders.sol";
13
13
  import { Cursors, Cur } from "./utils/Cursors.sol";
package/Commands.sol CHANGED
@@ -5,6 +5,7 @@ pragma solidity ^0.8.33;
5
5
  // Import this file for both standard Execution-based commands and custom decoders.
6
6
 
7
7
  import {CommandBase} from "./commands/Base.sol";
8
+ import {Flags} from "./codec/Descriptors.sol";
8
9
  import {Execution, Executions} from "./execution/Execution.sol";
9
10
  import {Lanes} from "./utils/Lanes.sol";
10
11
  import {Blocks} from "./codec/Blocks.sol";
package/Endpoints.sol CHANGED
@@ -5,21 +5,22 @@ pragma solidity ^0.8.33;
5
5
  // Import this file to inherit from the full rootzero callable host surface without managing individual paths.
6
6
 
7
7
  // Shared endpoint hooks
8
+ import {Flags} from "./codec/Descriptors.sol";
8
9
  import {CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook} from "./core/Settlement.sol";
9
10
 
10
11
  // Commands
11
12
  import {CommandBase} from "./commands/Base.sol";
12
13
  import {Allocate, AllocateHook} from "./commands/Allocate.sol";
13
14
  import {Burn, BurnHook} from "./commands/Burn.sol";
14
- import {CreditAccount, InternalCreditAccount} from "./commands/Credit.sol";
15
- import {DebitAccount, InternalDebitAccount} from "./commands/Debit.sol";
15
+ import {CreditAccount, CreditAccountInternal} from "./commands/Credit.sol";
16
+ import {DebitAccount, DebitAccountInternal} from "./commands/Debit.sol";
16
17
  import {Deposit, DepositHook, DepositPayable, DepositPayableHook} from "./commands/Deposit.sol";
17
18
  import {Payout, PayoutHook} from "./commands/Payout.sol";
18
19
  import {Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook} from "./commands/Provision.sol";
19
20
  import {RecoverPayable, RecoverPayableHook} from "./commands/Recover.sol";
20
21
  import {Repay, RepayPayable, RepayPayableHook} from "./commands/Repay.sol";
21
22
  import {RelayPayable, RelayBalancePayable, RelayPayableHook} from "./commands/Relay.sol";
22
- import {Settle, SettlePayable, SettlePayableHook, InternalSettle} from "./commands/Settle.sol";
23
+ import {Settle, SettlePayable, SettlePayableHook, SettleInternal} from "./commands/Settle.sol";
23
24
  import {Withdraw, WithdrawHook} from "./commands/Withdraw.sol";
24
25
 
25
26
  // Admin commands
@@ -43,7 +44,7 @@ import {CreditAccountPort} from "./ports/Credit.sol";
43
44
  import {DebitAccountPort} from "./ports/Debit.sol";
44
45
  import {DenyAssetsPort} from "./ports/DenyAssets.sol";
45
46
  import {PipePayablePort} from "./ports/Pipe.sol";
46
- import {DispatchPayablePort} from "./ports/Dispatch.sol";
47
+ import {DispatchPayablePort, DispatchPayableHook} from "./ports/Dispatch.sol";
47
48
  import {PostPort} from "./ports/Post.sol";
48
49
 
49
50
  // Guard endpoints
package/Events.sol CHANGED
@@ -12,7 +12,8 @@ import { CommanderEvent } from "./events/Commander.sol";
12
12
  import { DispatchEvent } from "./events/Dispatch.sol";
13
13
  import { EndpointEvent } from "./events/Endpoint.sol";
14
14
  import { ReceivedEvent } from "./events/Received.sol";
15
- import { RecoveredEvent } from "./events/Recovered.sol";
15
+ import { ResolvedEvent } from "./events/Resolved.sol";
16
+ import { RelayEvent } from "./events/Relay.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";
@@ -21,7 +22,7 @@ import { NodeEvent } from "./events/Node.sol";
21
22
  import { RootedEvent } from "./events/Rooted.sol";
22
23
  import { RouteEvent } from "./events/Route.sol";
23
24
  import { SpentEvent } from "./events/Spent.sol";
24
- import { UndeliveredEvent } from "./events/Undelivered.sol";
25
+ import { UnresolvedEvent } from "./events/Unresolved.sol";
25
26
  import { UnlockedEvent } from "./events/Unlocked.sol";
26
27
 
27
28
 
package/README.md CHANGED
@@ -121,17 +121,18 @@ on the wire. The full schema language is specified in
121
121
  `Schemas` and their runtime keys in `Keys` (both via
122
122
  `@rootzero/contracts/Codec.sol`).
123
123
 
124
+ A rare top-level list is published as a custom schema such as `many #asset`.
125
+ Its context-local schema key becomes the outer block key accepted by the
126
+ endpoint; nested lists continue to use the generic `#list` key.
127
+
124
128
  ## Batches
125
129
 
126
130
  A input is not a single struct; it is a run of blocks. One `#amount` block
127
131
  asks for one deposit, five blocks ask for five, and the code path is identical
128
132
  — every endpoint parses with a cursor and loops until the stream is exhausted.
129
133
  The descriptor lane key is the prime item: it is the block type that may repeat
130
- for batching. Plain lanes are encoded as `[key][0]`; readers interpret the zero
131
- group byte as group size 1 when the lane is non-empty. Generic list lanes such
132
- as `many #asset` are encoded as
133
- `[Keys.List][Keys.Asset]`, so indexers can see both the top-level LIST container
134
- and the item type inside it.
134
+ for batching. Readers interpret a zero group byte as group size 1 when the lane
135
+ is non-empty.
135
136
 
136
137
  Off-chain, building a batch is concatenation. Using the reference encoders from
137
138
  [`test/helpers/blocks.ts`](test/helpers/blocks.ts):
@@ -308,7 +309,7 @@ abstract contract MyCommand is CommandBase {
308
309
  uint private immutable descriptor;
309
310
 
310
311
  constructor() {
311
- (, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
312
+ (, descriptor) = command("myCommand", Specs.Empty, Specs.Amount, Specs.Balance, 0, 0);
312
313
  }
313
314
 
314
315
  function myCommand(
@@ -327,6 +328,10 @@ abstract contract MyCommand is CommandBase {
327
328
  }
328
329
  ```
329
330
 
331
+ The final argument is a packed flags byte. Pass `0` for an ordinary endpoint,
332
+ or compose values such as `Flags.Funded`, `Flags.Admin`, and
333
+ `Flags.AdminFunded` from the command or endpoint package entry point.
334
+
330
335
  The standard commands cover the common ledger movements: `deposit` and
331
336
  `depositPayable` (external funds in), `settlePayable` (funded settlement),
332
337
  `withdraw` and `burn` (funds out),
@@ -378,8 +383,8 @@ command batching — and `resources` is a chain-specific word interpreted by the
378
383
  portal adapter (on EVM, the low 128 bits are native value in wei, drawn from a
379
384
  shared budget), so the same pipeline bytes are meaningful to every port.
380
385
 
381
- Hosts that implement a pipeline locally can inherit `InternalDebitAccount`,
382
- `InternalCreditAccount`, and `InternalSettle` to advertise the canonical command
386
+ Hosts that implement a pipeline locally can inherit `DebitAccountInternal`,
387
+ `CreditAccountInternal`, and `SettleInternal` to advertise the canonical command
383
388
  endpoints while routing their local command IDs through `executeDebitAccount`,
384
389
  `executeCreditAccount`, and `executeSettle`. These adapters consume the
385
390
  memory-backed pipeline state directly and avoid an external self-call. Pass the
@@ -466,12 +471,13 @@ Import from the package entry points rather than deep paths:
466
471
 
467
472
  - `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
468
473
  `Settlement`, `Pipeline`, `Portal`, validator
469
- - `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, codec
470
- helpers, and shared value types for authoring custom commands
474
+ - `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, `Flags`,
475
+ codec helpers, and shared value types for authoring custom commands
471
476
  - `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
472
- mixins and their hooks
477
+ mixins, their hooks, and `Flags`
473
478
  - `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
474
- `Reader`/`Readers`, `Writers`, `Schemas`, `Keys`, and `Specs`
479
+ `Reader`/`Readers`, `Writers`, `Schemas`, `Descriptors`, `Flags`, `Keys`, and
480
+ `Specs`
475
481
  - `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
476
482
  layout and value helpers
477
483
  - `@rootzero/contracts/Events.sol` — protocol event contracts
package/Utils.sol CHANGED
@@ -11,7 +11,6 @@ import { Amounts, Assets } from "./utils/Assets.sol";
11
11
  import { ECDSA } from "./utils/ECDSA.sol";
12
12
  import { Ids } from "./utils/Ids.sol";
13
13
  import { Nodes } from "./utils/Nodes.sol";
14
- import { Selectors } from "./utils/Selectors.sol";
15
14
  import { Layout } from "./utils/Layout.sol";
16
15
  import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, clear8, clear16, clear32, clear64, divisible, ensureAddr, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, replace8, replace16, replace32, replace64, retryTicket, toLocalBase, toUnspecifiedBase, ValueOverflow, ZeroAddress } from "./utils/Utils.sol";
17
16
 
@@ -194,7 +194,15 @@ library Decoders {
194
194
  /// @param cur Cursor advanced past the list.
195
195
  /// @return items Cursor spanning the list payload.
196
196
  function list(Cur memory cur) internal pure returns (Cur memory items) {
197
- (uint abs, uint end) = consume(cur, Specs.List);
197
+ return list(cur, Specs.List);
198
+ }
199
+
200
+ /// @notice Consume a list block described by `spec` and return a cursor over its items.
201
+ /// @param cur Cursor advanced past the list.
202
+ /// @param spec Custom list block specification.
203
+ /// @return items Cursor spanning the list payload.
204
+ function list(Cur memory cur, uint spec) internal pure returns (Cur memory items) {
205
+ (uint abs, uint end) = consume(cur, spec);
198
206
  items.state = Cursors.create(abs, end - abs, 0, 0, 0);
199
207
  }
200
208
 
@@ -4,25 +4,32 @@ pragma solidity ^0.8.33;
4
4
  import {Specs} from "./Specs.sol";
5
5
  import {Lanes} from "../utils/Lanes.sol";
6
6
 
7
+ /// @title Flags
8
+ /// @notice Packed endpoint behavior flags.
9
+ library Flags {
10
+ /// @dev Endpoint accepts nonzero native value.
11
+ uint8 internal constant Funded = 1 << 0;
12
+ /// @dev Endpoint is restricted to the admin account.
13
+ uint8 internal constant Admin = 1 << 1;
14
+ /// @dev Endpoint accepts nonzero native value and is restricted to the admin account.
15
+ uint8 internal constant AdminFunded = Admin | Funded;
16
+ }
17
+
7
18
  /// @title Descriptors
8
19
  /// @notice Packing and lane metadata helpers for endpoint descriptors.
9
20
  library Descriptors {
10
21
  /// @dev The requested lane is not part of an endpoint descriptor.
11
22
  error InvalidLane();
12
23
 
13
- /// @dev Endpoint accepts nonzero native value.
14
- uint8 internal constant Funded = 1 << 0;
15
- /// @dev Endpoint is restricted to the admin account.
16
- uint8 internal constant Admin = 1 << 1;
17
-
18
24
  /// @notice Create a descriptor from endpoint lane specifications and flags.
19
25
  /// @dev Layout: `[state key:4][stride:1]`
20
- /// `[input key:4][item:4][stride:1]`
26
+ /// `[input key:4][stride:1]`
21
27
  /// `[output key:4][min:4][max:4][hint:3][stride:1]`
28
+ /// `[reserved:4]`
22
29
  /// `[transactions:1]`
23
30
  /// `[flags:1]`. Flag bits: funded = 0, admin = 1.
24
31
  /// @param state State lane specification.
25
- /// @param input Input lane specification, optionally wrapped in a container.
32
+ /// @param input Direct input lane specification.
26
33
  /// @param output Output writer specification.
27
34
  /// @param transactions Transactions produced per batch.
28
35
  /// @param flags Endpoint behavior flags.
@@ -34,15 +41,13 @@ library Descriptors {
34
41
  uint8 transactions,
35
42
  uint8 flags
36
43
  ) internal pure returns (uint descriptor) {
37
- state = Specs.normalize(state, true);
38
- input = Specs.normalize(input, false);
39
- output = Specs.normalize(output, true);
44
+ output = Specs.normalize(output);
40
45
  descriptor = pack(state, input, output, transactions, flags);
41
46
  }
42
47
 
43
- /// @dev Pack normalized endpoint specs and flags into a descriptor.
44
- /// @param state Normalized direct state specification.
45
- /// @param input Normalized input specification.
48
+ /// @dev Pack endpoint specs and flags into a descriptor.
49
+ /// @param state State specification.
50
+ /// @param input Input specification.
46
51
  /// @param output Normalized direct output specification.
47
52
  /// @param transactions Transactions produced per batch.
48
53
  /// @param flags Endpoint behavior flags.
@@ -54,14 +59,9 @@ library Descriptors {
54
59
  uint8 transactions,
55
60
  uint8 flags
56
61
  ) private pure returns (uint descriptor) {
57
- (bytes4 outer, bytes4 child) = Specs.keys(input);
58
-
59
- descriptor |= uint(uint32(Specs.key(state))) << 224;
60
- descriptor |= uint(Specs.stride(state)) << 216;
61
- descriptor |= uint(uint32(outer)) << 184;
62
- descriptor |= uint(uint32(child)) << 152;
63
- descriptor |= uint(Specs.stride(input)) << 144;
64
- descriptor |= (output >> 128) << 16;
62
+ descriptor |= uint(Specs.lane(state)) << 216;
63
+ descriptor |= uint(Specs.lane(input)) << 176;
64
+ descriptor |= (output >> 128) << 48;
65
65
  descriptor |= uint(transactions) << 8;
66
66
  descriptor |= flags;
67
67
  }
@@ -82,22 +82,22 @@ library Descriptors {
82
82
  /// @return Effective blocks per batch for the lane.
83
83
  function stride(uint descriptor, uint8 lane) internal pure returns (uint8) {
84
84
  if (lane == Lanes.State) return uint8(descriptor >> 216);
85
- if (lane == Lanes.Input) return uint8(descriptor >> 144);
86
- if (lane == Lanes.Output) return uint8(descriptor >> 16);
85
+ if (lane == Lanes.Input) return uint8(descriptor >> 176);
86
+ if (lane == Lanes.Output) return uint8(descriptor >> 48);
87
87
  if (lane == Lanes.Transactions) return uint8(descriptor >> 8);
88
88
  revert InvalidLane();
89
89
  }
90
90
 
91
91
  /// @notice Return the effective block key for `lane`.
92
- /// @dev Input returns its outer key. Transaction blocks have a fixed
93
- /// protocol key that is not stored in the descriptor.
92
+ /// @dev Transaction blocks have a fixed protocol key that is not stored in
93
+ /// the descriptor.
94
94
  /// @param descriptor Packed endpoint descriptor.
95
95
  /// @param lane Lane identifier from `Lanes`.
96
- /// @return Effective outer block key for the lane.
96
+ /// @return Effective block key for the lane.
97
97
  function key(uint descriptor, uint8 lane) internal pure returns (bytes4) {
98
98
  if (lane == Lanes.State) return bytes4(uint32(descriptor >> 224));
99
99
  if (lane == Lanes.Input) return bytes4(uint32(descriptor >> 184));
100
- if (lane == Lanes.Output) return bytes4(uint32(descriptor >> 112));
100
+ if (lane == Lanes.Output) return bytes4(uint32(descriptor >> 144));
101
101
  if (lane == Lanes.Transactions) return Specs.key(Specs.Transaction);
102
102
  revert InvalidLane();
103
103
  }
@@ -110,7 +110,7 @@ library Descriptors {
110
110
  /// @return growable Whether the writer may grow beyond that capacity.
111
111
  function allocation(uint descriptor, uint8 lane, uint groups) internal pure returns (uint capacity, bool growable) {
112
112
  if (lane == Lanes.Output) {
113
- uint spec = uint(uint128(descriptor >> 16)) << 128;
113
+ uint spec = uint(uint128(descriptor >> 48)) << 128;
114
114
  return Specs.allocation(spec, groups);
115
115
  }
116
116
  if (lane == Lanes.Transactions) {
package/codec/Schema.sol CHANGED
@@ -8,7 +8,7 @@ pragma solidity ^0.8.33;
8
8
  //
9
9
  // Schema:
10
10
  // - block aliases are published separately from payload schemas
11
- // - payload schemas are written as `{ fields }`
11
+ // - payload schemas are `""`, `{ fields }`, or a top-level `many #x`
12
12
  // - an empty schema string means the block has no structured payload
13
13
  // - commas separate siblings at every level
14
14
  // - braces define the current block payload body
@@ -16,10 +16,10 @@ pragma solidity ^0.8.33;
16
16
  // - command state is a single active state run without trailing globals
17
17
  // - run items may repeat at top level for batching
18
18
  // - `maybe #x` marks an optional block item
19
- // - `many #x` emits one generic list block containing repeated `#x` items
20
- // - endpoint descriptor lanes are `[key bytes4][item bytes4]`; normal keys widen to `[key][0]`
21
- // - descriptor lanes for `many #x` use `[Keys.List][keyOfX]`; bare `[Keys.List][0]`
22
- // is incomplete discovery metadata and should be rejected by tooling
19
+ // - nested `many #x` emits one generic list block containing repeated `#x` items
20
+ // - a custom schema consisting of top-level `many #x` uses its custom key for
21
+ // the outer list block and contains repeated `#x` items directly
22
+ // - endpoint descriptor lanes identify their top-level block key directly
23
23
  // - `portal` fields are routing identifiers, often destination host IDs
24
24
  // - `resources` fields are chain-specific resource words. A portal adapter
25
25
  // interprets them for the destination runtime. EVM resources use the low
package/codec/Specs.sol CHANGED
@@ -2,7 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {Keys} from "./Keys.sol";
5
- import {max24, replace8, replace32} from "../utils/Utils.sol";
5
+ import {max24, replace8} from "../utils/Utils.sol";
6
6
 
7
7
  /// @title Sizes
8
8
  /// @notice Total byte sizes for fixed-width block types, including the 8-byte header (4-byte key + 4-byte payloadLen).
@@ -39,15 +39,13 @@ library Sizes {
39
39
 
40
40
  /// @title Specs
41
41
  /// @notice Word-aligned block specifications encoded as
42
- /// `[key:4][min:4][max:4][hint:3][stride:1][container:4][reserved:12]`.
42
+ /// `[key:4][min:4][max:4][hint:3][stride:1][reserved:16]`.
43
43
  /// The upper eight bytes of a fixed-layout spec are its encoded block header,
44
44
  /// allowing the entire spec word to be written directly as that header.
45
45
  /// A maximum of zero means unbounded and requires a growable writer.
46
46
  library Specs {
47
47
  /// @dev A payload is incompatible with its block specification.
48
48
  error InvalidSpec();
49
- /// @dev A direct specification cannot contain a wrapper block.
50
- error InvalidContainer();
51
49
  uint private constant SizeFields = (uint(1) << 192) | (uint(1) << 160) | (uint(1) << 136);
52
50
 
53
51
  // Reusable field shapes keep public specs readable while remaining valid
@@ -156,29 +154,20 @@ library Specs {
156
154
 
157
155
  /// @notice Return the canonical form of `spec` with implicit defaults resolved.
158
156
  /// @param spec Packed block specification.
159
- /// @param direct Whether the specification must not contain a wrapper block.
160
157
  /// @dev A present spec with an encoded zero stride receives stride one;
161
158
  /// the empty spec remains unchanged.
162
159
  /// @return Canonical specification.
163
- function normalize(uint spec, bool direct) internal pure returns (uint) {
164
- if (direct && uint32(spec >> 96) != 0) revert InvalidContainer();
160
+ function normalize(uint spec) internal pure returns (uint) {
165
161
  if (stride(spec) == 0 && key(spec) != bytes4(0)) spec |= uint(1) << 128;
166
162
  return spec;
167
163
  }
168
164
 
169
- /// @notice Return the effective outer block key and optional wrapped child key.
170
- /// @dev A direct spec returns its own key as `outer` and a zero `child`.
165
+ /// @notice Return the canonical descriptor lane metadata for `spec`.
171
166
  /// @param spec Packed block specification.
172
- /// @return outer Effective outer block key.
173
- /// @return child Wrapped child key, or zero for a direct specification.
174
- function keys(uint spec) internal pure returns (bytes4 outer, bytes4 child) {
175
- child = key(spec);
176
- outer = bytes4(uint32(spec >> 96));
177
-
178
- if (outer == bytes4(0)) {
179
- outer = child;
180
- child = bytes4(0);
181
- }
167
+ /// @return Packed `[key:4][stride:1]` lane metadata.
168
+ function lane(uint spec) internal pure returns (uint40) {
169
+ spec = normalize(spec);
170
+ return (uint40(uint32(key(spec))) << 8) | stride(spec);
182
171
  }
183
172
 
184
173
  /// @notice Return whether a payload size lies within a specification's bounds.
@@ -213,7 +202,7 @@ library Specs {
213
202
  /// @param groups Number of groups.
214
203
  /// @return Number of blocks across all groups.
215
204
  function count(uint spec, uint groups) internal pure returns (uint) {
216
- return groups * stride(normalize(spec, false));
205
+ return groups * stride(normalize(spec));
217
206
  }
218
207
 
219
208
  /// @notice Return the buffer configuration for `groups` of `spec`.
@@ -227,13 +216,6 @@ library Specs {
227
216
  growable = uint32(spec >> 160) == 0;
228
217
  }
229
218
 
230
- /// @notice Return `spec` annotated as a generic LIST item.
231
- /// @param spec Child block specification.
232
- /// @return Specification wrapped in a LIST container.
233
- function many(uint spec) internal pure returns (uint) {
234
- return replace32(spec, 96, uint32(key(List)));
235
- }
236
-
237
219
  /// @notice Return `spec` grouped with an explicit stride.
238
220
  /// @param spec Packed block specification.
239
221
  /// @param n Number of blocks per group.
@@ -24,7 +24,7 @@ abstract contract Allocate is CommandBase, AllocateHook {
24
24
  uint private immutable descriptor;
25
25
 
26
26
  constructor() {
27
- (, descriptor) = command("allocate", Specs.Balance, Specs.Node, Specs.Custody, 0, false, false);
27
+ (, descriptor) = command("allocate", Specs.Balance, Specs.Node, Specs.Custody, 0, 0);
28
28
  }
29
29
 
30
30
  /// @notice Allocate BALANCE state blocks to matching NODE input blocks.
package/commands/Base.sol CHANGED
@@ -9,9 +9,8 @@ import {HostAmount, Position} from "../core/Types.sol";
9
9
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
10
10
  import {ReceivedEvent} from "../events/Received.sol";
11
11
  import {Actions} from "../utils/Actions.sol";
12
- import {Descriptors} from "../codec/Descriptors.sol";
12
+ import {Descriptors, Flags} from "../codec/Descriptors.sol";
13
13
  import {Nodes} from "../utils/Nodes.sol";
14
- import {Selectors} from "../utils/Selectors.sol";
15
14
  import {Cursors} from "../utils/Cursors.sol";
16
15
 
17
16
  using Executions for Execution;
@@ -45,8 +44,7 @@ abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
45
44
  /// @param input Input block specification.
46
45
  /// @param output Output block specification.
47
46
  /// @param transactions Number of transaction blocks produced per batch, or zero for none.
48
- /// @param funded Whether the command accepts nonzero native value.
49
- /// @param admin Whether the command is restricted to the admin account.
47
+ /// @param flags Packed command behavior flags.
50
48
  /// @return id Command node ID.
51
49
  /// @return descriptor Packed endpoint lane metadata and flags.
52
50
  function command(
@@ -55,12 +53,8 @@ abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
55
53
  uint input,
56
54
  uint output,
57
55
  uint8 transactions,
58
- bool funded,
59
- bool admin
56
+ uint8 flags
60
57
  ) internal returns (uint id, uint descriptor) {
61
- uint8 flags = 0;
62
- if (funded) flags |= Descriptors.Funded;
63
- if (admin) flags |= Descriptors.Admin;
64
58
  descriptor = Descriptors.create(state, input, output, transactions, flags);
65
59
  return command(name, descriptor);
66
60
  }
@@ -72,7 +66,7 @@ abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
72
66
  /// @return id Command node ID.
73
67
  /// @return published Published endpoint descriptor.
74
68
  function command(string memory name, uint descriptor) internal returns (uint id, uint published) {
75
- id = Nodes.toCommand(Selectors.command(name), address(this));
69
+ id = Nodes.toCommand(name, address(this));
76
70
  published = endpoint(id, name, descriptor);
77
71
  }
78
72
 
package/commands/Burn.sol CHANGED
@@ -25,7 +25,7 @@ abstract contract Burn is CommandBase, BurnHook, Action {
25
25
 
26
26
  constructor() {
27
27
  uint id;
28
- (id, descriptor) = command("burn", Specs.Balance, Specs.Empty, Specs.Empty, 0, false, false);
28
+ (id, descriptor) = command("burn", Specs.Balance, Specs.Empty, Specs.Empty, 0, 0);
29
29
  action(id, Actions.Burn);
30
30
  }
31
31
 
@@ -17,7 +17,7 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
17
17
  uint private immutable id;
18
18
 
19
19
  constructor() {
20
- (id, descriptor) = command("creditAccount", Specs.Balance, Specs.Empty, Specs.Empty, 0, false, false);
20
+ (id, descriptor) = command("creditAccount", Specs.Balance, Specs.Empty, Specs.Empty, 0, 0);
21
21
  }
22
22
 
23
23
  /// @notice Return the registered CREDIT_ACCOUNT command ID.
@@ -45,11 +45,11 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
45
45
  }
46
46
  }
47
47
 
48
- /// @title InternalCreditAccount
48
+ /// @title CreditAccountInternal
49
49
  /// @notice Extends the advertised credit-account command with memory-state pipeline dispatch.
50
50
  /// @dev This adapter is not a separate command. It uses the command ID and account hook
51
51
  /// inherited from `CreditAccount` while accepting the state location used by `Pipeline`.
52
- abstract contract InternalCreditAccount is CreditAccount {
52
+ abstract contract CreditAccountInternal is CreditAccount {
53
53
  /// @notice Execute the inherited credit-account command from an internal pipeline.
54
54
  /// @param account Account credited by each balance.
55
55
  /// @param state BALANCE block stream held in pipeline memory.
@@ -20,7 +20,7 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
20
20
  uint private immutable id;
21
21
 
22
22
  constructor() {
23
- (id, descriptor) = command("debitAccount", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
23
+ (id, descriptor) = command("debitAccount", Specs.Empty, Specs.Amount, Specs.Balance, 0, 0);
24
24
  }
25
25
 
26
26
  /// @notice Return the registered DEBIT_ACCOUNT command ID.
@@ -49,11 +49,11 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
49
49
  }
50
50
  }
51
51
 
52
- /// @title InternalDebitAccount
52
+ /// @title DebitAccountInternal
53
53
  /// @notice Extends the advertised debit-account command with memory-state pipeline dispatch.
54
54
  /// @dev This adapter is not a separate command. It uses the command ID and account hook
55
55
  /// inherited from `DebitAccount` while accepting the state location used by `Pipeline`.
56
- abstract contract InternalDebitAccount is DebitAccount {
56
+ abstract contract DebitAccountInternal is DebitAccount {
57
57
  /// @notice Execute the inherited debit-account command from an internal pipeline.
58
58
  /// @param account Account whose funds are debited.
59
59
  /// @param state Empty pipeline state required by the command schema.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { Execution, Executions, CommandBase, Lanes, Specs } from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.sol";
5
5
  import {Action} from "../annotations/Action.sol";
6
6
  import {Actions} from "../utils/Actions.sol";
7
7
 
@@ -39,7 +39,7 @@ abstract contract Deposit is CommandBase, DepositHook, Action {
39
39
 
40
40
  constructor() {
41
41
  uint id;
42
- (id, descriptor) = command("deposit", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
42
+ (id, descriptor) = command("deposit", Specs.Empty, Specs.Amount, Specs.Balance, 0, 0);
43
43
  action(id, Actions.Deposit);
44
44
  }
45
45
 
@@ -72,7 +72,7 @@ abstract contract DepositPayable is CommandBase, DepositPayableHook, Action {
72
72
 
73
73
  constructor() {
74
74
  uint id;
75
- (id, descriptor) = command("depositPayable", Specs.Empty, Specs.Amount, Specs.Balance, 0, true, false);
75
+ (id, descriptor) = command("depositPayable", Specs.Empty, Specs.Amount, Specs.Balance, 0, Flags.Funded);
76
76
  action(id, Actions.Deposit);
77
77
  }
78
78
 
@@ -96,10 +96,3 @@ abstract contract DepositPayable is CommandBase, DepositPayableHook, Action {
96
96
  return close(exec, account);
97
97
  }
98
98
  }
99
-
100
-
101
-
102
-
103
-
104
-
105
-
@@ -26,7 +26,7 @@ abstract contract Payout is CommandBase, PayoutHook, Action {
26
26
 
27
27
  constructor() {
28
28
  uint id;
29
- (id, descriptor) = command("payout", Specs.Balance, Specs.Account, Specs.Empty, 0, false, false);
29
+ (id, descriptor) = command("payout", Specs.Balance, Specs.Account, Specs.Empty, 0, 0);
30
30
  action(id, Actions.Payout);
31
31
  }
32
32