@rootzero/contracts 1.14.0 → 1.16.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 (68) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -1
  4. package/Core.sol +8 -5
  5. package/Endpoints.sol +6 -6
  6. package/Events.sol +1 -3
  7. package/README.md +118 -35
  8. package/annotations/Action.sol +17 -0
  9. package/annotations/Label.sol +23 -0
  10. package/annotations/Schema.sol +53 -0
  11. package/codec/Blocks.sol +127 -32
  12. package/codec/Buffers.sol +3 -3
  13. package/codec/Decoders.sol +46 -13
  14. package/codec/Descriptors.sol +0 -1
  15. package/codec/Keys.sol +8 -4
  16. package/codec/Readers.sol +13 -0
  17. package/codec/Schema.sol +12 -3
  18. package/codec/Specs.sol +28 -4
  19. package/codec/Writers.sol +21 -6
  20. package/commands/Base.sol +39 -44
  21. package/commands/Burn.sol +8 -4
  22. package/commands/Credit.sol +35 -3
  23. package/commands/Debit.sol +46 -13
  24. package/commands/Deposit.sol +14 -8
  25. package/commands/Payout.sol +6 -2
  26. package/commands/Provision.sol +4 -4
  27. package/commands/Recover.sol +15 -8
  28. package/commands/Relay.sol +37 -12
  29. package/commands/Settle.sol +77 -0
  30. package/commands/Withdraw.sol +8 -4
  31. package/commands/admin/AllowAssets.sol +2 -2
  32. package/commands/admin/Allowance.sol +2 -2
  33. package/commands/admin/Annotate.sol +36 -0
  34. package/commands/admin/Appoint.sol +6 -5
  35. package/commands/admin/Authorize.sol +2 -2
  36. package/commands/admin/Base.sol +8 -1
  37. package/commands/admin/DenyAssets.sol +2 -2
  38. package/commands/admin/Dismiss.sol +6 -5
  39. package/commands/admin/Execute.sol +5 -4
  40. package/commands/admin/Unauthorize.sol +2 -2
  41. package/core/Access.sol +91 -49
  42. package/core/Calls.sol +27 -22
  43. package/core/Endpoint.sol +36 -46
  44. package/core/Host.sol +63 -21
  45. package/core/Pipeline.sol +6 -6
  46. package/core/Settlement.sol +37 -8
  47. package/core/Types.sol +13 -1
  48. package/docs/Schema.md +68 -7
  49. package/events/Annotation.sol +24 -0
  50. package/events/Guardian.sol +2 -2
  51. package/events/Introduction.sol +3 -2
  52. package/execution/Budget.sol +12 -4
  53. package/execution/Execution.sol +113 -18
  54. package/guards/Base.sol +4 -4
  55. package/package.json +1 -1
  56. package/ports/Base.sol +19 -7
  57. package/ports/Dispatch.sol +6 -6
  58. package/ports/{Settle.sol → Post.sol} +13 -9
  59. package/queries/Base.sol +18 -3
  60. package/utils/Accounts.sol +0 -23
  61. package/utils/Actions.sol +1 -0
  62. package/utils/Cursors.sol +79 -34
  63. package/utils/Layout.sol +0 -2
  64. package/commands/admin/Label.sol +0 -36
  65. package/commands/admin/Schemas.sol +0 -36
  66. package/events/Labeled.sol +0 -21
  67. package/events/Position.sol +0 -22
  68. package/events/Schema.sol +0 -23
package/CHANGELOG.md CHANGED
@@ -3,6 +3,106 @@
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.16.0
7
+
8
+ ### Breaking Changes
9
+
10
+ - Removed the unused generic `Position` event and `PositionEvent` base contract.
11
+ - Renamed transaction handling from settlement to posting: the transaction
12
+ helper is now `post(...)`, and `portSettle(bytes)` is now `portPost(bytes)`.
13
+ The port selector and node ID change, and its action is now `Actions.Post`
14
+ (`14`) instead of `Actions.Settle` (`3`). The `Settlement` convenience base
15
+ implements both the transaction `PostHook` and position `SettleHook`.
16
+ - Removed `openInput` from `EndpointBase`. Port, query, and guard bases expose
17
+ `openInput` through the input-only `InputEndpointBase`, while custom commands
18
+ must pass both state and input through `openCommand`.
19
+ - Removed the wildcard `Specs.Any` state type. The stateful relay is now
20
+ `relayBalancePayable` and accepts `BALANCE` state blocks; `relayPayable`
21
+ explicitly accepts empty state.
22
+ - Renamed the numeric `Position` fields from `assets` and `liabilities` to
23
+ `amount` and `debt`.
24
+ - Renamed `Budget.use` to `useResourceValue` and split execution spending into
25
+ exact `useValue` and packed-resource `useResourceValue` helpers.
26
+ - Renamed `RoutePayableHook.route` to `RelayPayableHook.relayTo`. Recovery hooks
27
+ are now `RecoverPayableHook` implementations that receive the complete
28
+ resource word and mutable execution budget.
29
+
30
+ ### Added
31
+
32
+ - Added the hostless `#position` state block for threading asset-liability pairs
33
+ between pipeline commands.
34
+ - Added the `settle` command, the position `SettleHook`, and the transaction
35
+ `PostHook`. The `Settlement` convenience base provides default implementations
36
+ of both through the debit and credit account hooks.
37
+ - Added `relayBalancePayable` for relaying required `BALANCE` state while
38
+ `relayPayable` now explicitly relays with empty state.
39
+ - Added memory-backed `InternalDebitAccount`, `InternalCreditAccount`, and
40
+ `InternalSettle` adapters for hosts that execute canonical commands directly
41
+ from their pipeline dispatcher.
42
+
43
+ ### Changed
44
+
45
+ - Documented the command state-safety invariant: every command must handle the
46
+ complete supplied state stream or revert, and commands with empty state lanes
47
+ must reject non-empty state.
48
+ - Endpoint decoder opening now requires the complete supplied lane to be one
49
+ homogeneous run of the descriptor's declared key; trailing block types are
50
+ rejected instead of silently left outside the decoder cursor.
51
+ - Block runs and cursors now retain raw block counts only. Descriptor stride
52
+ conversion and state/input group reconciliation happen once in execution
53
+ opening rather than in `Blocks` or `Cursors`.
54
+ - Pipeline transaction streams are posted before the next step, and position
55
+ state is settled separately through the scalar asset, amount, liability, and
56
+ debt hook.
57
+
58
+ ### Upgrade Compatibility
59
+
60
+ - Command and port selectors, block schemas, and hook signatures changed in
61
+ this release. Deploy fresh hosts and update pipeline builders and peer
62
+ integrations together.
63
+
64
+ ## 1.15.0
65
+
66
+ ### Breaking Changes
67
+
68
+ - Replaced the monolithic `AccessControl` base with the composable
69
+ `CommanderAccess`, `AdminAccess`, `NodeAccess`, `GuardianAccess`,
70
+ `CallerAccess`, and `TrustAccess` capabilities. `CommandBase` now requires a
71
+ concrete caller policy and no longer inherits outbound `NodeCalls`.
72
+ - Split host composition into the commander-only `CommandHost`, the advanced
73
+ `Host`, and the optional `Admins` and `Guardians` feature bundles. Commands
74
+ that use trusted outbound calls must now inherit `NodeCalls` directly and
75
+ compose a `TrustAccess` implementation.
76
+ - Removed the guardian account subtype. Guardians are now ordinary user
77
+ accounts assigned a host-local role, so previously encoded guardian account
78
+ IDs are not compatible.
79
+ - Replaced the `Labeled` and `Schema` discovery events and their dedicated admin
80
+ commands with typed blocks in the generic `Annotation` event and the
81
+ `annotate` admin command.
82
+ - Added the origin user account to `Introduction`, changing its event signature
83
+ to `Introduction(uint indexed host, uint peer, bytes32 origin, uint blocknum)`.
84
+
85
+ ### Added
86
+
87
+ - Added opt-in `Label`, `Schema`, and `Action` annotation mixins together with
88
+ canonical `#label`, `#schema`, `#annotation`, and `#action` codec support.
89
+ - Added semantic action annotations to deposit, payable deposit, withdrawal,
90
+ burn, payout, and port settlement endpoints.
91
+
92
+ ### Changed
93
+
94
+ - Enabled the Solidity optimizer with 200 runs and pinned release testing to
95
+ the Cancun EVM target, the minimum target supporting the codec's `MCOPY` use.
96
+ - Guardians can revoke node access but remain unable to grant it; admin
97
+ commands continue to require both the immutable commander caller and its
98
+ derived admin account.
99
+
100
+ ### Upgrade Compatibility
101
+
102
+ - Existing deployments are not upgradeable and this release does not preserve
103
+ storage layout or guardian mapping keys for proxy upgrades. Deploy fresh host
104
+ contracts when adopting this version.
105
+
6
106
  ## 1.14.0
7
107
 
8
108
  ### Breaking Changes
package/Codec.sol CHANGED
@@ -4,7 +4,7 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports the complete block encoding and decoding surface.
5
5
  // Import this file for low-level codec extensions and direct stream processing.
6
6
 
7
- import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
7
+ import { AssetAmount, AccountAsset, 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
10
  import { Descriptors } from "./codec/Descriptors.sol";
package/Commands.sol CHANGED
@@ -11,4 +11,4 @@ import {Blocks} from "./codec/Blocks.sol";
11
11
  import {Sizes, Specs} from "./codec/Specs.sol";
12
12
  import {Decoders} from "./codec/Decoders.sol";
13
13
  import {Cursors, Cur} from "./utils/Cursors.sol";
14
- import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx} from "./core/Types.sol";
14
+ import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx} from "./core/Types.sol";
package/Core.sol CHANGED
@@ -4,18 +4,21 @@ 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";
14
+ import { Admins, CommandHost, Guardians, Host, HostIntroduction, IHostIntroduction } from "./core/Host.sol";
15
+ import { CommandCalls, FailedCall, NodeCalls, PortCalls, RawNodeCalls } from "./core/Calls.sol";
13
16
  import { EndpointBase } from "./core/Endpoint.sol";
14
17
  import { Pipeline } from "./core/Pipeline.sol";
15
18
  import { Budget, Budgets } from "./execution/Budget.sol";
16
- import { CreditAccountHook, DebitAccountHook, Settlement } from "./core/Settlement.sol";
19
+ import { CreditAccountHook, DebitAccountHook, PostHook, SettleHook, Settlement } from "./core/Settlement.sol";
17
20
  import { Portal } from "./core/Portal.sol";
18
- import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
21
+ import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Position, Tx } from "./core/Types.sol";
19
22
  import { Validator } from "./core/Validator.sol";
20
23
 
21
24
 
package/Endpoints.sol CHANGED
@@ -5,7 +5,7 @@ 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 { CreditAccountHook, DebitAccountHook } from "./core/Settlement.sol";
8
+ import { CreditAccountHook, DebitAccountHook, PostHook, SettleHook } from "./core/Settlement.sol";
9
9
 
10
10
  // Commands
11
11
  import { Allocate, AllocateHook } from "./commands/Allocate.sol";
@@ -15,21 +15,21 @@ import { DebitAccount } from "./commands/Debit.sol";
15
15
  import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
16
16
  import { Payout, PayoutHook } from "./commands/Payout.sol";
17
17
  import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
18
- import { RecoverHook, RecoverPayable } from "./commands/Recover.sol";
19
- import { RelayPayable, RoutePayableHook } from "./commands/Relay.sol";
18
+ import { RecoverPayable, RecoverPayableHook } from "./commands/Recover.sol";
19
+ import { RelayPayable, RelayBalancePayable, RelayPayableHook } from "./commands/Relay.sol";
20
+ import { Settle } from "./commands/Settle.sol";
20
21
  import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
21
22
 
22
23
  // Admin commands
23
24
  import { AdminBase } from "./commands/admin/Base.sol";
24
25
  import { AllowAssets, AllowAssetsHook } from "./commands/admin/AllowAssets.sol";
25
26
  import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
27
+ import { Annotate } from "./commands/admin/Annotate.sol";
26
28
  import { Appoint } from "./commands/admin/Appoint.sol";
27
29
  import { Authorize } from "./commands/admin/Authorize.sol";
28
30
  import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
29
31
  import { Dismiss } from "./commands/admin/Dismiss.sol";
30
32
  import { ExecutePayable } from "./commands/admin/Execute.sol";
31
- import { Label } from "./commands/admin/Label.sol";
32
- import { PublishSchema } from "./commands/admin/Schemas.sol";
33
33
  import { Unauthorize } from "./commands/admin/Unauthorize.sol";
34
34
 
35
35
  // Port endpoints
@@ -42,7 +42,7 @@ import { PortDebitAccount } from "./ports/Debit.sol";
42
42
  import { PortDenyAssets } from "./ports/DenyAssets.sol";
43
43
  import { PortPipePayable } from "./ports/Pipe.sol";
44
44
  import { PortDispatchPayable } from "./ports/Dispatch.sol";
45
- import { PortSettle } from "./ports/Settle.sol";
45
+ import { PortPost } from "./ports/Post.sol";
46
46
 
47
47
  // Guard endpoints
48
48
  import { GuardBase } from "./guards/Base.sol";
package/Events.sol CHANGED
@@ -4,24 +4,22 @@ 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";
10
11
  import { CommanderEvent } from "./events/Commander.sol";
11
12
  import { DispatchEvent } from "./events/Dispatch.sol";
12
13
  import { EndpointEvent } from "./events/Endpoint.sol";
13
- import { PositionEvent } from "./events/Position.sol";
14
14
  import { ReceivedEvent } from "./events/Received.sol";
15
15
  import { RecoveredEvent } from "./events/Recovered.sol";
16
16
  import { EventEmitter } from "./events/Emitter.sol";
17
17
  import { GuardianEvent } from "./events/Guardian.sol";
18
18
  import { IntroductionEvent } from "./events/Introduction.sol";
19
- import { LabeledEvent } from "./events/Labeled.sol";
20
19
  import { LockedEvent } from "./events/Locked.sol";
21
20
  import { NodeEvent } from "./events/Node.sol";
22
21
  import { RootedEvent } from "./events/Rooted.sol";
23
22
  import { RouteEvent } from "./events/Route.sol";
24
- import { SchemaEvent } from "./events/Schema.sol";
25
23
  import { SpentEvent } from "./events/Spent.sol";
26
24
  import { UndeliveredEvent } from "./events/Undelivered.sol";
27
25
  import { UnlockedEvent } from "./events/Unlocked.sol";
package/README.md CHANGED
@@ -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,6 +47,29 @@ 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
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
@@ -126,7 +149,7 @@ const input = concat([
126
149
  ```
127
150
 
128
151
  Everything downstream keeps this shape: commands loop over input blocks,
129
- settlement loops over transactions, pipelines loop over steps. Batching is
152
+ posting loops over transactions, pipelines loop over steps. Batching is
130
153
  never a special case.
131
154
 
132
155
  ## IDs, Accounts, Assets, and Nodes
@@ -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
@@ -219,30 +248,51 @@ struct CommandContext {
219
248
 
220
249
  Every command returns two block streams: `state`, which is threaded into the
221
250
  next pipeline step, and `transactions`, which contains `#transaction` blocks
222
- for the pipeline host to settle outside the state lane. Either stream may be
251
+ for the pipeline host to post outside the state lane. Either stream may be
223
252
  empty.
224
253
 
225
- The input carries instructions; the state carries live value. While a
226
- sequence of commands executes, `#balance` and `#custody` blocks in the state
227
- are the funds being moved produced by one command, consumed by the next.
254
+ State is linear, not optional ambient context. A command is responsible for
255
+ the entire state stream it receives: it must validate and consume it, transform
256
+ and return it, forward it intact, or revert. A command must never succeed while
257
+ silently ignoring or dropping supplied state. Commands that declare
258
+ `Specs.Empty` state therefore reject any non-empty state, while commands that
259
+ accept state validate the complete stream against their declared state schema.
260
+ This is especially important for `#position`, because dropping a position could
261
+ silently discard both live value and an outstanding debt requirement.
228
262
 
229
- The standard `Deposit` mixin shows the canonical shape open the input,
230
- loop the batch, call the hook, write the output run:
263
+ The input carries instructions; the state carries live value. While a
264
+ sequence of commands executes, `#balance`, `#custody`, and `#position` blocks
265
+ in the state are the value being moved — produced by one command, consumed by
266
+ the next. A position carries an asset-liability pair as
267
+ `{ asset, amount, liability, debt }`.
268
+
269
+ `#position` is general live state rather than a lending-specific debt record.
270
+ It pairs value acquired or controlled with value owed or required. A command
271
+ may preserve or replace either side and return the resulting position for the
272
+ next step; `settle` terminally consumes the pair. This supports swaps,
273
+ borrowing, refinancing, collateral changes, callback obligations, cross-host
274
+ claims, fees, netting, and other multi-step operations. A position is a
275
+ transient representation and does not itself create or erase an obligation
276
+ recorded by an external system.
277
+
278
+ The standard `Deposit` mixin shows the canonical shape: open and validate both
279
+ command lanes, loop the batch, call the hook, and write the output run:
231
280
 
232
281
  ```solidity
233
282
  function deposit(
234
- CommandContext calldata c
283
+ bytes32 account,
284
+ bytes calldata state,
285
+ bytes calldata input
235
286
  ) external onlyCommand returns (bytes memory, bytes memory) {
236
- (Cur memory input, uint outputs) = openInput(c.input, descriptor);
237
- Writer memory output = Writers.allocBalances(outputs);
287
+ Execution memory exec = openCommand(state, input, descriptor, 0);
238
288
 
239
- while (input.i < input.len) {
240
- (bytes32 asset, uint amount) = input.unpackAmount();
241
- deposit(c.account, asset, amount); // host policy hook
242
- output.appendBalance(asset, amount);
289
+ while (exec.more()) {
290
+ (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
291
+ deposit(account, asset, amount); // host policy hook
292
+ exec.outputBalance(asset, amount);
243
293
  }
244
294
 
245
- return (end(output), "");
295
+ return close(exec, account);
246
296
  }
247
297
  ```
248
298
 
@@ -263,7 +313,13 @@ abstract contract MyCommand is CommandBase {
263
313
  bytes calldata state,
264
314
  bytes calldata input
265
315
  ) external onlyCommand returns (bytes memory, bytes memory) {
266
- // parse input, loop, return the output state run and any transactions
316
+ Execution memory exec = openCommand(state, input, descriptor, 0);
317
+ while (exec.more()) {
318
+ (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
319
+ // Apply command-specific behavior for this group.
320
+ exec.outputBalance(asset, amount);
321
+ }
322
+ return close(exec, account);
267
323
  }
268
324
  }
269
325
  ```
@@ -272,8 +328,10 @@ The standard commands cover the common ledger movements: `deposit` and
272
328
  `depositPayable` (external funds in), `withdraw` and `burn` (funds out),
273
329
  `debitAccount` and `creditAccount` (internal movements), `payout` (deliver
274
330
  state to other accounts), `allocate` (turn balance state into custody),
275
- `provision` (provision custody from an external allocation), and `relayPayable`
276
- (hand a pipeline to another portal).
331
+ `provision` (provision custody from an external allocation), `settle` (consume
332
+ asset-liability position state), `relayPayable` (relay a pipeline without
333
+ state), and `relayBalancePayable` (relay balance state and a pipeline to another
334
+ portal).
277
335
 
278
336
  ## Pipelines
279
337
 
@@ -287,7 +345,7 @@ step { uint cmd, uint resources, #bytes as input }
287
345
  Each step names a command, the resources it may spend, and its input.
288
346
  The returned state threads into the next command and the final state must be
289
347
  empty. Returned transactions do not enter the state lane; the pipeline passes
290
- each decoded transaction to the shared settlement implementation before
348
+ each decoded transaction to the shared posting implementation before
291
349
  running the next step. This is the core of `Pipeline.pipe`:
292
350
 
293
351
  ```solidity
@@ -299,11 +357,11 @@ while (cur.more()) {
299
357
  account,
300
358
  state,
301
359
  input,
302
- budget.use(resources)
360
+ budget.useResourceValue(resources)
303
361
  );
304
362
  while (transactions.more()) {
305
363
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = transactions.unpackTransaction();
306
- settle(from, to, asset, amount);
364
+ post(from, to, asset, amount);
307
365
  }
308
366
  }
309
367
  if (state.length != 0) revert UnexpectedState();
@@ -316,6 +374,31 @@ command batching — and `resources` is a chain-specific word interpreted by the
316
374
  portal adapter (on EVM, the low 128 bits are native value in wei, drawn from a
317
375
  shared budget), so the same pipeline bytes are meaningful to every port.
318
376
 
377
+ Hosts that implement a pipeline locally can inherit `InternalDebitAccount`,
378
+ `InternalCreditAccount`, and `InternalSettle` to advertise the canonical command
379
+ endpoints while routing their local command IDs through `executeDebitAccount`,
380
+ `executeCreditAccount`, and `executeSettle`. These adapters consume the
381
+ memory-backed pipeline state directly and avoid an external self-call. The host
382
+ dispatcher must reject nonzero step value before invoking them because all three
383
+ commands are non-funded.
384
+
385
+ Positions also support backward-composed pipelines. In an exact-output route,
386
+ the asset side can represent the desired result while the liability side
387
+ represents the value currently required upstream. Each hop consumes one
388
+ position, fulfills or transforms its current requirement, and returns the next
389
+ position:
390
+
391
+ ```txt
392
+ position(C, 100, C, 100)
393
+ → position(C, 100, B, 50)
394
+ → position(C, 100, A, 25)
395
+ → settle
396
+ ```
397
+
398
+ This is backward composition, not backward execution: `#step` blocks still
399
+ execute forward in their encoded order. Exact-output routing is only one use;
400
+ other commands may transform the asset side, the liability side, or both.
401
+
319
402
  ## Queries
320
403
 
321
404
  Queries are the read endpoints: view functions that take a block-stream input
@@ -336,15 +419,15 @@ the descriptor's lanes through the published block schemas.
336
419
  Ports are the host-to-host surfaces, callable only by trusted peer hosts. The two
337
420
  central ones are batches all the way down:
338
421
 
339
- - `portSettle` consumes `transaction { bytes32 from, bytes32 to, bytes32 asset,
422
+ - `portPost` consumes `transaction { bytes32 from, bytes32 to, bytes32 asset,
340
423
  uint amount }` blocks, debiting `from` and crediting `to` per
341
- block — how two hosts record settlement between their ledgers.
424
+ block — how two hosts post transactions between their ledgers.
342
425
  - `portPipePayable` consumes `context` blocks, each carrying an account, an
343
426
  initial state, and a run of steps — a complete pipeline delivered by another
344
427
  host, executed locally against the port call's shared value budget.
345
428
 
346
- This is also the cross-portal mechanism. `relayPayable` (or `portDispatchPayable`)
347
- wraps a pipe and addresses it to a portal, commonly the destination host ID;
429
+ This is also the cross-portal mechanism. `relayPayable`, `relayBalancePayable`,
430
+ or `portDispatchPayable` wraps a pipe and addresses it to a portal, commonly the destination host ID;
348
431
  a bridge adapter moves the **raw
349
432
  bytes**; the destination host parses them with the same cursor rules and runs
350
433
  the same pipeline loop. Nothing in the payload is EVM-specific — step commands
@@ -357,9 +440,9 @@ bytes and produce the same output bytes for every endpoint.
357
440
 
358
441
  Admin commands use the regular command shape but are gated to the host's admin
359
442
  account: trust management (`authorize`, `unauthorize`), guardian management
360
- (`appoint`, `dismiss`), metadata (`label`, `publishSchema`), asset gating (`allowAssets`,
361
- `denyAssets`, `allowance`), lifecycle (`init`, `destroy`), and raw calls
362
- (`executePayable`). Guards go the other way: direct actions guardians can take
443
+ (`appoint`, `dismiss`), metadata (`annotate`), optional asset gating
444
+ (`allowAssets`, `denyAssets`, `allowance`), and raw calls (`executePayable`).
445
+ Guards go the other way: direct actions guardians can take
363
446
  without any command context — the default is `revoke`, which lets a guardian
364
447
  drop a trusted node immediately.
365
448
 
@@ -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
+ }