@rootzero/contracts 1.21.0 → 1.23.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 (54) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/Endpoints.sol +2 -3
  3. package/Events.sol +1 -1
  4. package/README.md +25 -19
  5. package/annotations/Action.sol +1 -1
  6. package/annotations/Label.sol +1 -1
  7. package/annotations/Schema.sol +36 -3
  8. package/codec/Blocks.sol +100 -29
  9. package/codec/Decoders.sol +62 -8
  10. package/codec/Readers.sol +39 -0
  11. package/codec/Schema.sol +44 -35
  12. package/codec/Writers.sol +8 -0
  13. package/commands/Allocate.sol +5 -8
  14. package/commands/Base.sol +29 -13
  15. package/commands/Burn.sol +5 -7
  16. package/commands/Credit.sol +5 -7
  17. package/commands/Debit.sol +5 -7
  18. package/commands/Deposit.sol +10 -14
  19. package/commands/Payout.sol +5 -8
  20. package/commands/Provision.sol +10 -14
  21. package/commands/Recover.sol +4 -6
  22. package/commands/Relay.sol +16 -22
  23. package/commands/Repay.sol +10 -14
  24. package/commands/Settle.sol +10 -14
  25. package/commands/Withdraw.sol +5 -7
  26. package/commands/admin/AllowAssets.sol +5 -7
  27. package/commands/admin/Allowance.sol +5 -7
  28. package/commands/admin/Annotate.sol +5 -7
  29. package/commands/admin/Appoint.sol +5 -7
  30. package/commands/admin/Authorize.sol +5 -7
  31. package/commands/admin/Base.sol +12 -3
  32. package/commands/admin/DenyAssets.sol +5 -7
  33. package/commands/admin/Dismiss.sol +5 -7
  34. package/commands/admin/Execute.sol +5 -7
  35. package/commands/admin/Unauthorize.sol +5 -7
  36. package/core/Calls.sol +72 -3
  37. package/core/Host.sol +7 -10
  38. package/core/Portal.sol +2 -2
  39. package/events/Dispatch.sol +1 -1
  40. package/events/Positioned.sol +22 -0
  41. package/events/Relay.sol +1 -1
  42. package/events/Route.sol +1 -1
  43. package/execution/Execution.sol +124 -4
  44. package/package.json +2 -3
  45. package/ports/Allowance.sol +22 -12
  46. package/ports/Assets.sol +99 -0
  47. package/ports/Dispatch.sol +2 -1
  48. package/utils/Cursors.sol +3 -3
  49. package/utils/Nodes.sol +1 -1
  50. package/utils/Utils.sol +31 -31
  51. package/docs/Schema.md +0 -427
  52. package/events/Commander.sol +0 -19
  53. package/ports/AllowAssets.sol +0 -34
  54. package/ports/DenyAssets.sol +0 -34
package/docs/Schema.md DELETED
@@ -1,427 +0,0 @@
1
- # Schema
2
-
3
- Rootzero input and response data is encoded as a stream of typed blocks. A
4
- schema string describes the payload body for discovery events and tooling; the
5
- runtime block key is the compact type tag that identifies that payload layout in
6
- the active schema context. The block alias is published separately from the
7
- payload schema.
8
-
9
- ## Wire Format
10
-
11
- Every block uses the same header:
12
-
13
- ```txt
14
- [bytes4 key][uint32 payloadLen][payload]
15
- ```
16
-
17
- `payloadLen` is big-endian and counts only payload bytes. Child blocks and list
18
- items use the same header format.
19
-
20
- Standard built-in block keys use:
21
-
22
- ```txt
23
- bytes4(keccak256("#name"))
24
- ```
25
-
26
- For example, the standard `amount` alias uses the key derived from `#amount`
27
- and the schema body `{ bytes32 asset, uint amount }`. Custom block keys do not
28
- have to be keccak-derived. They
29
- are opaque `bytes4` tags and only need to be unique in the context where they are
30
- used. A host can publish the meaning of a custom key as an annotation:
31
-
32
- ```solidity
33
- event Annotation(uint indexed entity, bytes data);
34
- #schema { uint spec, #string as body, bytes32 name }
35
- ```
36
-
37
- Annotation merge behavior is defined by the annotation block type rather than
38
- by the `Annotation` event. A `#schema` annotation is identified by its entity
39
- and the block key encoded in `spec`: distinct keys accumulate, while the latest
40
- trusted claim for the same key replaces the earlier one. Other annotation types
41
- may define additive, historical, or explicitly revocable behavior instead.
42
-
43
- The standard `#action { uint action }` annotation assigns one primary semantic
44
- action to an entity. The latest trusted value replaces the previous value, and
45
- `Actions.None` clears the classification.
46
-
47
- For example, a host-specific payment block can use a small literal, the command
48
- selector, or any other chosen `bytes4` value as long as that key is not
49
- overloaded in the relevant host/schema context.
50
-
51
- ## Block Syntax
52
-
53
- A block definition has an event alias and a schema body. A schema body is one
54
- of three forms:
55
-
56
- ```txt
57
- "" empty or raw payload
58
- { fields } structured payload
59
- many #item top-level custom list payload
60
- ```
61
-
62
- Fixed fields are written in braces:
63
-
64
- ```txt
65
- alias: amount
66
- schema: { bytes32 asset, uint amount }
67
- ```
68
-
69
- A block body can reference another block alias as a child item with `#`:
70
-
71
- ```txt
72
- { bytes32 account, #bytes as state, #bytes as input }
73
- ```
74
-
75
- The empty schema string `""` means the block has no structured payload. This is
76
- used for zero-payload blocks such as `#unit` and raw dynamic blocks such as
77
- `#bytes`. A root `many #item` body is reserved for an emitted custom schema
78
- whose own key identifies the outer list block.
79
-
80
- A structured schema body is a comma-separated list of items. Order is
81
- significant.
82
-
83
- ```txt
84
- { #amount, maybe #account as recipient }
85
- ```
86
-
87
- ## Payload Layout
88
-
89
- A block payload encodes schema items in declaration order. Fixed fields are
90
- packed inline, and zero or more child blocks are embedded directly at their
91
- declared positions. Because every child block carries its own header and payload
92
- length, fixed fields may appear before, after, or between child blocks.
93
-
94
- ```txt
95
- { uint target, uint resources, #bytes as payload }
96
- { bytes32 account, #bytes as state, #bytes as input }
97
- { uint spec, #string as body, bytes32 name }
98
- { #bytes as left, uint op, #bytes as right }
99
- ```
100
-
101
- There is no wrapper around embedded child blocks.
102
-
103
- Raw dynamic bytes are represented with the reserved `#bytes` child block. Use an
104
- alias to give those bytes a presentation name:
105
-
106
- ```txt
107
- #bytes as payload
108
- ```
109
-
110
- ## Modifiers
111
-
112
- Cardinality is expressed with prefix keywords:
113
-
114
- ```txt
115
- #balance
116
- maybe #balance
117
- many #balance
118
- maybe many #balance
119
- ```
120
-
121
- - no prefix: one required item
122
- - `maybe`: optional item
123
- - `many`: a list whose payload contains repeated items
124
- - `maybe many`: optional `#list` block
125
-
126
- `maybe` emits no placeholder when absent. Inside a structured schema body,
127
- `many` wraps repeated items in one generic `#list` block; it does not repeat the
128
- item in place:
129
-
130
- ```txt
131
- { uint id, many #asset as assets }
132
- ```
133
-
134
- When an emitted custom schema consists entirely of a top-level `many` item, the
135
- custom schema key identifies the outer list block instead. Its payload contains
136
- the repeated items directly:
137
-
138
- ```txt
139
- schema key: 0x00000001
140
- schema body: many #asset
141
- wire value: [0x00000001][length][ASSET][ASSET]...
142
- ```
143
-
144
- This convention gives a top-level list a discoverable, context-local type while
145
- retaining the generic `#list` key for lists whose type is supplied by an
146
- enclosing schema.
147
-
148
- ## Endpoint Lanes
149
-
150
- Endpoint descriptors identify each lane with a block key and group size. In
151
- Solidity, endpoint definition helpers accept block specs such as `Specs.Amount`.
152
- A zero group is interpreted as group size 1, while `Specs.Empty` means the
153
- endpoint has no blocks in that lane. Use `group(spec, size)` when a lane needs
154
- an explicit group size other than 1.
155
-
156
- The packed descriptor uses these lane layouts:
157
-
158
- ```txt
159
- state [key:4][group:1]
160
- input [key:4][group:1]
161
- output [key:4][min:4][max:4][hint:3][group:1]
162
- reserve [reserved:4]
163
- tx [transactions:1]
164
- flags [flags:1]
165
- ```
166
-
167
- Each lane directly identifies its top-level block key. Output lanes retain their
168
- size bounds and allocation hint so execution can reconstruct the output spec and
169
- initialize its writer directly. Four descriptor-level bytes are reserved after
170
- the lane metadata. The Solidity output decoder returns a left-aligned,
171
- writer-ready spec that retains its encoded group and clears its reserved fields.
172
- `Specs.group` returns the effective group, interpreting an encoded zero as one
173
- for a non-empty spec.
174
-
175
- Any non-empty lane resolves its key to a block alias and schema body through the
176
- active schema context. A top-level list lane uses the key of its emitted custom
177
- `many` schema; the descriptor treats it like every other direct lane spec.
178
-
179
- The lane key is the prime item. Prime items may repeat at the top level for
180
- batching. Later top-level items are globals for the whole batch and are not
181
- counted as per-operation prime blocks.
182
-
183
- The prime item cannot be optional. If an endpoint needs a per-operation marker
184
- with no payload, use a zero-payload block such as `#unit`.
185
-
186
- Endpoint descriptors currently use a narrower convention than the full block
187
- grammar: each state, input, or output lane is a single run of blocks, without
188
- additional global items. Endpoint decoder opening requires that run to consume
189
- the complete supplied lane; a trailing block with another key is invalid.
190
- Lower-level cursor scanning may still intentionally open only a prefix run.
191
- Those lower layers retain only the raw block count; descriptor strides are
192
- applied and lane groups reconciled once when an endpoint execution opens.
193
- Future protocol surfaces may use the more flexible top-level structure.
194
-
195
- For commands, complete-lane validation is also a state-safety rule. State is a
196
- linear value owned by the current pipeline step, not optional context that a
197
- command may disregard. Every command must account for the complete supplied
198
- state by consuming it, transforming and returning it, forwarding it intact, or
199
- reverting. A command whose descriptor declares an empty state lane must reject
200
- non-empty state. A command that accepts state must validate the complete stream
201
- against its declared schema; accepting only a prefix and silently dropping the
202
- remainder is invalid.
203
-
204
- ## Live Pipeline State
205
-
206
- `#balance`, `#custody`, and `#position` are live state carried between command
207
- steps for the active account. A position atomically pairs an asset side with a
208
- liability side:
209
-
210
- ```txt
211
- position { bytes32 asset, uint amount, bytes32 liability, uint debt }
212
- ```
213
-
214
- The pair is deliberately general. The asset side represents value acquired or
215
- controlled, and the liability side represents value owed or required. Commands
216
- may preserve or replace either side and return a new position. The terminal
217
- `settle` command consumes the pair. Position state is transient protocol state;
218
- rewriting it does not by itself create, discharge, or replace an obligation
219
- persisted by a host or external protocol. The responsible command hook must
220
- perform or verify those effects. A command must not ignore a supplied position:
221
- it must explicitly consume, transform, forward, or reject it, so neither its
222
- asset nor its debt can disappear accidentally.
223
-
224
- This representation supports ordinary forward transformations as well as
225
- backward composition. For example, an exact-output route can carry its desired
226
- asset while successive hops replace the upstream liability:
227
-
228
- ```txt
229
- position(C, 100, C, 100)
230
- → position(C, 100, B, 50)
231
- → position(C, 100, A, 25)
232
- → settle
233
- ```
234
-
235
- “Backward” describes how requirements are composed from the desired result
236
- toward the source. Pipeline execution is not reversed: STEP blocks always run
237
- forward in their encoded order. Exact-output routing is only an example;
238
- borrowing, refinancing, collateral transformation, callback obligations,
239
- cross-host claims, fees, and netting can use the same position state.
240
-
241
- ## Field Aliases
242
-
243
- Block aliases are published in `#schema` annotations. Field aliases are presentation
244
- metadata for tooling. They do not change payload layout or runtime keys.
245
-
246
- ```txt
247
- maybe #account as recipient
248
- { uint target, uint resources, #bytes as payload }
249
- ```
250
-
251
- Field aliases may be used on any block item, including child blocks and prime
252
- items.
253
-
254
- Child blocks are schema references:
255
-
256
- ```txt
257
- { uint handler, uint resources, bytes32 key, #bytes as witness }
258
- ```
259
-
260
- Alias resolution is context-dependent. A consumer may resolve `#context` from
261
- standard block events, from app-specific block events, or from another active
262
- schema context. Custom parents should define nested custom blocks from the
263
- bottom up and reference them by alias. Consumers should reject schemas with
264
- unresolved aliases. The runtime encoding is still an embedded child block with
265
- the referenced key and layout.
266
-
267
- ## Field Paths
268
-
269
- Field names and aliases may use dotted paths for offchain projection. A dotted
270
- path does not change the block key, payload bytes, payload length, cursor
271
- behavior, or any onchain validation. It is metadata only.
272
-
273
- ```txt
274
- { uint dst.portal, uint dst.resources, #bytes as dst.payload }
275
- ```
276
-
277
- This has the same runtime layout as:
278
-
279
- ```txt
280
- { uint portal, uint resources, #bytes as payload }
281
- ```
282
-
283
- Offchain tooling may decode the dotted form into a nested object:
284
-
285
- ```ts
286
- {
287
- dst: {
288
- portal,
289
- resources,
290
- payload
291
- }
292
- }
293
- ```
294
-
295
- Encoding and decoding must still follow schema declaration order, not object
296
- property order. Fields with the same path prefix do not need to be contiguous,
297
- although contiguous fields are easier to read when they represent one logical
298
- object.
299
-
300
- Tooling should reject duplicate full paths and prefix/value collisions:
301
-
302
- ```txt
303
- uint dst.portal, uint dst.portal // duplicate path
304
- uint dst, uint dst.portal // prefix/value collision
305
- ```
306
-
307
- The same rule applies to field aliases:
308
-
309
- ```txt
310
- { uint target, uint resources, #bytes as calldata.payload }
311
- maybe #account as recipient.account
312
- ```
313
-
314
- ## Field Types
315
-
316
- Supported field types are chain-neutral:
317
-
318
- ```txt
319
- uint, uint8, uint16, uint32, uint64, uint128, uint256
320
- int, int8, int16, int32, int64, int128, int256
321
- bool
322
- bytes1 through bytes32
323
- ```
324
-
325
- `uint` means `uint256`; `int` means `int256`. Other integer widths, unsized
326
- `bytes`, `string`, and array syntax are not part of the core schema DSL.
327
-
328
- Restricting fixed bytes to the power-of-two widths `bytes1`, `bytes2`,
329
- `bytes4`, `bytes8`, `bytes16`, and `bytes32` is under consideration, but has
330
- not been decided. Until that decision is made, the schema DSL continues to
331
- allow every `bytesN` width from 1 through 32.
332
-
333
- Integers are encoded big-endian. Signed integers use two's-complement encoding
334
- for their declared width. `bool` is one byte: `0x00` for false and `0x01` for
335
- true. `bytesN` values are encoded as exactly `N` bytes with no padding.
336
-
337
- ## Chain Resources
338
-
339
- Fields named `portal` are routing identifiers; they are often the destination
340
- host ID, but a transport adapter may define a different stable handle.
341
-
342
- Fields named `resources` are chain-specific resource words. A portal adapter
343
- interprets them for the destination runtime. Different runtimes may pack these
344
- words differently, but a given runtime must use one stable format everywhere.
345
- For EVM chains, the low 128 bits are native value / endowment in wei; higher
346
- bits are reserved for execution resources such as gas.
347
-
348
- ## Protocol IDs
349
-
350
- Account, asset, and node ID fields use one 32-byte convention:
351
-
352
- - first byte `0x00`: opaque ID, encoded as `0x00 || bytes31(hash)`. The full
353
- preimage must come from a lookup table or witness data when native metadata is
354
- needed.
355
- - first byte nonzero: structured ID. The value may be deconstructed according
356
- to its chain/runtime layout.
357
-
358
- Opaque preimages must start with a one-byte format/hash tag; `0x01` means
359
- keccak256. The remaining bytes are host/domain-specific until the protocol
360
- standardizes a fuller preimage payload format.
361
-
362
- The field name supplies the protocol role for opaque IDs. For example, a
363
- `bytes32 asset` whose first byte is zero is still an asset in that block; it
364
- just cannot be decoded without external context. Runtime helpers that inspect
365
- the layout of an ID only apply to structured IDs.
366
-
367
- ## Identifiers
368
-
369
- Block aliases use lower camelCase ASCII identifiers. Field names and aliases
370
- use one or more lower camelCase path segments separated by dots:
371
-
372
- ```txt
373
- [a-z][a-zA-Z0-9]*
374
- [a-z][a-zA-Z0-9]*(\.[a-z][a-zA-Z0-9]*)*
375
- ```
376
-
377
- Invalid examples:
378
-
379
- ```txt
380
- Amount
381
- asset_meta
382
- asset-meta
383
- 0account
384
- asset.
385
- .asset
386
- ```
387
-
388
- Reserved words include `maybe`, `many`, `as`, all field type names, and the
389
- reserved block aliases `bytes` and `list`. For dotted paths, reserved words are
390
- invalid in any path segment.
391
-
392
- ## Reserved Blocks
393
-
394
- - `#bytes`: raw dynamic bytes, written without a body
395
- - `#string`: UTF-8 string bytes, written without a body
396
- - `#list`: generic list wrapper emitted by nested `many`
397
-
398
- Custom input shapes should define their own context-local block spec and publish
399
- it with a `#schema` annotation. Endpoint contracts can use `schema(...)` to
400
- construct and publish that spec:
401
-
402
- ```solidity
403
- uint input = schema(1, 64, 64, 64, "{ bytes32 asset, uint amount }", bytes32(0));
404
- ```
405
-
406
- Use different numeric keys when a host needs more than one local block key. The
407
- key can also be a selector or any other `bytes4` value that is unique in the
408
- context where it is used. The numeric arguments after the key are the minimum,
409
- maximum, and allocation hint payload sizes. The alias names the block; the
410
- schema string describes only the payload body.
411
-
412
- ## Standard Blocks
413
-
414
- Common protocol schemas live in `contracts/codec/Schema.sol`:
415
-
416
- ```txt
417
- amount { bytes32 asset, uint amount }
418
- balance { bytes32 asset, uint amount }
419
- custody { uint host, bytes32 asset, uint amount }
420
- call { uint target, uint resources, #bytes as payload }
421
- step { uint cmd, uint resources, #bytes as input }
422
- context { bytes32 account, #bytes as state, #bytes as input }
423
- recover { uint handler, uint resources, bytes32 key, #bytes as witness }
424
- schema { uint spec, #string as body, bytes32 name }
425
- ```
426
-
427
- `Keys.sol` contains the corresponding standard runtime keys.
@@ -1,19 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {EventEmitter} from "./Emitter.sol";
5
-
6
- /// @notice Emitted when a commander is announced for a chain/domain.
7
- abstract contract CommanderEvent is EventEmitter {
8
- string private constant ABI = "event Commander(uint indexed host, uint chain, bytes32 native, bytes32 admin)";
9
-
10
- /// @param host Commander host node ID for the chain.
11
- /// @param chain Chain/domain node ID.
12
- /// @param native Native asset ID for the chain.
13
- /// @param admin Admin account for the commander host on the chain.
14
- event Commander(uint indexed host, uint chain, bytes32 native, bytes32 admin);
15
-
16
- constructor() {
17
- emit EventAbi(ABI);
18
- }
19
- }
@@ -1,34 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {PortBase} from "./Base.sol";
5
- import {AllowAssetsHook} from "../commands/admin/AllowAssets.sol";
6
- import {Specs} from "../Codec.sol";
7
- import {Execution, Executions, Lanes} from "../execution/Execution.sol";
8
-
9
- using Executions for Execution;
10
-
11
- /// @title AllowAssetsPort
12
- /// @notice Port that permits a list of assets on behalf of a peer host.
13
- /// Each ASSET block in the input calls `allowAsset`. Restricted to trusted peers.
14
- abstract contract AllowAssetsPort is PortBase, AllowAssetsHook {
15
- uint private immutable descriptor;
16
-
17
- constructor() {
18
- (, descriptor) = port("portAllowAssets", Specs.Asset, Specs.Empty, 0);
19
- }
20
-
21
- /// @notice Execute the allow-assets peer call.
22
- /// @param data ASSET block stream supplied by the trusted peer.
23
- /// @return Empty response bytes.
24
- function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- Execution memory exec = openInput(data, descriptor, 0);
26
-
27
- while (exec.more()) {
28
- bytes32 asset = exec.unpackAsset(Lanes.Input);
29
- allowAsset(asset);
30
- }
31
-
32
- return "";
33
- }
34
- }
@@ -1,34 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {PortBase} from "./Base.sol";
5
- import {DenyAssetsHook} from "../commands/admin/DenyAssets.sol";
6
- import {Specs} from "../Codec.sol";
7
- import {Execution, Executions, Lanes} from "../execution/Execution.sol";
8
-
9
- using Executions for Execution;
10
-
11
- /// @title DenyAssetsPort
12
- /// @notice Port that blocks a list of assets on behalf of a peer host.
13
- /// Each ASSET block in the input calls `denyAsset`. Restricted to trusted peers.
14
- abstract contract DenyAssetsPort is PortBase, DenyAssetsHook {
15
- uint private immutable descriptor;
16
-
17
- constructor() {
18
- (, descriptor) = port("portDenyAssets", Specs.Asset, Specs.Empty, 0);
19
- }
20
-
21
- /// @notice Execute the deny-assets peer call.
22
- /// @param data ASSET block stream supplied by the trusted peer.
23
- /// @return Empty response bytes.
24
- function portDenyAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- Execution memory exec = openInput(data, descriptor, 0);
26
-
27
- while (exec.more()) {
28
- bytes32 asset = exec.unpackAsset(Lanes.Input);
29
- denyAsset(asset);
30
- }
31
-
32
- return "";
33
- }
34
- }