@rootzero/contracts 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/Endpoints.sol +10 -7
  3. package/Events.sol +1 -0
  4. package/README.md +300 -79
  5. package/blocks/Cursors.sol +49 -14
  6. package/blocks/Keys.sol +4 -0
  7. package/blocks/Schema.sol +10 -4
  8. package/blocks/Writers.sol +16 -0
  9. package/commands/Base.sol +10 -9
  10. package/commands/Burn.sol +4 -5
  11. package/commands/Credit.sol +4 -5
  12. package/commands/Debit.sol +4 -5
  13. package/commands/Deposit.sol +8 -10
  14. package/commands/Payout.sol +5 -6
  15. package/commands/Provision.sol +8 -10
  16. package/commands/Relay.sol +4 -5
  17. package/commands/Withdraw.sol +4 -5
  18. package/commands/admin/AllowAssets.sol +4 -5
  19. package/commands/admin/Allowance.sol +4 -4
  20. package/commands/admin/Appoint.sol +4 -5
  21. package/commands/admin/Authorize.sol +4 -5
  22. package/commands/admin/DenyAssets.sol +4 -5
  23. package/commands/admin/Destroy.sol +3 -4
  24. package/commands/admin/Dismiss.sol +4 -5
  25. package/commands/admin/Execute.sol +4 -5
  26. package/commands/admin/Init.sol +3 -4
  27. package/commands/admin/Label.sol +35 -0
  28. package/commands/admin/Unauthorize.sol +4 -5
  29. package/core/Host.sol +7 -11
  30. package/core/Pipeline.sol +1 -1
  31. package/docs/Schema.md +59 -2
  32. package/events/Admin.sol +3 -9
  33. package/events/Chain.sol +2 -3
  34. package/events/Command.sol +3 -9
  35. package/events/Guard.sol +2 -3
  36. package/events/Introduction.sol +2 -4
  37. package/events/Labeled.sol +21 -0
  38. package/events/Peer.sol +2 -11
  39. package/events/Query.sol +2 -3
  40. package/events/Transfer.sol +1 -1
  41. package/guards/Base.sol +7 -6
  42. package/guards/Revoke.sol +4 -5
  43. package/package.json +1 -1
  44. package/peer/AllowAssets.sol +9 -5
  45. package/peer/Allowance.sol +9 -5
  46. package/peer/BalancePull.sol +9 -5
  47. package/peer/Base.sol +7 -6
  48. package/peer/Credit.sol +39 -0
  49. package/peer/Debit.sol +39 -0
  50. package/peer/DenyAssets.sol +9 -5
  51. package/peer/Dispatch.sol +9 -5
  52. package/peer/Pipe.sol +9 -5
  53. package/peer/Settle.sol +9 -5
  54. package/queries/Assets.sol +4 -4
  55. package/queries/Balances.sol +4 -4
  56. package/queries/Base.sol +9 -8
  57. package/queries/Positions.sol +4 -5
package/blocks/Schema.sol CHANGED
@@ -11,26 +11,30 @@ pragma solidity ^0.8.33;
11
11
  // - a block without braces has no payload, e.g. `#unit`
12
12
  // - commas separate siblings at every level
13
13
  // - braces define parent-child boundaries
14
- // - command requests start with the input run when the request schema is non-empty
15
- // - postcheck command requests include a constraint run after the input run; if the
16
- // request schema is empty, the constraint run starts the request
17
- // - command state starts with the active state run; trailing state globals may follow
14
+ // - command requests are a single run when the request schema is non-empty
15
+ // - command state is a single active state run without trailing globals
18
16
  // - run items may repeat at top level for batching
19
17
  // - `maybe #x { ... }` marks an optional block item
20
18
  // - `many #x { ... }` emits one generic list block containing repeated `#x` items
21
19
  // - `resources` fields are chain-specific resource words; one chain type may
22
20
  // pack them differently from another, but a given chain type must use one
23
21
  // stable format everywhere. EVM resources use the low 128 bits as native value.
22
+ // - dotted field names and aliases, e.g. `dst.chain` or `#bytes as dst.payload`,
23
+ // are offchain projection metadata only and do not change runtime encoding
24
24
  // - fixed fields are packed in declaration order
25
25
  // - blocks have fixed fields followed by a dynamic child-block tail
26
26
  // - child block tails are embedded directly, without an extra stream wrapper
27
27
  // - `#bytes` is a reserved child block that stores raw bytes and has no body
28
+ // - `#string` is a reserved child block that stores UTF-8 string bytes and has no body
28
29
  // - generic `#data` uses the stable key derived from `#data`
29
30
  // - generic lists use the stable key derived from `#list`
30
31
  // - keys are derived from block names, e.g. bytes4(keccak256("#amount"))
31
32
  // - see `docs/Schema.md` for the full working spec
32
33
  //
33
34
  // Pipeline state:
35
+ // - command request and state streams are each a single run of blocks under the
36
+ // current protocol convention; the block format may support other shapes in
37
+ // future protocol surfaces
34
38
  // - `balance(...)` and `custody(...)` are live, linear state in the active command pipeline
35
39
  // - pipeline state belongs to the active account while the pipeline is executing
36
40
  // - while a balance or custody is in-flight as pipeline state, it is not simultaneously persisted
@@ -73,7 +77,9 @@ library Schemas {
73
77
  string constant Bounty = "#bounty { uint amount, bytes32 relayer }";
74
78
  string constant Fee = "#fee { uint amount }";
75
79
  string constant Auth = "#auth { uint cid, uint deadline, #bytes as proof }";
80
+ string constant Label = "#label { uint id, bytes32 namespace, #string as name }";
76
81
  string constant Bytes = "#bytes";
82
+ string constant String = "#string";
77
83
  string constant Data = "#data";
78
84
  string constant List = "#list";
79
85
  string constant Evm = "#evm";
@@ -24,6 +24,7 @@ struct Writer {
24
24
  library Hints {
25
25
  uint constant Any = 128;
26
26
  uint constant Bytes = 128;
27
+ uint constant String = 128;
27
28
  uint constant Step = 256;
28
29
  uint constant Call = 256;
29
30
  uint constant Context = 512;
@@ -116,6 +117,14 @@ library Writers {
116
117
  return allocFromHint(count, Hints.Bytes);
117
118
  }
118
119
 
120
+ /// @notice Allocate a writer for `count` STRING blocks using a per-block capacity hint.
121
+ /// @dev The backing buffer expands automatically if encoded string blocks exceed the initial hint.
122
+ /// @param count Number of string blocks to allocate space for.
123
+ /// @return writer Allocated growable writer.
124
+ function allocStrings(uint count) internal pure returns (Writer memory writer) {
125
+ return allocFromHint(count, Hints.String);
126
+ }
127
+
119
128
  /// @notice Allocate a writer sized for exactly `count` 32-byte-payload blocks.
120
129
  /// @param count Number of blocks to allocate space for.
121
130
  /// @return writer Allocated writer.
@@ -862,6 +871,13 @@ library Writers {
862
871
  appendBlock(writer, Keys.Bytes, data);
863
872
  }
864
873
 
874
+ /// @notice Append a STRING block.
875
+ /// @param writer Destination writer; `i` is advanced by the encoded STRING block length.
876
+ /// @param data String payload.
877
+ function appendString(Writer memory writer, string memory data) internal pure {
878
+ appendBlock(writer, Keys.String, bytes(data));
879
+ }
880
+
865
881
  /// @notice Append a STEP block with a nested request BYTES payload.
866
882
  /// @param writer Destination writer; `i` is advanced by the encoded STEP block length.
867
883
  /// @param target Command target identifier.
package/commands/Base.sol CHANGED
@@ -3,8 +3,9 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {NodeCalls} from "../core/Calls.sol";
5
5
  import {CommandEvent} from "../events/Command.sol";
6
+ import {LabeledEvent} from "../events/Labeled.sol";
6
7
  import {Keys} from "../blocks/Keys.sol";
7
- import {Ids, Selectors} from "../utils/Ids.sol";
8
+ import {Ids} from "../utils/Ids.sol";
8
9
 
9
10
  /// @notice Execution context passed to every command invocation.
10
11
  struct CommandContext {
@@ -19,8 +20,8 @@ struct CommandContext {
19
20
  /// @title CommandBase
20
21
  /// @notice Abstract base for all rootzero command contracts.
21
22
  /// Provides access control modifiers, event emission, and the `commandId`
22
- /// helper used to derive stable identifiers for named commands.
23
- abstract contract CommandBase is NodeCalls, CommandEvent {
23
+ /// helper used to derive stable identifiers for command selectors.
24
+ abstract contract CommandBase is NodeCalls, CommandEvent, LabeledEvent {
24
25
  /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
25
26
  error Expired();
26
27
 
@@ -44,12 +45,12 @@ abstract contract CommandBase is NodeCalls, CommandEvent {
44
45
  _;
45
46
  }
46
47
 
47
- /// @notice Derive the deterministic node ID for a named command on this contract.
48
- /// The ID encodes the ABI selector of `name((bytes32,bytes,bytes))` and
49
- /// `address(this)`, making it unique per (function name, contract address) pair.
50
- /// @param name Command function name (without argument list).
48
+ /// @notice Derive the deterministic node ID for a command selector on this contract.
49
+ /// The ID encodes the ABI selector and `address(this)`, making it unique
50
+ /// per (function selector, contract address) pair.
51
+ /// @param selector Command entrypoint selector.
51
52
  /// @return Command node ID.
52
- function commandId(string memory name) internal view returns (uint) {
53
- return Ids.toCommand(Selectors.command(name), address(this));
53
+ function commandId(bytes4 selector) internal view returns (uint) {
54
+ return Ids.toCommand(selector, address(this));
54
55
  }
55
56
  }
package/commands/Burn.sol CHANGED
@@ -20,19 +20,18 @@ abstract contract BurnHook {
20
20
  /// @notice Command that irreversibly destroys each BALANCE state block via a virtual hook.
21
21
  /// Produces no output state.
22
22
  abstract contract Burn is CommandBase, BurnHook {
23
- string private constant NAME = "burn";
24
-
25
- uint internal immutable burnId = commandId(NAME);
23
+ uint internal immutable burnId = commandId(this.burn.selector);
26
24
 
27
25
  constructor() {
28
- emit Command(host, burnId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
26
+ emit Command(host, burnId, "0:1:0", "", Keys.Balance, Keys.Empty, false);
27
+ emit Labeled(burnId, bytes32(0), "burn");
29
28
  }
30
29
 
31
30
  /// @notice Burn each BALANCE block from the command state.
32
31
  /// @param c Command context; `c.state` must contain BALANCE blocks.
33
32
  /// @return Empty output state.
34
33
  function burn(CommandContext calldata c) external onlyCommand returns (bytes memory) {
35
- (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
34
+ (Cur memory state, , ) = Cursors.init(c.state, 1);
36
35
 
37
36
  while (state.i < state.len) {
38
37
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
@@ -20,12 +20,11 @@ abstract contract CreditAccountHook {
20
20
  /// @notice Command that delivers BALANCE state blocks to an account via a virtual hook.
21
21
  /// Use for internally recording credits that have already been settled externally.
22
22
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
23
- string private constant NAME = "creditAccount";
24
-
25
- uint internal immutable creditAccountId = commandId(NAME);
23
+ uint internal immutable creditAccountId = commandId(this.creditAccount.selector);
26
24
 
27
25
  constructor() {
28
- emit Command(host, creditAccountId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
26
+ emit Command(host, creditAccountId, "0:1:0", "", Keys.Balance, Keys.Empty, false);
27
+ emit Labeled(creditAccountId, bytes32(0), "creditAccount");
29
28
  }
30
29
 
31
30
  /// @notice Credit each BALANCE block from the command state to the command account.
@@ -34,7 +33,7 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
34
33
  function creditAccount(
35
34
  CommandContext calldata c
36
35
  ) external onlyCommand returns (bytes memory) {
37
- (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
36
+ (Cur memory state, , ) = Cursors.init(c.state, 1);
38
37
 
39
38
  while (state.i < state.len) {
40
39
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
@@ -22,19 +22,18 @@ abstract contract DebitAccountHook {
22
22
  /// Use for internally recording debits. The virtual `debitAccount` hook is called once per
23
23
  /// AMOUNT block; the default batch implementation handles the full request loop.
24
24
  abstract contract DebitAccount is CommandBase, DebitAccountHook {
25
- string private constant NAME = "debitAccount";
26
-
27
- uint internal immutable debitAccountId = commandId(NAME);
25
+ uint internal immutable debitAccountId = commandId(this.debitAccount.selector);
28
26
 
29
27
  constructor() {
30
- emit Command(host, debitAccountId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, false);
28
+ emit Command(host, debitAccountId, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
29
+ emit Labeled(debitAccountId, bytes32(0), "debitAccount");
31
30
  }
32
31
 
33
32
  /// @notice Override to customize request parsing or batching for debits.
34
33
  /// The default implementation iterates AMOUNT blocks, calls
35
34
  /// `debitAccount`, and emits matching BALANCE blocks.
36
35
  function debitAccount(bytes32 account, bytes calldata request) internal virtual returns (bytes memory) {
37
- (Cur memory input, uint groups, ) = Cursors.init(request, 0, 1);
36
+ (Cur memory input, uint groups, ) = Cursors.init(request, 1);
38
37
  Writer memory writer = Writers.allocBalances(groups);
39
38
 
40
39
  while (input.i < input.len) {
@@ -37,12 +37,11 @@ abstract contract DepositPayableHook {
37
37
  /// Use `deposit` for assets arriving from outside the protocol (e.g. ERC-20 transfers, ETH).
38
38
  /// For internal balance deductions, use `debitAccount` instead.
39
39
  abstract contract Deposit is CommandBase, DepositHook {
40
- string private constant NAME = "deposit";
41
-
42
- uint internal immutable depositId = commandId(NAME);
40
+ uint internal immutable depositId = commandId(this.deposit.selector);
43
41
 
44
42
  constructor() {
45
- emit Command(host, depositId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, false);
43
+ emit Command(host, depositId, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
44
+ emit Labeled(depositId, bytes32(0), "deposit");
46
45
  }
47
46
 
48
47
  /// @notice Deposit AMOUNT request blocks into the command account and output matching BALANCE blocks.
@@ -51,7 +50,7 @@ abstract contract Deposit is CommandBase, DepositHook {
51
50
  function deposit(
52
51
  CommandContext calldata c
53
52
  ) external onlyCommand returns (bytes memory) {
54
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
53
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
55
54
  Writer memory writer = Writers.allocBalances(groups);
56
55
 
57
56
  while (request.i < request.len) {
@@ -69,12 +68,11 @@ abstract contract Deposit is CommandBase, DepositHook {
69
68
  /// @notice Command that receives externally sourced assets and records them as BALANCE state.
70
69
  /// Use `depositPayable` when the hook needs tracked access to `msg.value` via a mutable budget.
71
70
  abstract contract DepositPayable is CommandBase, Payable, DepositPayableHook {
72
- string private constant NAME = "depositPayable";
73
-
74
- uint internal immutable depositPayableId = commandId(NAME);
71
+ uint internal immutable depositPayableId = commandId(this.depositPayable.selector);
75
72
 
76
73
  constructor() {
77
- emit Command(host, depositPayableId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, true);
74
+ emit Command(host, depositPayableId, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, true);
75
+ emit Labeled(depositPayableId, bytes32(0), "depositPayable");
78
76
  }
79
77
 
80
78
  /// @notice Deposit AMOUNT request blocks with access to a mutable native-value budget.
@@ -83,7 +81,7 @@ abstract contract DepositPayable is CommandBase, Payable, DepositPayableHook {
83
81
  function depositPayable(
84
82
  CommandContext calldata c
85
83
  ) external payable onlyCommand returns (bytes memory) {
86
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
84
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
87
85
  Writer memory writer = Writers.allocBalances(groups);
88
86
  Budget memory budget = valueBudget();
89
87
 
@@ -22,20 +22,19 @@ abstract contract PayoutHook {
22
22
  /// @notice Command that sinks BALANCE state blocks to matching ACCOUNT request blocks.
23
23
  /// Each BALANCE block is paired with one ACCOUNT block at the same position.
24
24
  abstract contract Payout is CommandBase, PayoutHook {
25
- string private constant NAME = "payout";
26
-
27
- uint internal immutable payoutId = commandId(NAME);
25
+ uint internal immutable payoutId = commandId(this.payout.selector);
28
26
 
29
27
  constructor() {
30
- emit Command(host, payoutId, NAME, "1:1:0", Schemas.Account, Keys.Balance, Keys.Empty, false, false);
28
+ emit Command(host, payoutId, "1:1:0", Schemas.Account, Keys.Balance, Keys.Empty, false);
29
+ emit Labeled(payoutId, bytes32(0), "payout");
31
30
  }
32
31
 
33
32
  /// @notice Pay out BALANCE state blocks to matching ACCOUNT request blocks.
34
33
  /// @param c Command context; `c.state` must contain BALANCE blocks and `c.request` matching ACCOUNT blocks.
35
34
  /// @return Empty output state.
36
35
  function payout(CommandContext calldata c) external onlyCommand returns (bytes memory) {
37
- (Cur memory state, uint groups, ) = Cursors.init(c.state, 0, 1);
38
- (Cur memory request, ) = Cursors.init(c.request, 0, 1, groups);
36
+ (Cur memory state, uint groups, ) = Cursors.init(c.state, 1);
37
+ (Cur memory request, ) = Cursors.init(c.request, 1, groups);
39
38
 
40
39
  while (state.i < state.len) {
41
40
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
@@ -33,19 +33,18 @@ abstract contract ProvisionPayableHook {
33
33
  /// @notice Command that provisions assets to peer hosts from ALLOCATION request blocks.
34
34
  /// Each request block supplies the target host plus an asset amount; the output is a CUSTODY state stream.
35
35
  abstract contract Provision is CommandBase, ProvisionHook {
36
- string private constant NAME = "provision";
37
-
38
- uint internal immutable provisionId = commandId(NAME);
36
+ uint internal immutable provisionId = commandId(this.provision.selector);
39
37
 
40
38
  constructor() {
41
- emit Command(host, provisionId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false, false);
39
+ emit Command(host, provisionId, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false);
40
+ emit Labeled(provisionId, bytes32(0), "provision");
42
41
  }
43
42
 
44
43
  /// @notice Provision ALLOCATION request blocks and output matching CUSTODY state blocks.
45
44
  /// @param c Command context; `c.request` must contain ALLOCATION blocks.
46
45
  /// @return CUSTODY block stream matching the provisioned allocations.
47
46
  function provision(CommandContext calldata c) external onlyCommand returns (bytes memory) {
48
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
47
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
49
48
  Writer memory writer = Writers.allocCustodies(groups);
50
49
 
51
50
  while (request.i < request.len) {
@@ -64,12 +63,11 @@ abstract contract Provision is CommandBase, ProvisionHook {
64
63
  /// Each request block supplies the target host plus an asset amount; the output is a CUSTODY state stream.
65
64
  /// The hook receives a mutable native-value budget drawn from `msg.value`.
66
65
  abstract contract ProvisionPayable is CommandBase, Payable, ProvisionPayableHook {
67
- string private constant NAME = "provisionPayable";
68
-
69
- uint internal immutable provisionPayableId = commandId(NAME);
66
+ uint internal immutable provisionPayableId = commandId(this.provisionPayable.selector);
70
67
 
71
68
  constructor() {
72
- emit Command(host, provisionPayableId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false, true);
69
+ emit Command(host, provisionPayableId, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, true);
70
+ emit Labeled(provisionPayableId, bytes32(0), "provisionPayable");
73
71
  }
74
72
 
75
73
  /// @notice Provision ALLOCATION request blocks with access to a mutable native-value budget.
@@ -78,7 +76,7 @@ abstract contract ProvisionPayable is CommandBase, Payable, ProvisionPayableHook
78
76
  function provisionPayable(
79
77
  CommandContext calldata c
80
78
  ) external payable onlyCommand returns (bytes memory) {
81
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
79
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
82
80
  Writer memory writer = Writers.allocCustodies(groups);
83
81
  Budget memory budget = valueBudget();
84
82
 
@@ -25,19 +25,18 @@ abstract contract DispatchPayableHook {
25
25
  /// the same state from being duplicated across multiple relays.
26
26
  /// Produces no output state.
27
27
  abstract contract RelayPayable is CommandBase, Payable, DispatchPayableHook {
28
- string private constant NAME = "relayPayable";
29
-
30
- uint internal immutable relayPayableId = commandId(NAME);
28
+ uint internal immutable relayPayableId = commandId(this.relayPayable.selector);
31
29
 
32
30
  constructor() {
33
- emit Command(host, relayPayableId, NAME, "1:0:0", Schemas.Relay, Keys.Any, Keys.Empty, false, true);
31
+ emit Command(host, relayPayableId, "1:0:0", Schemas.Relay, Keys.Any, Keys.Empty, true);
32
+ emit Labeled(relayPayableId, bytes32(0), "relayPayable");
34
33
  }
35
34
 
36
35
  /// @notice Relay one RELAY request block with the command account and current state.
37
36
  /// @param c Command context; `c.request` must contain exactly one RELAY block.
38
37
  /// @return output Empty output state.
39
38
  function relayPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory output) {
40
- (Cur memory request, ) = Cursors.init(c.request, 0, 1, 1);
39
+ (Cur memory request, ) = Cursors.init(c.request, 1, 1);
41
40
  Budget memory budget = valueBudget();
42
41
 
43
42
  (uint chain, uint resources, bytes memory pipe) = request.relayToPipe(c.account, c.state);
@@ -20,12 +20,11 @@ abstract contract WithdrawHook {
20
20
  /// Use `withdraw` for assets being sent outside the protocol (e.g. ERC-20 transfers, ETH sends).
21
21
  /// For internal balance credits, use `creditAccount` instead.
22
22
  abstract contract Withdraw is CommandBase, WithdrawHook {
23
- string private constant NAME = "withdraw";
24
-
25
- uint internal immutable withdrawId = commandId(NAME);
23
+ uint internal immutable withdrawId = commandId(this.withdraw.selector);
26
24
 
27
25
  constructor() {
28
- emit Command(host, withdrawId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
26
+ emit Command(host, withdrawId, "0:1:0", "", Keys.Balance, Keys.Empty, false);
27
+ emit Labeled(withdrawId, bytes32(0), "withdraw");
29
28
  }
30
29
 
31
30
  /// @notice Withdraw each BALANCE block from the command state to the command account.
@@ -34,7 +33,7 @@ abstract contract Withdraw is CommandBase, WithdrawHook {
34
33
  function withdraw(
35
34
  CommandContext calldata c
36
35
  ) external onlyCommand returns (bytes memory) {
37
- (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
36
+ (Cur memory state, , ) = Cursors.init(c.state, 1);
38
37
 
39
38
  while (state.i < state.len) {
40
39
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
@@ -18,12 +18,11 @@ abstract contract AllowAssetsHook {
18
18
  /// @notice Admin command that permits a list of (asset, meta) pairs via a virtual hook.
19
19
  /// Each ASSET block in the request calls `allowAsset`. Only callable by the admin account.
20
20
  abstract contract AllowAssets is CommandBase, AdminEvent, AllowAssetsHook {
21
- string private constant NAME = "allowAssets";
22
-
23
- uint internal immutable allowAssetsId = commandId(NAME);
21
+ uint internal immutable allowAssetsId = commandId(this.allowAssets.selector);
24
22
 
25
23
  constructor() {
26
- emit Admin(host, allowAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false, false);
24
+ emit Admin(host, allowAssetsId, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
25
+ emit Labeled(allowAssetsId, bytes32(0), "allowAssets");
27
26
  }
28
27
 
29
28
  /// @notice Allow each ASSET block in the admin request.
@@ -32,7 +31,7 @@ abstract contract AllowAssets is CommandBase, AdminEvent, AllowAssetsHook {
32
31
  function allowAssets(
33
32
  CommandContext calldata c
34
33
  ) external onlyAdmin(c.account) returns (bytes memory) {
35
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
34
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
36
35
 
37
36
  while (request.i < request.len) {
38
37
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
@@ -22,18 +22,18 @@ abstract contract AllowanceHook {
22
22
  /// @notice Admin command that applies cross-host allowance entries via a virtual hook.
23
23
  /// Each ALLOWANCE block grants or updates a host-scoped asset cap. Only callable by the admin account.
24
24
  abstract contract Allowance is CommandBase, AdminEvent, AllowanceHook {
25
- string private constant NAME = "allowance";
26
- uint internal immutable allowanceId = commandId(NAME);
25
+ uint internal immutable allowanceId = commandId(this.allowance.selector);
27
26
 
28
27
  constructor() {
29
- emit Admin(host, allowanceId, NAME, "1:0:0", Schemas.Allowance, Keys.Empty, Keys.Empty, false, false);
28
+ emit Admin(host, allowanceId, "1:0:0", Schemas.Allowance, Keys.Empty, Keys.Empty, false);
29
+ emit Labeled(allowanceId, bytes32(0), "allowance");
30
30
  }
31
31
 
32
32
  /// @notice Apply each ALLOWANCE block in the admin request.
33
33
  /// @param c Admin command context; `c.request` must contain ALLOWANCE blocks.
34
34
  /// @return Empty output state.
35
35
  function allowance(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
36
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
36
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
37
37
 
38
38
  while (request.i < request.len) {
39
39
  (uint peer, bytes32 asset, bytes32 meta, uint amount) = request.unpackAllowance();
@@ -11,12 +11,11 @@ using Cursors for Cur;
11
11
  /// Each ACCOUNT block in the request is enabled as a guardian on the host.
12
12
  /// Only callable by the admin account.
13
13
  abstract contract Appoint is CommandBase, AdminEvent {
14
- string private constant NAME = "appoint";
15
-
16
- uint internal immutable appointId = commandId(NAME);
14
+ uint internal immutable appointId = commandId(this.appoint.selector);
17
15
 
18
16
  constructor() {
19
- emit Admin(host, appointId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false, false);
17
+ emit Admin(host, appointId, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
18
+ emit Labeled(appointId, bytes32(0), "appoint");
20
19
  }
21
20
 
22
21
  /// @notice Appoint each ACCOUNT block in the admin request as a guardian.
@@ -25,7 +24,7 @@ abstract contract Appoint is CommandBase, AdminEvent {
25
24
  function appoint(
26
25
  CommandContext calldata c
27
26
  ) external onlyAdmin(c.account) returns (bytes memory) {
28
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
27
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
29
28
 
30
29
  while (request.i < request.len) {
31
30
  bytes32 account = request.unpackAccount();
@@ -11,12 +11,11 @@ using Cursors for Cur;
11
11
  /// Each NODE block in the request is authorized on the host.
12
12
  /// Only callable by the admin account.
13
13
  abstract contract Authorize is CommandBase, AdminEvent {
14
- string private constant NAME = "authorize";
15
-
16
- uint internal immutable authorizeId = commandId(NAME);
14
+ uint internal immutable authorizeId = commandId(this.authorize.selector);
17
15
 
18
16
  constructor() {
19
- emit Admin(host, authorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false, false);
17
+ emit Admin(host, authorizeId, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
18
+ emit Labeled(authorizeId, bytes32(0), "authorize");
20
19
  }
21
20
 
22
21
  /// @notice Authorize each NODE block in the admin request.
@@ -25,7 +24,7 @@ abstract contract Authorize is CommandBase, AdminEvent {
25
24
  function authorize(
26
25
  CommandContext calldata c
27
26
  ) external onlyAdmin(c.account) returns (bytes memory) {
28
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
27
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
29
28
 
30
29
  while (request.i < request.len) {
31
30
  uint node = request.unpackNode();
@@ -18,12 +18,11 @@ abstract contract DenyAssetsHook {
18
18
  /// @notice Admin command that blocks a list of (asset, meta) pairs via a virtual hook.
19
19
  /// Each ASSET block in the request calls `denyAsset`. Only callable by the admin account.
20
20
  abstract contract DenyAssets is CommandBase, AdminEvent, DenyAssetsHook {
21
- string private constant NAME = "denyAssets";
22
-
23
- uint internal immutable denyAssetsId = commandId(NAME);
21
+ uint internal immutable denyAssetsId = commandId(this.denyAssets.selector);
24
22
 
25
23
  constructor() {
26
- emit Admin(host, denyAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false, false);
24
+ emit Admin(host, denyAssetsId, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
25
+ emit Labeled(denyAssetsId, bytes32(0), "denyAssets");
27
26
  }
28
27
 
29
28
  /// @notice Deny each ASSET block in the admin request.
@@ -32,7 +31,7 @@ abstract contract DenyAssets is CommandBase, AdminEvent, DenyAssetsHook {
32
31
  function denyAssets(
33
32
  CommandContext calldata c
34
33
  ) external onlyAdmin(c.account) returns (bytes memory) {
35
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
34
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
36
35
 
37
36
  while (request.i < request.len) {
38
37
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
@@ -17,12 +17,11 @@ abstract contract DestroyHook {
17
17
  /// @notice Admin command that runs host teardown logic via a virtual hook.
18
18
  /// The full request is passed to `destroy` as a cursor. Only callable by the admin account.
19
19
  abstract contract Destroy is CommandBase, AdminEvent, DestroyHook {
20
- string private constant NAME = "destroy";
21
-
22
- uint internal immutable destroyId = commandId(NAME);
20
+ uint internal immutable destroyId = commandId(this.destroy.selector);
23
21
 
24
22
  constructor(string memory input) {
25
- emit Admin(host, destroyId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false, false);
23
+ emit Admin(host, destroyId, "1:0:0", input, Keys.Empty, Keys.Empty, false);
24
+ emit Labeled(destroyId, bytes32(0), "destroy");
26
25
  }
27
26
 
28
27
  /// @notice Run host teardown logic over the full admin request.
@@ -11,12 +11,11 @@ using Cursors for Cur;
11
11
  /// Each ACCOUNT block in the request is disabled as a guardian on the host.
12
12
  /// Only callable by the admin account.
13
13
  abstract contract Dismiss is CommandBase, AdminEvent {
14
- string private constant NAME = "dismiss";
15
-
16
- uint internal immutable dismissId = commandId(NAME);
14
+ uint internal immutable dismissId = commandId(this.dismiss.selector);
17
15
 
18
16
  constructor() {
19
- emit Admin(host, dismissId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false, false);
17
+ emit Admin(host, dismissId, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
18
+ emit Labeled(dismissId, bytes32(0), "dismiss");
20
19
  }
21
20
 
22
21
  /// @notice Dismiss each ACCOUNT block in the admin request from guardian status.
@@ -25,7 +24,7 @@ abstract contract Dismiss is CommandBase, AdminEvent {
25
24
  function dismiss(
26
25
  CommandContext calldata c
27
26
  ) external onlyAdmin(c.account) returns (bytes memory) {
28
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
27
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
29
28
 
30
29
  while (request.i < request.len) {
31
30
  bytes32 account = request.unpackAccount();
@@ -16,19 +16,18 @@ using Cursors for Cur;
16
16
  /// Only callable by the admin account.
17
17
  /// Unspent top-level `msg.value` remains on this host.
18
18
  abstract contract ExecutePayable is CommandBase, Payable, AdminEvent {
19
- string private constant NAME = "executePayable";
20
-
21
- uint internal immutable executePayableId = commandId(NAME);
19
+ uint internal immutable executePayableId = commandId(this.executePayable.selector);
22
20
 
23
21
  constructor() {
24
- emit Admin(host, executePayableId, NAME, "1:0:0", Schemas.Call, Keys.Empty, Keys.Empty, false, true);
22
+ emit Admin(host, executePayableId, "1:0:0", Schemas.Call, Keys.Empty, Keys.Empty, true);
23
+ emit Labeled(executePayableId, bytes32(0), "executePayable");
25
24
  }
26
25
 
27
26
  /// @notice Execute each CALL block in the admin request.
28
27
  /// @param c Admin command context; `c.request` must contain CALL blocks.
29
28
  /// @return Empty output state.
30
29
  function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
31
- (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
30
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
32
31
  Budget memory budget = valueBudget();
33
32
 
34
33
  while (request.i < request.len) {
@@ -17,12 +17,11 @@ abstract contract InitHook {
17
17
  /// @notice Admin command that runs host initialization logic via a virtual hook.
18
18
  /// The full request is passed to `init` as a cursor. Only callable by the admin account.
19
19
  abstract contract Init is CommandBase, AdminEvent, InitHook {
20
- string private constant NAME = "init";
21
-
22
- uint internal immutable initId = commandId(NAME);
20
+ uint internal immutable initId = commandId(this.init.selector);
23
21
 
24
22
  constructor(string memory input) {
25
- emit Admin(host, initId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false, false);
23
+ emit Admin(host, initId, "1:0:0", input, Keys.Empty, Keys.Empty, false);
24
+ emit Labeled(initId, bytes32(0), "init");
26
25
  }
27
26
 
28
27
  /// @notice Run host initialization logic over the full admin request.
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {CommandBase, CommandContext, Keys} from "../Base.sol";
5
+ import {Cursors, Cur, Schemas} from "../../Cursors.sol";
6
+ import {AdminEvent} from "../../events/Admin.sol";
7
+ using Cursors for Cur;
8
+
9
+ /// @title Label
10
+ /// @notice Admin command that publishes namespaced labels for node IDs.
11
+ /// Each LABEL block in the request emits one `Labeled` event. Only callable by
12
+ /// the admin account.
13
+ abstract contract Label is CommandBase, AdminEvent {
14
+ uint internal immutable labelId = commandId(this.label.selector);
15
+
16
+ constructor() {
17
+ emit Admin(host, labelId, "1:0:0", Schemas.Label, Keys.Empty, Keys.Empty, false);
18
+ emit Labeled(labelId, bytes32(0), "label");
19
+ }
20
+
21
+ /// @notice Publish each LABEL block in the admin request.
22
+ /// @param c Admin command context; `c.request` must contain LABEL blocks.
23
+ /// @return Empty output state.
24
+ function label(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
25
+ (Cur memory request, , ) = Cursors.init(c.request, 1);
26
+
27
+ while (request.i < request.len) {
28
+ (uint id, bytes32 namespace, string memory name) = request.unpackLabel();
29
+ emit Labeled(id, namespace, name);
30
+ }
31
+
32
+ request.complete();
33
+ return "";
34
+ }
35
+ }