@rootzero/contracts 1.11.0 → 1.12.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,32 @@
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.12.0
7
+
8
+ ### Breaking Changes
9
+
10
+ - Removed the generic `EndpointBase.schema(...)` helper. Use
11
+ `localSchema(...)` for context-local endpoint schemas or emit `Schema`
12
+ directly for custom/named keys.
13
+ - Replaced `isDebitAccount`, `isCreditAccount`, `isAuthorize`, and
14
+ `isUnauthorize` helper predicates with internal command ID fields:
15
+ `debitAccountId`, `creditAccountId`, `authorizeId`, and `unauthorizeId`.
16
+
17
+ ### Added
18
+
19
+ - Added `Keys.Local` as the default context-local block key and added
20
+ `EndpointBase.localSchema(...)` helpers for publishing local endpoint
21
+ schemas.
22
+ - Added the standard `#schema` block, `unpackSchema`, and an opt-in
23
+ `publishSchema` admin command for emitting schema claims from `#schema`
24
+ blocks.
25
+
26
+ ### Changed
27
+
28
+ - Updated the schema DSL documentation to allow any number of child blocks in
29
+ declaration order, including fixed fields before, after, or between child
30
+ blocks.
31
+
6
32
  ## 1.11.0
7
33
 
8
34
  ### Breaking Changes
package/Endpoints.sol CHANGED
@@ -31,6 +31,7 @@ import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
31
31
  import { Dismiss } from "./commands/admin/Dismiss.sol";
32
32
  import { ExecutePayable } from "./commands/admin/Execute.sol";
33
33
  import { Label } from "./commands/admin/Label.sol";
34
+ import { PublishSchema } from "./commands/admin/Schemas.sol";
34
35
  import { Unauthorize } from "./commands/admin/Unauthorize.sol";
35
36
 
36
37
  // Port endpoints
package/README.md CHANGED
@@ -89,8 +89,8 @@ request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
89
89
  port would parse; what differs per chain is how a host *resolves* the
90
90
  identifiers inside, never how the bytes are laid out.
91
91
 
92
- Schemas can express more than flat fields: a block may end in nested child
93
- blocks (`#bytes as payload` names a run of raw dynamic bytes), items can be
92
+ Schemas can express more than flat fields: a block may contain any number of
93
+ nested child blocks (`#bytes as payload` names raw dynamic bytes), items can be
94
94
  marked `maybe` (optional) or `many` (a list), and aliases and dotted field
95
95
  paths give off-chain tooling presentation names without changing a single byte
96
96
  on the wire. The full schema language is specified in
@@ -332,7 +332,7 @@ bytes and produce the same output bytes for every endpoint.
332
332
 
333
333
  Admin commands use the regular command shape but are gated to the host's admin
334
334
  account: trust management (`authorize`, `unauthorize`), guardian management
335
- (`appoint`, `dismiss`), naming (`label`), asset gating (`allowAssets`,
335
+ (`appoint`, `dismiss`), metadata (`label`, `publishSchema`), asset gating (`allowAssets`,
336
336
  `denyAssets`, `allowance`), lifecycle (`init`, `destroy`), and raw calls
337
337
  (`executePayable`). Guards go the other way: direct actions guardians can take
338
338
  without any command context — the default is `revoke`, which lets a guardian
@@ -726,6 +726,19 @@ library Cursors {
726
726
  cur.ensureAt(end);
727
727
  }
728
728
 
729
+ /// @notice Consume a SCHEMA block and return its fields.
730
+ /// @param cur Cursor; advanced past the SCHEMA block.
731
+ /// @return key Block key being defined.
732
+ /// @return body Schema DSL string describing the block payload body.
733
+ /// @return name Optional block alias.
734
+ function unpackSchema(Cur memory cur) internal pure returns (bytes4 key, string memory body, bytes32 name) {
735
+ uint end = cur.enter(Keys.Schema, 36 + Sizes.Header, 0);
736
+ key = cur.read4();
737
+ body = cur.unpackString();
738
+ name = cur.read32();
739
+ cur.ensureAt(end);
740
+ }
741
+
729
742
  /// @notice Consume a dynamic block with a single bytes32 payload.
730
743
  /// @param cur Cursor; advanced past the block.
731
744
  /// @param key Expected dynamic block key.
package/blocks/Keys.sol CHANGED
@@ -21,6 +21,8 @@ library Keys {
21
21
  bytes4 constant Empty = bytes4(0);
22
22
  /// @dev Wildcard key used in discovery when any block stream is accepted.
23
23
  bytes4 constant Any = 0xffffffff;
24
+ /// @dev Default context-local block key for hosts/endpoints that need one custom schema.
25
+ bytes4 constant Local = bytes4(uint32(1));
24
26
  /// @dev Input amount - (bytes32 asset, uint amount)
25
27
  bytes4 constant Amount = bytes4(keccak256("#amount"));
26
28
  /// @dev Ledger balance - (bytes32 asset, uint amount)
@@ -71,6 +73,8 @@ library Keys {
71
73
  bytes4 constant Bounty = bytes4(keccak256("#bounty"));
72
74
  /// @dev Mutable node label - (uint id, bytes32 namespace, #string as name)
73
75
  bytes4 constant Label = bytes4(keccak256("#label"));
76
+ /// @dev Block schema publication - (bytes4 key, #string as body, bytes32 name)
77
+ bytes4 constant Schema = bytes4(keccak256("#schema"));
74
78
 
75
79
  /// @dev Structural status form - (uint code)
76
80
  bytes4 constant Status = bytes4(keccak256("#status"));
package/blocks/Schema.sol CHANGED
@@ -28,9 +28,9 @@ pragma solidity ^0.8.33;
28
28
  // are offchain projection metadata only and do not change runtime encoding
29
29
  // - child blocks resolve by alias in the active schema context; unresolved aliases are invalid
30
30
  // - schema strings describe the payload body only; the `Block` event carries the alias
31
- // - fixed fields are packed in declaration order
32
- // - blocks have fixed fields followed by a dynamic child-block tail
33
- // - child block tails are embedded directly, without an extra stream wrapper
31
+ // - items are encoded in declaration order
32
+ // - fixed fields are packed inline and any number of child blocks are embedded directly
33
+ // - child blocks may appear between fixed fields because each block carries its own length
34
34
  // - `#bytes` is a reserved child block that stores raw bytes and has no body
35
35
  // - `#string` is a reserved child block that stores UTF-8 string bytes and has no body
36
36
  // - generic lists use the stable key derived from `#list`
@@ -87,6 +87,7 @@ library Schemas {
87
87
  string constant Fee = "{ uint amount }";
88
88
  string constant Auth = "{ uint cid, uint deadline, #bytes as proof }";
89
89
  string constant Label = "{ uint id, bytes32 namespace, #string as name }";
90
+ string constant Schema = "{ bytes4 key, #string as body, bytes32 name }";
90
91
  string constant Bytes = "";
91
92
  string constant String = "";
92
93
  string constant List = "";
@@ -20,15 +20,10 @@ abstract contract CreditAccountHook {
20
20
  /// Use for internally recording credits that have already been settled externally.
21
21
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
22
22
  bytes32 private immutable descriptor;
23
- uint private immutable id;
23
+ uint internal immutable creditAccountId;
24
24
 
25
25
  constructor() {
26
- (id, descriptor) = command("creditAccount", Keys.Balance, Keys.Empty, Keys.Empty, 0, false, false);
27
- }
28
-
29
- /// @notice Return true if `candidate` is this command's credit account ID.
30
- function isCreditAccount(uint candidate) internal view returns (bool) {
31
- return candidate == id;
26
+ (creditAccountId, descriptor) = command("creditAccount", Keys.Balance, Keys.Empty, Keys.Empty, 0, false, false);
32
27
  }
33
28
 
34
29
  /// @notice Credit each BALANCE block from the command state to the command account.
@@ -22,15 +22,10 @@ abstract contract DebitAccountHook {
22
22
  /// AMOUNT block; the default batch implementation handles the full request loop.
23
23
  abstract contract DebitAccount is CommandBase, DebitAccountHook {
24
24
  bytes32 private immutable descriptor;
25
- uint private immutable id;
25
+ uint internal immutable debitAccountId;
26
26
 
27
27
  constructor() {
28
- (id, descriptor) = command("debitAccount", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
29
- }
30
-
31
- /// @notice Return true if `candidate` is this command's debit account ID.
32
- function isDebitAccount(uint candidate) internal view returns (bool) {
33
- return candidate == id;
28
+ (debitAccountId, descriptor) = command("debitAccount", Keys.Empty, Keys.Amount, Keys.Balance, 0, false, false);
34
29
  }
35
30
 
36
31
  /// @notice Override to customize request parsing or batching for debits.
@@ -1,8 +1,8 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, CommandContext, Keys } from "./Base.sol";
5
- import { Cursors, Cur } from "../../Cursors.sol";
4
+ import {AdminBase, CommandContext, Keys} from "./Base.sol";
5
+ import {Cursors, Cur} from "../../Cursors.sol";
6
6
  using Cursors for Cur;
7
7
 
8
8
  /// @title Authorize
@@ -11,17 +11,16 @@ using Cursors for Cur;
11
11
  /// Only callable by the admin account.
12
12
  abstract contract Authorize is AdminBase {
13
13
  bytes32 private immutable descriptor;
14
+ uint internal immutable authorizeId;
14
15
 
15
16
  constructor() {
16
- (, descriptor) = command("authorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
17
+ (authorizeId, descriptor) = command("authorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
17
18
  }
18
19
 
19
20
  /// @notice Authorize each NODE block in the admin request.
20
21
  /// @param c Admin command context; `c.input` must contain NODE blocks.
21
22
  /// @return Empty output state.
22
- function authorize(
23
- CommandContext calldata c
24
- ) external onlyAdmin(c.account) returns (bytes memory) {
23
+ function authorize(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
25
24
  (Cur memory input, ) = openInput(c.input, descriptor);
26
25
 
27
26
  while (input.i < input.len) {
@@ -31,8 +30,3 @@ abstract contract Authorize is AdminBase {
31
30
  return "";
32
31
  }
33
32
  }
34
-
35
-
36
-
37
-
38
-
@@ -0,0 +1,31 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {AdminBase, CommandContext, Keys} from "./Base.sol";
5
+ import {Cursors, Cur} from "../../Cursors.sol";
6
+ using Cursors for Cur;
7
+
8
+ /// @title PublishSchema
9
+ /// @notice Admin command that publishes block schemas for keys.
10
+ /// Each SCHEMA block in the request emits one `Schema` event. Only callable by
11
+ /// the admin account.
12
+ abstract contract PublishSchema is AdminBase {
13
+ bytes32 private immutable descriptor;
14
+
15
+ constructor() {
16
+ (, descriptor) = command("publishSchema", Keys.Empty, Keys.Schema, Keys.Empty, 0, false, true);
17
+ }
18
+
19
+ /// @notice Publish each SCHEMA block in the admin request.
20
+ /// @param c Admin command context; `c.input` must contain SCHEMA blocks.
21
+ /// @return Empty output state.
22
+ function publishSchema(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
23
+ (Cur memory input, ) = openInput(c.input, descriptor);
24
+
25
+ while (input.i < input.len) {
26
+ (bytes4 key, string memory body, bytes32 name) = input.unpackSchema();
27
+ emit Schema(host, key, body, name);
28
+ }
29
+ return "";
30
+ }
31
+ }
@@ -1,8 +1,8 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, CommandContext, Keys } from "./Base.sol";
5
- import { Cursors, Cur } from "../../Cursors.sol";
4
+ import {AdminBase, CommandContext, Keys} from "./Base.sol";
5
+ import {Cursors, Cur} from "../../Cursors.sol";
6
6
  using Cursors for Cur;
7
7
 
8
8
  /// @title Unauthorize
@@ -11,17 +11,16 @@ using Cursors for Cur;
11
11
  /// Only callable by the admin account.
12
12
  abstract contract Unauthorize is AdminBase {
13
13
  bytes32 private immutable descriptor;
14
+ uint internal immutable unauthorizeId;
14
15
 
15
16
  constructor() {
16
- (, descriptor) = command("unauthorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
17
+ (unauthorizeId, descriptor) = command("unauthorize", Keys.Empty, Keys.Node, Keys.Empty, 0, false, true);
17
18
  }
18
19
 
19
20
  /// @notice Unauthorize each NODE block in the admin request.
20
21
  /// @param c Admin command context; `c.input` must contain NODE blocks.
21
22
  /// @return Empty output state.
22
- function unauthorize(
23
- CommandContext calldata c
24
- ) external onlyAdmin(c.account) returns (bytes memory) {
23
+ function unauthorize(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
25
24
  (Cur memory input, ) = openInput(c.input, descriptor);
26
25
 
27
26
  while (input.i < input.len) {
@@ -31,8 +30,3 @@ abstract contract Unauthorize is AdminBase {
31
30
  return "";
32
31
  }
33
32
  }
34
-
35
-
36
-
37
-
38
-
package/core/Endpoint.sol CHANGED
@@ -118,14 +118,21 @@ abstract contract EndpointBase is Runtime, EndpointEvent, LabeledEvent, SchemaEv
118
118
  (input, , outputs) = openLane(source, descriptor, Lane.Input, 0);
119
119
  }
120
120
 
121
- /// @notice Publish a block schema and return its key for descriptor construction.
122
- /// @param key Block key being defined.
121
+ /// @notice Publish the default local block schema and return `Keys.Local`.
123
122
  /// @param body Schema DSL string describing the block payload body.
124
- /// @param name Optional block alias used by descriptor tooling and nested schemas.
125
- /// @return The same block key, for inline descriptor construction.
126
- function schema(bytes4 key, string memory body, bytes32 name) internal returns (bytes4) {
127
- emit Schema(host, key, body, name);
128
- return key;
123
+ /// @return The default context-local block key.
124
+ function localSchema(string memory body) internal returns (bytes4) {
125
+ return localSchema(1, body);
126
+ }
127
+
128
+ /// @notice Publish a context-local block schema and return its key.
129
+ /// @param key Context-local key value.
130
+ /// @param body Schema DSL string describing the block payload body.
131
+ /// @return The context-local block key.
132
+ function localSchema(uint32 key, string memory body) internal returns (bytes4) {
133
+ bytes4 k = Keys.local(key);
134
+ emit Schema(host, k, body, bytes32(0));
135
+ return k;
129
136
  }
130
137
 
131
138
  /// @notice Create and publish endpoint metadata with a default label.
package/core/Host.sol CHANGED
@@ -23,7 +23,7 @@ interface IHostIntroduction {
23
23
 
24
24
  /// @title Host
25
25
  /// @notice Abstract base contract for rootzero host implementations.
26
- /// Inherits admin command support (authorize, unauthorize, executePayable),
26
+ /// Inherits admin command support (authorize, unauthorize, label, executePayable),
27
27
  /// guardian management, the default guardian revoke action, and
28
28
  /// optionally introduces itself to a commander host at deployment.
29
29
  /// Accepts native ETH payments via the `receive` function.
package/docs/Schema.md CHANGED
@@ -33,7 +33,7 @@ used. A host can publish the meaning of a custom key with:
33
33
  event Schema(uint indexed host, bytes4 key, string schema, bytes32 name);
34
34
  ```
35
35
 
36
- For example, a host-specific payment block can use `Keys.local(1)`, the command
36
+ For example, a host-specific payment block can use `Keys.Local`, the command
37
37
  selector, or any other chosen `bytes4` value as long as that key is not
38
38
  overloaded in the relevant host/schema context.
39
39
 
@@ -65,16 +65,19 @@ A schema body is a comma-separated list of items. Order is significant.
65
65
 
66
66
  ## Payload Layout
67
67
 
68
- A block payload has fixed fields first, followed by an optional child-block tail.
69
- Once a child block appears, no more fixed fields may follow.
68
+ A block payload encodes schema items in declaration order. Fixed fields are
69
+ packed inline, and zero or more child blocks are embedded directly at their
70
+ declared positions. Because every child block carries its own header and payload
71
+ length, fixed fields may appear before, after, or between child blocks.
70
72
 
71
73
  ```txt
72
74
  { uint target, uint resources, #bytes as payload }
73
75
  { bytes32 account, #bytes as state, #bytes as request }
76
+ { bytes4 key, #string as body, bytes32 name }
77
+ { #bytes as left, uint op, #bytes as right }
74
78
  ```
75
79
 
76
- The tail is embedded directly as child block bytes. There is no wrapper around a
77
- child-block tail.
80
+ There is no wrapper around embedded child blocks.
78
81
 
79
82
  Raw dynamic bytes are represented with the reserved `#bytes` child block. Use an
80
83
  alias to give those bytes a presentation name:
@@ -293,19 +296,21 @@ invalid in any path segment.
293
296
  ## Reserved Blocks
294
297
 
295
298
  - `#bytes`: raw dynamic bytes, written without a body
299
+ - `#string`: UTF-8 string bytes, written without a body
296
300
  - `#list`: generic list wrapper emitted by `many`
297
301
 
298
302
  Custom input shapes should define their own context-local block key and publish
299
303
  that key with a `Schema` event:
300
304
 
301
305
  ```solidity
302
- bytes4 constant Input = Keys.local(1);
306
+ bytes4 constant Input = Keys.Local;
303
307
  emit Schema(host, Input, "{ bytes32 asset, uint amount }", bytes32("payment"));
304
308
  ```
305
309
 
306
- The key can be a small literal, a selector, or any other `bytes4` value that is
307
- unique in the context where it is used. The alias names the block; the schema
308
- string describes only the payload body.
310
+ Use `Keys.local(n)` when a host needs more than one local block key. The key can
311
+ also be a small literal, a selector, or any other `bytes4` value that is unique
312
+ in the context where it is used. The alias names the block; the schema string
313
+ describes only the payload body.
309
314
 
310
315
  ## Standard Blocks
311
316
 
@@ -320,6 +325,7 @@ step { uint target, uint resources, #bytes as request }
320
325
  context { bytes32 account, #bytes as state, #bytes as request }
321
326
  recover { uint handler, uint resources, bytes32 key, #bytes as witness }
322
327
  auth { uint cid, uint deadline, #bytes as proof }
328
+ schema { bytes4 key, #string as body, bytes32 name }
323
329
  ```
324
330
 
325
331
  `Keys.sol` contains the corresponding standard runtime keys.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
5
5
  "private": false,
6
6
  "license": "GPL-3.0-only",