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