@rootzero/contracts 0.9.2 → 0.9.4

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 (48) hide show
  1. package/Commands.sol +4 -3
  2. package/Core.sol +3 -0
  3. package/Cursors.sol +1 -1
  4. package/README.md +18 -24
  5. package/blocks/Cursors.sol +332 -335
  6. package/blocks/Keys.sol +38 -57
  7. package/blocks/Schema.sol +55 -114
  8. package/blocks/Writers.sol +361 -255
  9. package/commands/Base.sol +6 -48
  10. package/commands/Burn.sol +4 -4
  11. package/commands/Credit.sol +5 -4
  12. package/commands/Debit.sol +6 -5
  13. package/commands/Deposit.sol +17 -14
  14. package/commands/Provision.sol +17 -14
  15. package/commands/Transfer.sol +4 -4
  16. package/commands/Withdraw.sol +5 -4
  17. package/commands/admin/AllowAssets.sol +3 -3
  18. package/commands/admin/Allowance.sol +3 -3
  19. package/commands/admin/Authorize.sol +3 -3
  20. package/commands/admin/DenyAssets.sol +3 -3
  21. package/commands/admin/Destroy.sol +1 -1
  22. package/commands/admin/Execute.sol +9 -8
  23. package/commands/admin/Init.sol +1 -1
  24. package/commands/admin/Unauthorize.sol +3 -3
  25. package/core/Access.sol +11 -0
  26. package/core/Context.sol +11 -13
  27. package/core/Payable.sol +57 -0
  28. package/core/Pipeline.sol +55 -0
  29. package/docs/Schema.md +194 -0
  30. package/events/Admin.sol +5 -1
  31. package/events/Command.sol +6 -2
  32. package/events/Listing.sol +3 -4
  33. package/events/Peer.sol +5 -3
  34. package/events/Query.sol +5 -2
  35. package/package.json +2 -2
  36. package/peer/AllowAssets.sol +3 -3
  37. package/peer/Allowance.sol +3 -3
  38. package/peer/BalancePull.sol +43 -0
  39. package/peer/DenyAssets.sol +3 -3
  40. package/peer/Pipe.sol +38 -0
  41. package/peer/Settle.sol +3 -3
  42. package/queries/Assets.sol +7 -6
  43. package/queries/Balances.sol +5 -4
  44. package/queries/Positions.sol +14 -14
  45. package/utils/Value.sol +8 -14
  46. package/commands/Pipe.sol +0 -67
  47. package/docs/GETTING_STARTED.md +0 -294
  48. package/peer/AssetPull.sol +0 -43
package/commands/Base.sol CHANGED
@@ -2,11 +2,9 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {NodeCalls} from "../core/Calls.sol";
5
- import {Cur} from "../Cursors.sol";
6
5
  import {CommandEvent} from "../events/Command.sol";
7
6
  import {Keys} from "../blocks/Keys.sol";
8
7
  import {Ids, Selectors} from "../utils/Ids.sol";
9
- import {Budget, Values} from "../utils/Value.sol";
10
8
 
11
9
  /// @notice Execution context passed to every command invocation.
12
10
  struct CommandContext {
@@ -25,22 +23,18 @@ struct CommandContext {
25
23
  abstract contract CommandBase is NodeCalls, CommandEvent {
26
24
  /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
27
25
  error Expired();
28
- /// @dev Thrown when the raw active account word in calldata does not match the decoded context account.
29
- error ActiveAccountMismatch();
30
26
  /// @dev Thrown when `onlyAdmin` finds that `account` is not the admin account.
31
27
  error NotAdmin();
32
28
 
33
- /// @dev Restrict execution to trusted callers whose decoded context account matches the active calldata account.
34
- modifier onlyCommand(bytes32 account) {
35
- if (activeAccount() != account) revert ActiveAccountMismatch();
36
- enforceCaller(msg.sender);
29
+ /// @dev Restrict execution to the commander using the host's admin account.
30
+ modifier onlyAdmin(bytes32 account) {
31
+ if (account != adminAccount) revert NotAdmin();
32
+ enforceCommander(msg.sender);
37
33
  _;
38
34
  }
39
35
 
40
- /// @dev Restrict execution to trusted callers using the host's admin account.
41
- modifier onlyAdmin(bytes32 account) {
42
- if (activeAccount() != account) revert ActiveAccountMismatch();
43
- if (account != adminAccount) revert NotAdmin();
36
+ /// @dev Restrict execution to trusted callers.
37
+ modifier onlyCommand() {
44
38
  enforceCaller(msg.sender);
45
39
  _;
46
40
  }
@@ -66,40 +60,4 @@ abstract contract CommandBase is NodeCalls, CommandEvent {
66
60
  function commandId(string memory name) internal view returns (uint) {
67
61
  return Ids.toCommand(Selectors.command(name), address(this));
68
62
  }
69
-
70
- /// @notice Return the active command account directly from the fixed tuple head in calldata.
71
- /// @dev Command entrypoints use the ABI shape `name((bytes32,bytes,bytes))`, so the tuple head
72
- /// starts at byte 36 of `msg.data`, with the account field at bytes [36:68).
73
- function activeAccount() internal pure returns (bytes32 account) {
74
- account = bytes32(msg.data[36:68]);
75
- }
76
- }
77
-
78
- /// @title CommandPayable
79
- /// @notice Abstract base for commands that accept native value (`msg.value`).
80
- /// Provides a shared settlement hook for any unspent value remaining in the
81
- /// command's mutable budget after execution completes.
82
- abstract contract CommandPayable is CommandBase {
83
- /// @dev Thrown when a payable command completes with unspent native value.
84
- /// Override `settleValue` to implement refund or forwarding behavior instead.
85
- error UnusedValue(uint remaining);
86
-
87
- /// @notice Drains the command budget and settles any remaining native value.
88
- /// @dev Calls the amount-based `settleValue` hook only when some value remains.
89
- /// @param account Caller's account identifier for the current invocation.
90
- /// @param budget Mutable native-value budget used during command execution.
91
- function settleValue(bytes32 account, Budget memory budget) internal {
92
- uint remaining = Values.drain(budget);
93
- if (remaining != 0) settleValue(account, remaining);
94
- }
95
-
96
- /// @notice Handles leftover native value after a payable command has finished.
97
- /// @dev Override this hook to refund or redirect unused value for a command.
98
- /// The default implementation rejects any leftover amount.
99
- /// @param account Caller's account identifier for the current invocation.
100
- /// @param remaining Unspent native value left in the budget, in wei.
101
- function settleValue(bytes32 account, uint remaining) internal virtual {
102
- account;
103
- revert UnusedValue(remaining);
104
- }
105
63
  }
package/commands/Burn.sol CHANGED
@@ -25,18 +25,18 @@ abstract contract Burn is CommandBase, BurnHook {
25
25
  uint internal immutable burnId = commandId(NAME);
26
26
 
27
27
  constructor() {
28
- emit Command(host, burnId, NAME, "", Keys.Balance, Keys.Empty, false);
28
+ emit Command(host, burnId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false);
29
29
  }
30
30
 
31
- function burn(CommandContext calldata c) external onlyCommand(c.account) returns (bytes memory) {
32
- (Cur memory state, , ) = cursor(c.state, 1);
31
+ function burn(CommandContext calldata c) external onlyCommand returns (bytes memory) {
32
+ (Cur memory state, ) = cursor(c.state, 1);
33
33
 
34
34
  while (state.i < state.bound) {
35
35
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
36
36
  burn(c.account, asset, meta, amount);
37
37
  }
38
38
 
39
- state.complete();
39
+ state.close();
40
40
  return "";
41
41
  }
42
42
  }
@@ -22,17 +22,18 @@ abstract contract CreditAccountHook {
22
22
  /// An optional ACCOUNT block in the request overrides the default `c.account` destination.
23
23
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
24
24
  string private constant NAME = "creditAccount";
25
+ string private constant REQUEST = string.concat(Schemas.Unit, ", maybe ", Schemas.Account);
25
26
 
26
27
  uint internal immutable creditAccountId = commandId(NAME);
27
28
 
28
29
  constructor() {
29
- emit Command(host, creditAccountId, NAME, Schemas.Account, Keys.Balance, Keys.Empty, false);
30
+ emit Command(host, creditAccountId, NAME, "0:1:0", REQUEST, Keys.Balance, Keys.Empty, false);
30
31
  }
31
32
 
32
33
  function creditAccount(
33
34
  CommandContext calldata c
34
- ) external onlyCommand(c.account) returns (bytes memory) {
35
- (Cur memory state, , ) = cursor(c.state, 1);
35
+ ) external onlyCommand returns (bytes memory) {
36
+ (Cur memory state, ) = cursor(c.state, 1);
36
37
  bytes32 to = Cursors.resolveAccount(c.request, c.account);
37
38
 
38
39
  while (state.i < state.bound) {
@@ -40,7 +41,7 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
40
41
  creditAccount(to, asset, meta, amount);
41
42
  }
42
43
 
43
- state.complete();
44
+ state.close();
44
45
  return "";
45
46
  }
46
47
  }
@@ -27,15 +27,15 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
27
27
  uint internal immutable debitAccountId = commandId(NAME);
28
28
 
29
29
  constructor() {
30
- emit Command(host, debitAccountId, NAME, Schemas.Amount, Keys.Empty, Keys.Balance, false);
30
+ emit Command(host, debitAccountId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
31
31
  }
32
32
 
33
33
  /// @notice Override to customize request parsing or batching for debits.
34
34
  /// The default implementation iterates AMOUNT blocks, calls
35
35
  /// `debitAccount`, and emits matching BALANCE blocks.
36
36
  function debitAccount(bytes32 account, bytes calldata request) internal virtual returns (bytes memory) {
37
- (Cur memory input, uint count, ) = cursor(request, 1);
38
- Writer memory writer = Writers.allocBalances(count);
37
+ (Cur memory input, uint groups) = cursor(request, 1);
38
+ Writer memory writer = Writers.allocBalances(groups);
39
39
 
40
40
  while (input.i < input.bound) {
41
41
  (bytes32 asset, bytes32 meta, uint amount) = input.unpackAmount();
@@ -43,12 +43,13 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
43
43
  writer.appendBalance(asset, meta, amount);
44
44
  }
45
45
 
46
- return input.complete(writer);
46
+ input.close();
47
+ return writer.finish();
47
48
  }
48
49
 
49
50
  function debitAccount(
50
51
  CommandContext calldata c
51
- ) external onlyCommand(c.account) returns (bytes memory) {
52
+ ) external onlyCommand returns (bytes memory) {
52
53
  return debitAccount(c.account, c.request);
53
54
  }
54
55
  }
@@ -1,9 +1,10 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { CommandContext, CommandBase, CommandPayable, Keys } from "./Base.sol";
4
+ import { CommandContext, CommandBase, Keys } from "./Base.sol";
5
+ import { Payable } from "../core/Payable.sol";
5
6
  import { Cursors, Cur, Schemas, Writer, Writers } from "../Cursors.sol";
6
- import { Budget, Values } from "../utils/Value.sol";
7
+ import { Budget } from "../utils/Value.sol";
7
8
 
8
9
  using Cursors for Cur;
9
10
  using Writers for Writer;
@@ -41,14 +42,14 @@ abstract contract Deposit is CommandBase, DepositHook {
41
42
  uint internal immutable depositId = commandId(NAME);
42
43
 
43
44
  constructor() {
44
- emit Command(host, depositId, NAME, Schemas.Amount, Keys.Empty, Keys.Balance, false);
45
+ emit Command(host, depositId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
45
46
  }
46
47
 
47
48
  function deposit(
48
49
  CommandContext calldata c
49
- ) external onlyCommand(c.account) returns (bytes memory) {
50
- (Cur memory request, uint count, ) = cursor(c.request, 1);
51
- Writer memory writer = Writers.allocBalances(count);
50
+ ) external onlyCommand returns (bytes memory) {
51
+ (Cur memory request, uint groups) = cursor(c.request, 1);
52
+ Writer memory writer = Writers.allocBalances(groups);
52
53
 
53
54
  while (request.i < request.bound) {
54
55
  (bytes32 asset, bytes32 meta, uint amount) = request.unpackAmount();
@@ -56,28 +57,29 @@ abstract contract Deposit is CommandBase, DepositHook {
56
57
  writer.appendBalance(asset, meta, amount);
57
58
  }
58
59
 
59
- return request.complete(writer);
60
+ request.close();
61
+ return writer.finish();
60
62
  }
61
63
  }
62
64
 
63
65
  /// @title DepositPayable
64
66
  /// @notice Command that receives externally sourced assets and records them as BALANCE state.
65
67
  /// Use `depositPayable` when the hook needs tracked access to `msg.value` via a mutable budget.
66
- abstract contract DepositPayable is CommandPayable, DepositPayableHook {
68
+ abstract contract DepositPayable is CommandBase, Payable, DepositPayableHook {
67
69
  string private constant NAME = "depositPayable";
68
70
 
69
71
  uint internal immutable depositPayableId = commandId(NAME);
70
72
 
71
73
  constructor() {
72
- emit Command(host, depositPayableId, NAME, Schemas.Amount, Keys.Empty, Keys.Balance, true);
74
+ emit Command(host, depositPayableId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, true);
73
75
  }
74
76
 
75
77
  function depositPayable(
76
78
  CommandContext calldata c
77
- ) external payable onlyCommand(c.account) returns (bytes memory) {
78
- (Cur memory request, uint count, ) = cursor(c.request, 1);
79
- Writer memory writer = Writers.allocBalances(count);
80
- Budget memory budget = Values.fromMsg();
79
+ ) external payable onlyCommand returns (bytes memory) {
80
+ (Cur memory request, uint groups) = cursor(c.request, 1);
81
+ Writer memory writer = Writers.allocBalances(groups);
82
+ Budget memory budget = valueBudget();
81
83
 
82
84
  while (request.i < request.bound) {
83
85
  (bytes32 asset, bytes32 meta, uint amount) = request.unpackAmount();
@@ -86,7 +88,8 @@ abstract contract DepositPayable is CommandPayable, DepositPayableHook {
86
88
  }
87
89
 
88
90
  settleValue(c.account, budget);
89
- return request.complete(writer);
91
+ request.close();
92
+ return writer.finish();
90
93
  }
91
94
  }
92
95
 
@@ -1,9 +1,10 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {CommandContext, CommandBase, CommandPayable, Keys} from "./Base.sol";
4
+ import {CommandContext, CommandBase, Keys} from "./Base.sol";
5
+ import {Payable} from "../core/Payable.sol";
5
6
  import {HostAmount, Cursors, Cur, Schemas, Writer, Writers} from "../Cursors.sol";
6
- import {Budget, Values} from "../utils/Value.sol";
7
+ import {Budget} from "../utils/Value.sol";
7
8
  using Cursors for Cur;
8
9
  using Writers for Writer;
9
10
 
@@ -37,12 +38,12 @@ abstract contract Provision is CommandBase, ProvisionHook {
37
38
  uint internal immutable provisionId = commandId(NAME);
38
39
 
39
40
  constructor() {
40
- emit Command(host, provisionId, NAME, Schemas.Allocation, Keys.Empty, Keys.Custody, false);
41
+ emit Command(host, provisionId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false);
41
42
  }
42
43
 
43
- function provision(CommandContext calldata c) external onlyCommand(c.account) returns (bytes memory) {
44
- (Cur memory request, uint count, ) = cursor(c.request, 1);
45
- Writer memory writer = Writers.allocCustodies(count);
44
+ function provision(CommandContext calldata c) external onlyCommand returns (bytes memory) {
45
+ (Cur memory request, uint groups) = cursor(c.request, 1);
46
+ Writer memory writer = Writers.allocCustodies(groups);
46
47
 
47
48
  while (request.i < request.bound) {
48
49
  HostAmount memory allocation = request.unpackAllocationValue();
@@ -50,7 +51,8 @@ abstract contract Provision is CommandBase, ProvisionHook {
50
51
  writer.appendCustody(allocation);
51
52
  }
52
53
 
53
- return request.complete(writer);
54
+ request.close();
55
+ return writer.finish();
54
56
  }
55
57
  }
56
58
 
@@ -58,21 +60,21 @@ abstract contract Provision is CommandBase, ProvisionHook {
58
60
  /// @notice Command that provisions assets to peer hosts from ALLOCATION request blocks.
59
61
  /// Each request block supplies the target host plus an asset amount; the output is a CUSTODY state stream.
60
62
  /// The hook receives a mutable native-value budget drawn from `msg.value`.
61
- abstract contract ProvisionPayable is CommandPayable, ProvisionPayableHook {
63
+ abstract contract ProvisionPayable is CommandBase, Payable, ProvisionPayableHook {
62
64
  string private constant NAME = "provisionPayable";
63
65
 
64
66
  uint internal immutable provisionPayableId = commandId(NAME);
65
67
 
66
68
  constructor() {
67
- emit Command(host, provisionPayableId, NAME, Schemas.Allocation, Keys.Empty, Keys.Custody, true);
69
+ emit Command(host, provisionPayableId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, true);
68
70
  }
69
71
 
70
72
  function provisionPayable(
71
73
  CommandContext calldata c
72
- ) external payable onlyCommand(c.account) returns (bytes memory) {
73
- (Cur memory request, uint count, ) = cursor(c.request, 1);
74
- Writer memory writer = Writers.allocCustodies(count);
75
- Budget memory budget = Values.fromMsg();
74
+ ) external payable onlyCommand returns (bytes memory) {
75
+ (Cur memory request, uint groups) = cursor(c.request, 1);
76
+ Writer memory writer = Writers.allocCustodies(groups);
77
+ Budget memory budget = valueBudget();
76
78
 
77
79
  while (request.i < request.bound) {
78
80
  HostAmount memory allocation = request.unpackAllocationValue();
@@ -81,7 +83,8 @@ abstract contract ProvisionPayable is CommandPayable, ProvisionPayableHook {
81
83
  }
82
84
 
83
85
  settleValue(c.account, budget);
84
- return request.complete(writer);
86
+ request.close();
87
+ return writer.finish();
85
88
  }
86
89
  }
87
90
 
@@ -23,7 +23,7 @@ abstract contract Transfer is CommandBase, TransferHook {
23
23
  uint internal immutable transferId = commandId(NAME);
24
24
 
25
25
  constructor() {
26
- emit Command(host, transferId, NAME, Schemas.Payout, Keys.Empty, Keys.Empty, false);
26
+ emit Command(host, transferId, NAME, "1:0:0", Schemas.Payout, Keys.Empty, Keys.Empty, false);
27
27
  }
28
28
 
29
29
  /// @notice Override to customize request parsing or batching for transfers.
@@ -32,7 +32,7 @@ abstract contract Transfer is CommandBase, TransferHook {
32
32
  /// @param request Full request bytes.
33
33
  /// @return Empty bytes (transfers produce no state output).
34
34
  function transfer(bytes32 from, bytes calldata request) internal virtual returns (bytes memory) {
35
- (Cur memory input, , ) = cursor(request, 1);
35
+ (Cur memory input, ) = cursor(request, 1);
36
36
  Tx memory value;
37
37
  value.from = from;
38
38
 
@@ -42,13 +42,13 @@ abstract contract Transfer is CommandBase, TransferHook {
42
42
  transfer(value);
43
43
  }
44
44
 
45
- input.complete();
45
+ input.close();
46
46
  return "";
47
47
  }
48
48
 
49
49
  function transfer(
50
50
  CommandContext calldata c
51
- ) external onlyCommand(c.account) returns (bytes memory) {
51
+ ) external onlyCommand returns (bytes memory) {
52
52
  return transfer(c.account, c.request);
53
53
  }
54
54
  }
@@ -21,17 +21,18 @@ abstract contract WithdrawHook {
21
21
  /// For internal balance credits, use `creditAccount` instead.
22
22
  abstract contract Withdraw is CommandBase, WithdrawHook {
23
23
  string private constant NAME = "withdraw";
24
+ string private constant REQUEST = string.concat(Schemas.Unit, ", maybe ", Schemas.Account);
24
25
 
25
26
  uint internal immutable withdrawId = commandId(NAME);
26
27
 
27
28
  constructor() {
28
- emit Command(host, withdrawId, NAME, Schemas.Account, Keys.Balance, Keys.Empty, false);
29
+ emit Command(host, withdrawId, NAME, "0:1:0", REQUEST, Keys.Balance, Keys.Empty, false);
29
30
  }
30
31
 
31
32
  function withdraw(
32
33
  CommandContext calldata c
33
- ) external onlyCommand(c.account) returns (bytes memory) {
34
- (Cur memory state, , ) = cursor(c.state, 1);
34
+ ) external onlyCommand returns (bytes memory) {
35
+ (Cur memory state, ) = cursor(c.state, 1);
35
36
  bytes32 to = Cursors.resolveAccount(c.request, c.account);
36
37
 
37
38
  while (state.i < state.bound) {
@@ -39,7 +40,7 @@ abstract contract Withdraw is CommandBase, WithdrawHook {
39
40
  withdraw(to, asset, meta, amount);
40
41
  }
41
42
 
42
- state.complete();
43
+ state.close();
43
44
  return "";
44
45
  }
45
46
  }
@@ -21,20 +21,20 @@ abstract contract AllowAssets is CommandBase, AdminEvent, AllowAssetsHook {
21
21
  uint internal immutable allowAssetsId = commandId(NAME);
22
22
 
23
23
  constructor() {
24
- emit Admin(host, allowAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
+ emit Admin(host, allowAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
25
25
  }
26
26
 
27
27
  function allowAssets(
28
28
  CommandContext calldata c
29
29
  ) external onlyAdmin(c.account) returns (bytes memory) {
30
- (Cur memory request, , ) = cursor(c.request, 1);
30
+ (Cur memory request, ) = cursor(c.request, 1);
31
31
 
32
32
  while (request.i < request.bound) {
33
33
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
34
34
  allowAsset(asset, meta);
35
35
  }
36
36
 
37
- request.complete();
37
+ request.close();
38
38
  return "";
39
39
  }
40
40
  }
@@ -26,18 +26,18 @@ abstract contract Allowance is CommandBase, AdminEvent, AllowanceHook {
26
26
  uint internal immutable allowanceId = commandId(NAME);
27
27
 
28
28
  constructor() {
29
- emit Admin(host, allowanceId, NAME, Schemas.Allowance, Keys.Empty, Keys.Empty, false);
29
+ emit Admin(host, allowanceId, NAME, "1:0:0", Schemas.Allowance, Keys.Empty, Keys.Empty, false);
30
30
  }
31
31
 
32
32
  function allowance(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
33
- (Cur memory request, , ) = cursor(c.request, 1);
33
+ (Cur memory request, ) = cursor(c.request, 1);
34
34
 
35
35
  while (request.i < request.bound) {
36
36
  (uint peer, bytes32 asset, bytes32 meta, uint amount) = request.unpackAllowance();
37
37
  allowance(peer, asset, meta, amount);
38
38
  }
39
39
 
40
- request.complete();
40
+ request.close();
41
41
  return "";
42
42
  }
43
43
  }
@@ -16,20 +16,20 @@ abstract contract Authorize is CommandBase, AdminEvent {
16
16
  uint internal immutable authorizeId = commandId(NAME);
17
17
 
18
18
  constructor() {
19
- emit Admin(host, authorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, authorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
20
20
  }
21
21
 
22
22
  function authorize(
23
23
  CommandContext calldata c
24
24
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, , ) = cursor(c.request, 1);
25
+ (Cur memory request, ) = cursor(c.request, 1);
26
26
 
27
27
  while (request.i < request.bound) {
28
28
  uint node = request.unpackNode();
29
29
  authorize(node);
30
30
  }
31
31
 
32
- request.complete();
32
+ request.close();
33
33
  return "";
34
34
  }
35
35
  }
@@ -21,20 +21,20 @@ abstract contract DenyAssets is CommandBase, AdminEvent, DenyAssetsHook {
21
21
  uint internal immutable denyAssetsId = commandId(NAME);
22
22
 
23
23
  constructor() {
24
- emit Admin(host, denyAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
+ emit Admin(host, denyAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
25
25
  }
26
26
 
27
27
  function denyAssets(
28
28
  CommandContext calldata c
29
29
  ) external onlyAdmin(c.account) returns (bytes memory) {
30
- (Cur memory request, , ) = cursor(c.request, 1);
30
+ (Cur memory request, ) = cursor(c.request, 1);
31
31
 
32
32
  while (request.i < request.bound) {
33
33
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
34
34
  denyAsset(asset, meta);
35
35
  }
36
36
 
37
- request.complete();
37
+ request.close();
38
38
  return "";
39
39
  }
40
40
  }
@@ -22,7 +22,7 @@ abstract contract Destroy is CommandBase, AdminEvent, DestroyHook {
22
22
  uint internal immutable destroyId = commandId(NAME);
23
23
 
24
24
  constructor(string memory input) {
25
- emit Admin(host, destroyId, NAME, input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, destroyId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false);
26
26
  }
27
27
 
28
28
  function destroy(
@@ -1,10 +1,11 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {CommandContext, CommandPayable, Keys} from "../Base.sol";
4
+ import {CommandBase, CommandContext, Keys} from "../Base.sol";
5
+ import {Payable} from "../../core/Payable.sol";
5
6
  import {Cursors, Cur, Schemas} from "../../Cursors.sol";
6
7
  import {AdminEvent} from "../../events/Admin.sol";
7
- import {Budget, Values} from "../../utils/Value.sol";
8
+ import {Budget} from "../../utils/Value.sol";
8
9
  import {Ids} from "../../utils/Ids.sol";
9
10
 
10
11
  using Cursors for Cur;
@@ -13,26 +14,26 @@ using Cursors for Cur;
13
14
  /// @notice Admin command that forwards raw calldata to one or more target nodes.
14
15
  /// Each CALL block specifies a target node ID, native value, and raw calldata payload.
15
16
  /// Only callable by the admin account.
16
- abstract contract ExecutePayable is CommandPayable, AdminEvent {
17
+ abstract contract ExecutePayable is CommandBase, Payable, AdminEvent {
17
18
  string private constant NAME = "executePayable";
18
19
 
19
20
  uint internal immutable executePayableId = commandId(NAME);
20
21
 
21
22
  constructor() {
22
- emit Admin(host, executePayableId, NAME, Schemas.Call, Keys.Empty, Keys.Empty, true);
23
+ emit Admin(host, executePayableId, NAME, "1:0:0", Schemas.Call, Keys.Empty, Keys.Empty, true);
23
24
  }
24
25
 
25
26
  function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
26
- (Cur memory request, , ) = cursor(c.request, 1);
27
- Budget memory budget = Values.fromMsg();
27
+ (Cur memory request, ) = cursor(c.request, 1);
28
+ Budget memory budget = valueBudget();
28
29
 
29
30
  while (request.i < request.bound) {
30
31
  (uint target, uint value, bytes calldata data) = request.unpackCall();
31
32
  address addr = Ids.nodeAddr(target);
32
- callAddr(addr, Values.use(budget, value), data);
33
+ callAddr(addr, useValue(budget, value), data);
33
34
  }
34
35
 
35
- request.complete();
36
+ request.close();
36
37
  settleValue(c.account, budget);
37
38
  return "";
38
39
  }
@@ -22,7 +22,7 @@ abstract contract Init is CommandBase, AdminEvent, InitHook {
22
22
  uint internal immutable initId = commandId(NAME);
23
23
 
24
24
  constructor(string memory input) {
25
- emit Admin(host, initId, NAME, input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, initId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false);
26
26
  }
27
27
 
28
28
  function init(
@@ -16,20 +16,20 @@ abstract contract Unauthorize is CommandBase, AdminEvent {
16
16
  uint internal immutable unauthorizeId = commandId(NAME);
17
17
 
18
18
  constructor() {
19
- emit Admin(host, unauthorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, unauthorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
20
20
  }
21
21
 
22
22
  function unauthorize(
23
23
  CommandContext calldata c
24
24
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, , ) = cursor(c.request, 1);
25
+ (Cur memory request, ) = cursor(c.request, 1);
26
26
 
27
27
  while (request.i < request.bound) {
28
28
  uint node = request.unpackNode();
29
29
  unauthorize(node);
30
30
  }
31
31
 
32
- request.complete();
32
+ request.close();
33
33
  return "";
34
34
  }
35
35
  }
package/core/Access.sol CHANGED
@@ -78,4 +78,15 @@ abstract contract AccessControl is RootZeroContext, AccessEvent {
78
78
  }
79
79
  return caller;
80
80
  }
81
+
82
+ /// @notice Assert that `caller` is the commander and return it.
83
+ /// Used by admin modifiers to keep governance authority separate from peer trust.
84
+ /// @param caller Address to validate.
85
+ /// @return The same `caller` value if it is the commander.
86
+ function enforceCommander(address caller) internal view returns (address) {
87
+ if (caller == address(0) || caller != commander) {
88
+ revert UnauthorizedCaller(caller);
89
+ }
90
+ return caller;
91
+ }
81
92
  }
package/core/Context.sol CHANGED
@@ -27,23 +27,21 @@ abstract contract RootZeroContext {
27
27
  /// @param source Calldata slice to parse.
28
28
  /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
29
29
  /// @return cur Cursor with `bound` set to the end of the first run.
30
- /// @return count Total number of blocks in the run (a multiple of `group`).
31
- /// @return quotient Number of groups in the run (`count / group`).
32
- function cursor(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint count, uint quotient) {
33
- cur = Cursors.open(source);
34
- (, count, quotient) = cur.primeRun(group);
30
+ /// @return groups Number of block groups in the run (`prime block count / group`).
31
+ function cursor(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint groups) {
32
+ return Cursors.init(source, group);
35
33
  }
36
34
 
37
- /// @notice Open a cursor, prime it, and assert that its normalized quotient matches `expectedQuotient`.
38
- /// Equivalent to `open(source)` followed by `primeRun(group)` and a direct quotient equality check.
39
- /// Reverts with `Cursors.BadRatio` when the quotient does not match.
35
+ /// @notice Open a cursor, prime it, and assert that its group count matches `expectedGroups`.
36
+ /// Equivalent to `open(source)` followed by `primeRun(group)` and a direct group-count equality check.
37
+ /// Reverts with `Cursors.BadRatio` when the group count does not match.
40
38
  /// @param source Calldata slice to parse.
41
39
  /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
42
- /// @param expectedQuotient Required number of groups in the first run.
40
+ /// @param expectedGroups Required number of groups in the first run.
43
41
  /// @return cur Cursor with `bound` set to the end of the first run.
44
- function cursor(bytes calldata source, uint group, uint expectedQuotient) internal pure returns (Cur memory cur) {
45
- cur = Cursors.open(source);
46
- (, , uint quotient) = cur.primeRun(group);
47
- if (quotient != expectedQuotient) revert Cursors.BadRatio();
42
+ function cursor(bytes calldata source, uint group, uint expectedGroups) internal pure returns (Cur memory cur) {
43
+ uint groups;
44
+ (cur, groups) = Cursors.init(source, group);
45
+ if (groups != expectedGroups) revert Cursors.BadRatio();
48
46
  }
49
47
  }