@rootzero/contracts 0.9.0 → 0.9.2

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/Commands.sol CHANGED
@@ -4,7 +4,7 @@ pragma solidity ^0.8.33;
4
4
  // Aggregator: re-exports command, admin, and peer abstractions.
5
5
  // Import this file to inherit from the full rootzero command surface without managing individual paths.
6
6
 
7
- import { CommandBase, CommandContext, CommandPayable, encodeCommandCall } from "./commands/Base.sol";
7
+ import { CommandBase, CommandContext, CommandPayable } from "./commands/Base.sol";
8
8
  import { Keys } from "./blocks/Keys.sol";
9
9
  import { Burn, BurnHook } from "./commands/Burn.sol";
10
10
  import { CreditAccount, CreditAccountHook } from "./commands/Credit.sol";
@@ -20,12 +20,11 @@ import { ExecutePayable } from "./commands/admin/Execute.sol";
20
20
  import { Authorize } from "./commands/admin/Authorize.sol";
21
21
  import { DenyAssets, DenyAssetsHook } from "./commands/admin/DenyAssets.sol";
22
22
  import { Init, InitHook } from "./commands/admin/Init.sol";
23
- import { RelocatePayable } from "./commands/admin/Relocate.sol";
24
23
  import { Allowance, AllowanceHook } from "./commands/admin/Allowance.sol";
25
24
  import { Unauthorize } from "./commands/admin/Unauthorize.sol";
26
25
  import { PeerBase, encodePeerCall } from "./peer/Base.sol";
27
26
  import { PeerAllowance } from "./peer/Allowance.sol";
28
- import { PeerAssetPull, PeerAssetPullHook } from "./peer/AssetPull.sol";
27
+ import { PeerAssetPull, AssetPullHook } from "./peer/AssetPull.sol";
29
28
  import { PeerAllowAssets } from "./peer/AllowAssets.sol";
30
29
  import { PeerDenyAssets } from "./peer/DenyAssets.sol";
31
30
  import { PeerSettle } from "./peer/Settle.sol";
package/Events.sol CHANGED
@@ -5,6 +5,7 @@ pragma solidity ^0.8.33;
5
5
  // Import this file to get access to every event emitter in one import.
6
6
 
7
7
  import { AccessEvent } from "./events/Access.sol";
8
+ import { AdminEvent } from "./events/Admin.sol";
8
9
  import { AssetEvent } from "./events/Asset.sol";
9
10
  import { BalanceEvent } from "./events/Balance.sol";
10
11
  import { CollateralEvent } from "./events/Collateral.sol";
@@ -18,7 +19,7 @@ import { HostAnnouncedEvent } from "./events/Host.sol";
18
19
  import { ListingEvent } from "./events/Listing.sol";
19
20
  import { PeerEvent } from "./events/Peer.sol";
20
21
  import { QueryEvent } from "./events/Query.sol";
21
- import { RootZeroEvent } from "./events/RootZero.sol";
22
+ import { PipedEvent } from "./events/Piped.sol";
22
23
  import { SwapEvent } from "./events/Swap.sol";
23
24
  import { WithdrawalEvent } from "./events/Withdraw.sol";
24
25
 
package/Utils.sol CHANGED
@@ -11,7 +11,7 @@ import { ECDSA } from "./utils/ECDSA.sol";
11
11
  import { Ids, Selectors } from "./utils/Ids.sol";
12
12
  import { Layout } from "./utils/Layout.sol";
13
13
  import { Schemas } from "./blocks/Schema.sol";
14
- import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily, isLocal, isLocalFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, retryTicket, toLocalBase, toLocalFamily, toUnspecifiedBase, ValueOverflow } from "./utils/Utils.sol";
14
+ import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily, isLocalChain, isLocalFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, NotDivisible, retryTicket, toLocalBase, toLocalFamily, toUnspecifiedBase, ValueOverflow } from "./utils/Utils.sol";
15
15
  import { Budget, Values } from "./utils/Value.sol";
16
16
 
17
17
 
@@ -47,9 +47,6 @@ library Cursors {
47
47
  error UnexpectedValue();
48
48
  /// @dev Input and output block counts are not proportional to their declared group sizes.
49
49
  error BadRatio();
50
- /// @dev A fixed-width low-level unpacker received an invalid final-word keep length.
51
- error InvalidKeep();
52
-
53
50
  // -------------------------------------------------------------------------
54
51
  // Cursor construction and navigation
55
52
  // -------------------------------------------------------------------------
@@ -218,17 +215,6 @@ library Cursors {
218
215
  cur.i = next;
219
216
  }
220
217
 
221
- /// @notice Load a payload word and mask away any omitted tail bytes on the right.
222
- /// @param abs Absolute calldata offset of the word start.
223
- /// @param tail Number of trailing bytes omitted from the logical payload (0..31).
224
- /// @return value Decoded word with omitted tail bytes zeroed.
225
- function mask(uint abs, uint tail) internal pure returns (bytes32 value) {
226
- assembly ("memory-safe") {
227
- value := calldataload(abs)
228
- }
229
- if (tail != 0) value &= bytes32(type(uint256).max << (tail * 8));
230
- }
231
-
232
218
  /// @notice Enter a Bundle block at the current position and return the next offset.
233
219
  /// Advances `cur.i` past the bundle header so the bundled members can be parsed
234
220
  /// directly from the same cursor. The returned `next` is the byte offset
@@ -561,12 +547,9 @@ library Cursors {
561
547
  /// @param cur Cursor; advanced past the block.
562
548
  /// @param key Expected dynamic block key.
563
549
  /// @return value Decoded bytes32.
564
- /// @param keep Number of bytes to keep from the final payload word (1..32).
565
- function unpack32(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 value) {
566
- if (keep == 0 || keep > 32) revert InvalidKeep();
567
- uint len = keep;
568
- uint abs = consume(cur, key, len, len);
569
- value = mask(abs, 32 - keep);
550
+ function unpack32(Cur memory cur, bytes4 key) internal pure returns (bytes32 value) {
551
+ uint abs = consume(cur, key, 32, 32);
552
+ value = bytes32(msg.data[abs:abs + 32]);
570
553
  }
571
554
 
572
555
  /// @notice Consume a dynamic block with two bytes32 payload words.
@@ -574,13 +557,10 @@ library Cursors {
574
557
  /// @param key Expected dynamic block key.
575
558
  /// @return a First decoded bytes32.
576
559
  /// @return b Second decoded bytes32.
577
- /// @param keep Number of bytes to keep from the final payload word (1..32).
578
- function unpack64(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 a, bytes32 b) {
579
- if (keep == 0 || keep > 32) revert InvalidKeep();
580
- uint len = 32 + keep;
581
- uint abs = consume(cur, key, len, len);
560
+ function unpack64(Cur memory cur, bytes4 key) internal pure returns (bytes32 a, bytes32 b) {
561
+ uint abs = consume(cur, key, 64, 64);
582
562
  a = bytes32(msg.data[abs:abs + 32]);
583
- b = mask(abs + 32, 32 - keep);
563
+ b = bytes32(msg.data[abs + 32:abs + 64]);
584
564
  }
585
565
 
586
566
  /// @notice Consume a dynamic block with three bytes32 payload words.
@@ -589,14 +569,11 @@ library Cursors {
589
569
  /// @return a First decoded bytes32.
590
570
  /// @return b Second decoded bytes32.
591
571
  /// @return c Third decoded bytes32.
592
- /// @param keep Number of bytes to keep from the final payload word (1..32).
593
- function unpack96(Cur memory cur, bytes4 key, uint keep) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
594
- if (keep == 0 || keep > 32) revert InvalidKeep();
595
- uint len = 64 + keep;
596
- uint abs = consume(cur, key, len, len);
572
+ function unpack96(Cur memory cur, bytes4 key) internal pure returns (bytes32 a, bytes32 b, bytes32 c) {
573
+ uint abs = consume(cur, key, 96, 96);
597
574
  a = bytes32(msg.data[abs:abs + 32]);
598
575
  b = bytes32(msg.data[abs + 32:abs + 64]);
599
- c = mask(abs + 64, 32 - keep);
576
+ c = bytes32(msg.data[abs + 64:abs + 96]);
600
577
  }
601
578
 
602
579
  /// @notice Consume a dynamic block with a 128-byte payload (four 32-byte words).
@@ -606,19 +583,15 @@ library Cursors {
606
583
  /// @return b Second decoded bytes32.
607
584
  /// @return c Third decoded bytes32.
608
585
  /// @return d Fourth decoded bytes32.
609
- /// @param keep Number of bytes to keep from the final payload word (1..32).
610
586
  function unpack128(
611
587
  Cur memory cur,
612
- bytes4 key,
613
- uint keep
588
+ bytes4 key
614
589
  ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d) {
615
- if (keep == 0 || keep > 32) revert InvalidKeep();
616
- uint len = 96 + keep;
617
- uint abs = consume(cur, key, len, len);
590
+ uint abs = consume(cur, key, 128, 128);
618
591
  a = bytes32(msg.data[abs:abs + 32]);
619
592
  b = bytes32(msg.data[abs + 32:abs + 64]);
620
593
  c = bytes32(msg.data[abs + 64:abs + 96]);
621
- d = mask(abs + 96, 32 - keep);
594
+ d = bytes32(msg.data[abs + 96:abs + 128]);
622
595
  }
623
596
 
624
597
  /// @notice Consume a dynamic block with a 160-byte payload (five 32-byte words).
@@ -629,20 +602,16 @@ library Cursors {
629
602
  /// @return c Third decoded bytes32.
630
603
  /// @return d Fourth decoded bytes32.
631
604
  /// @return e Fifth decoded bytes32.
632
- /// @param keep Number of bytes to keep from the final payload word (1..32).
633
605
  function unpack160(
634
606
  Cur memory cur,
635
- bytes4 key,
636
- uint keep
607
+ bytes4 key
637
608
  ) internal pure returns (bytes32 a, bytes32 b, bytes32 c, bytes32 d, bytes32 e) {
638
- if (keep == 0 || keep > 32) revert InvalidKeep();
639
- uint len = 128 + keep;
640
- uint abs = consume(cur, key, len, len);
609
+ uint abs = consume(cur, key, 160, 160);
641
610
  a = bytes32(msg.data[abs:abs + 32]);
642
611
  b = bytes32(msg.data[abs + 32:abs + 64]);
643
612
  c = bytes32(msg.data[abs + 64:abs + 96]);
644
613
  d = bytes32(msg.data[abs + 96:abs + 128]);
645
- e = mask(abs + 128, 32 - keep);
614
+ e = bytes32(msg.data[abs + 128:abs + 160]);
646
615
  }
647
616
 
648
617
  /// @notice Consume a dynamic block with a single uint payload.
@@ -650,7 +619,7 @@ library Cursors {
650
619
  /// @param key Expected dynamic block key.
651
620
  /// @return value Decoded uint value.
652
621
  function unpackUint(Cur memory cur, bytes4 key) internal pure returns (uint value) {
653
- value = uint(unpack32(cur, key, 32));
622
+ value = uint(unpack32(cur, key));
654
623
  }
655
624
 
656
625
  /// @notice Consume a dynamic block with two uint payload words.
@@ -659,7 +628,7 @@ library Cursors {
659
628
  /// @return a First decoded uint.
660
629
  /// @return b Second decoded uint.
661
630
  function unpack2Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b) {
662
- (bytes32 x, bytes32 y) = unpack64(cur, key, 32);
631
+ (bytes32 x, bytes32 y) = unpack64(cur, key);
663
632
  return (uint(x), uint(y));
664
633
  }
665
634
 
@@ -670,7 +639,7 @@ library Cursors {
670
639
  /// @return b Second decoded uint.
671
640
  /// @return c Third decoded uint.
672
641
  function unpack3Uint(Cur memory cur, bytes4 key) internal pure returns (uint a, uint b, uint c) {
673
- (bytes32 x, bytes32 y, bytes32 z) = unpack96(cur, key, 32);
642
+ (bytes32 x, bytes32 y, bytes32 z) = unpack96(cur, key);
674
643
  return (uint(x), uint(y), uint(z));
675
644
  }
676
645
 
@@ -839,35 +808,35 @@ library Cursors {
839
808
  /// @param cur Cursor; advanced past the block.
840
809
  /// @return account Account identifier.
841
810
  function unpackAccount(Cur memory cur) internal pure returns (bytes32 account) {
842
- account = unpack32(cur, Keys.Account, 32);
811
+ account = unpack32(cur, Keys.Account);
843
812
  }
844
813
 
845
814
  /// @notice Consume a NODE block and return the node ID.
846
815
  /// @param cur Cursor; advanced past the block.
847
816
  /// @return node Node identifier.
848
817
  function unpackNode(Cur memory cur) internal pure returns (uint node) {
849
- node = uint(unpack32(cur, Keys.Node, 32));
818
+ node = uint(unpack32(cur, Keys.Node));
850
819
  }
851
820
 
852
821
  /// @notice Consume a RATE block and return the value.
853
822
  /// @param cur Cursor; advanced past the block.
854
823
  /// @return value Encoded ratio or rate.
855
824
  function unpackRate(Cur memory cur) internal pure returns (uint value) {
856
- value = uint(unpack32(cur, Keys.Rate, 32));
825
+ value = uint(unpack32(cur, Keys.Rate));
857
826
  }
858
827
 
859
828
  /// @notice Consume a QUANTITY block and return the amount.
860
829
  /// @param cur Cursor; advanced past the block.
861
830
  /// @return amount Scalar quantity value.
862
831
  function unpackQuantity(Cur memory cur) internal pure returns (uint amount) {
863
- amount = uint(unpack32(cur, Keys.Quantity, 32));
832
+ amount = uint(unpack32(cur, Keys.Quantity));
864
833
  }
865
834
 
866
835
  /// @notice Consume a FEE block and return the amount.
867
836
  /// @param cur Cursor; advanced past the block.
868
837
  /// @return amount Fee amount.
869
838
  function unpackFee(Cur memory cur) internal pure returns (uint amount) {
870
- amount = uint(unpack32(cur, Keys.Fee, 32));
839
+ amount = uint(unpack32(cur, Keys.Fee));
871
840
  }
872
841
 
873
842
  /// @notice Consume an ASSET block and return the asset descriptor fields.
@@ -875,7 +844,7 @@ library Cursors {
875
844
  /// @return asset Asset identifier.
876
845
  /// @return meta Asset metadata slot.
877
846
  function unpackAsset(Cur memory cur) internal pure returns (bytes32 asset, bytes32 meta) {
878
- (asset, meta) = unpack64(cur, Keys.Asset, 32);
847
+ (asset, meta) = unpack64(cur, Keys.Asset);
879
848
  }
880
849
 
881
850
  /// @notice Consume an ACCOUNT_ASSET form block and return its fields as separate values.
@@ -897,20 +866,12 @@ library Cursors {
897
866
  (value.account, value.asset, value.meta) = unpackAccountAsset(cur);
898
867
  }
899
868
 
900
- /// @notice Consume a RELOCATION block and return the host and amount.
901
- /// @param cur Cursor; advanced past the block.
902
- /// @return host Host node ID receiving the funding.
903
- /// @return amount Funding amount.
904
- function unpackRelocation(Cur memory cur) internal pure returns (uint host, uint amount) {
905
- (host, amount) = unpack2Uint(cur, Keys.Relocation);
906
- }
907
-
908
869
  /// @notice Consume a BOUNTY block and return the reward amount and relayer.
909
870
  /// @param cur Cursor; advanced past the block.
910
871
  /// @return amount Relayer reward amount.
911
872
  /// @return relayer Relayer account identifier.
912
873
  function unpackBounty(Cur memory cur) internal pure returns (uint amount, bytes32 relayer) {
913
- (bytes32 x, bytes32 y) = unpack64(cur, Keys.Bounty, 32);
874
+ (bytes32 x, bytes32 y) = unpack64(cur, Keys.Bounty);
914
875
  amount = uint(x);
915
876
  relayer = y;
916
877
  }
package/blocks/Keys.sol CHANGED
@@ -68,8 +68,6 @@ library Keys {
68
68
  bytes4 constant Asset = bytes4(keccak256("asset(bytes32 asset, bytes32 meta)"));
69
69
  /// @dev Node identifier - (uint id)
70
70
  bytes4 constant Node = bytes4(keccak256("node(uint id)"));
71
- /// @dev Native value relocation entry - (uint host, uint amount)
72
- bytes4 constant Relocation = bytes4(keccak256("relocation(uint host, uint amount)"));
73
71
  /// @dev Relayer bounty - (uint amount, bytes32 relayer)
74
72
  bytes4 constant Bounty = bytes4(keccak256("bounty(uint amount, bytes32 relayer)"));
75
73
 
package/blocks/Schema.sol CHANGED
@@ -114,7 +114,6 @@ library Schemas {
114
114
  string constant Allocation = "allocation(uint host, bytes32 asset, bytes32 meta, uint amount)";
115
115
  string constant Allowance = "allowance(uint host, bytes32 asset, bytes32 meta, uint amount)";
116
116
  string constant Transaction = "transaction(bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount)";
117
- string constant Relocation = "relocation(uint host, uint amount)";
118
117
  string constant Call = "call(uint target, uint value, bytes data)";
119
118
  string constant Step = "step(uint target, uint value, bytes request)";
120
119
  string constant Bounty = "bounty(uint amount, bytes32 relayer)";
package/commands/Base.sol CHANGED
@@ -18,25 +18,6 @@ struct CommandContext {
18
18
  bytes request;
19
19
  }
20
20
 
21
- /// @notice ABI-encode a command call from a command ID and execution context.
22
- /// @dev Derives the function selector from `cid` via `Ids.commandSelector(cid)`.
23
- /// Reverts if `cid` is not a valid command ID.
24
- /// @param cid Command node ID embedding the target selector.
25
- /// @param account Caller account identifier for the command context.
26
- /// @param state Current state block stream passed to the command.
27
- /// @param request Input block stream for the command invocation.
28
- /// @return ABI-encoded calldata for the command entry point.
29
- function encodeCommandCall(
30
- uint cid,
31
- bytes32 account,
32
- bytes memory state,
33
- bytes calldata request
34
- ) pure returns (bytes memory) {
35
- bytes4 selector = Ids.commandSelector(cid);
36
- CommandContext memory ctx = CommandContext(account, state, request);
37
- return abi.encodeWithSelector(selector, ctx);
38
- }
39
-
40
21
  /// @title CommandBase
41
22
  /// @notice Abstract base for all rootzero command contracts.
42
23
  /// Provides access control modifiers, event emission, and the `commandId`
package/commands/Burn.sol CHANGED
@@ -5,8 +5,6 @@ import { CommandBase, CommandContext, Keys } from "./Base.sol";
5
5
  import { Cursors, Cur } from "../Cursors.sol";
6
6
  using Cursors for Cur;
7
7
 
8
- string constant NAME = "burn";
9
-
10
8
  abstract contract BurnHook {
11
9
  /// @notice Override to burn or consume the provided balance amount.
12
10
  /// Called once per BALANCE block in state.
@@ -22,6 +20,8 @@ abstract contract BurnHook {
22
20
  /// @notice Command that irreversibly destroys each BALANCE state block via a virtual hook.
23
21
  /// Produces no output state.
24
22
  abstract contract Burn is CommandBase, BurnHook {
23
+ string private constant NAME = "burn";
24
+
25
25
  uint internal immutable burnId = commandId(NAME);
26
26
 
27
27
  constructor() {
@@ -3,7 +3,6 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "./Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../Cursors.sol";
6
- string constant NAME = "creditAccount";
7
6
 
8
7
  using Cursors for Cur;
9
8
 
@@ -22,6 +21,8 @@ abstract contract CreditAccountHook {
22
21
  /// Use for internally recording credits that have already been settled externally.
23
22
  /// An optional ACCOUNT block in the request overrides the default `c.account` destination.
24
23
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
24
+ string private constant NAME = "creditAccount";
25
+
25
26
  uint internal immutable creditAccountId = commandId(NAME);
26
27
 
27
28
  constructor() {
@@ -4,8 +4,6 @@ pragma solidity ^0.8.33;
4
4
  import { CommandContext, CommandBase, Keys } from "./Base.sol";
5
5
  import { Cursors, Cur, Schemas, Writer, Writers } from "../Cursors.sol";
6
6
 
7
- string constant NAME = "debitAccount";
8
-
9
7
  using Cursors for Cur;
10
8
  using Writers for Writer;
11
9
 
@@ -24,6 +22,8 @@ abstract contract DebitAccountHook {
24
22
  /// Use for internally recording debits. The virtual `debitAccount` hook is called once per
25
23
  /// AMOUNT block; the default batch implementation handles the full request loop.
26
24
  abstract contract DebitAccount is CommandBase, DebitAccountHook {
25
+ string private constant NAME = "debitAccount";
26
+
27
27
  uint internal immutable debitAccountId = commandId(NAME);
28
28
 
29
29
  constructor() {
@@ -5,9 +5,6 @@ import { CommandContext, CommandBase, CommandPayable, Keys } from "./Base.sol";
5
5
  import { Cursors, Cur, Schemas, Writer, Writers } from "../Cursors.sol";
6
6
  import { Budget, Values } from "../utils/Value.sol";
7
7
 
8
- string constant DEPOSIT = "deposit";
9
- string constant DEPOSIT_PAYABLE = "depositPayable";
10
-
11
8
  using Cursors for Cur;
12
9
  using Writers for Writer;
13
10
 
@@ -39,10 +36,12 @@ abstract contract DepositPayableHook {
39
36
  /// Use `deposit` for assets arriving from outside the protocol (e.g. ERC-20 transfers, ETH).
40
37
  /// For internal balance deductions, use `debitAccount` instead.
41
38
  abstract contract Deposit is CommandBase, DepositHook {
42
- uint internal immutable depositId = commandId(DEPOSIT);
39
+ string private constant NAME = "deposit";
40
+
41
+ uint internal immutable depositId = commandId(NAME);
43
42
 
44
43
  constructor() {
45
- emit Command(host, depositId, DEPOSIT, Schemas.Amount, Keys.Empty, Keys.Balance, false);
44
+ emit Command(host, depositId, NAME, Schemas.Amount, Keys.Empty, Keys.Balance, false);
46
45
  }
47
46
 
48
47
  function deposit(
@@ -65,10 +64,12 @@ abstract contract Deposit is CommandBase, DepositHook {
65
64
  /// @notice Command that receives externally sourced assets and records them as BALANCE state.
66
65
  /// Use `depositPayable` when the hook needs tracked access to `msg.value` via a mutable budget.
67
66
  abstract contract DepositPayable is CommandPayable, DepositPayableHook {
68
- uint internal immutable depositPayableId = commandId(DEPOSIT_PAYABLE);
67
+ string private constant NAME = "depositPayable";
68
+
69
+ uint internal immutable depositPayableId = commandId(NAME);
69
70
 
70
71
  constructor() {
71
- emit Command(host, depositPayableId, DEPOSIT_PAYABLE, Schemas.Amount, Keys.Empty, Keys.Balance, true);
72
+ emit Command(host, depositPayableId, NAME, Schemas.Amount, Keys.Empty, Keys.Balance, true);
72
73
  }
73
74
 
74
75
  function depositPayable(
package/commands/Pipe.sol CHANGED
@@ -8,11 +8,18 @@ import {Budget, Values} from "../utils/Value.sol";
8
8
 
9
9
  using Cursors for Cur;
10
10
 
11
- string constant NAME = "pipePayable";
12
-
13
11
  abstract contract PipePayableHook {
14
- function dispatchStep(
15
- uint target,
12
+ /// @notice Override to dispatch one piped command step.
13
+ /// Called once per STEP block. The returned bytes become the state passed to
14
+ /// the next step.
15
+ /// @param id Command node ID to invoke or handle.
16
+ /// @param account Account identifier for the piped command context.
17
+ /// @param state Current threaded state block stream.
18
+ /// @param request Step request block stream.
19
+ /// @param value Native value assigned to this step.
20
+ /// @return Updated state block stream for the next step.
21
+ function dispatchCommand(
22
+ uint id,
16
23
  bytes32 account,
17
24
  bytes memory state,
18
25
  bytes calldata request,
@@ -22,10 +29,12 @@ abstract contract PipePayableHook {
22
29
 
23
30
  /// @title PipePayable
24
31
  /// @notice Command that sequences multiple sub-command STEP invocations in a single transaction.
25
- /// Each STEP block carries a target node, native value to forward, and an embedded request.
32
+ /// Each STEP block carries a command node, native value to forward, and an embedded request.
26
33
  /// State threads through the steps: each step's output becomes the next step's state.
27
34
  /// Admin accounts are not permitted to use `pipePayable`.
28
35
  abstract contract PipePayable is CommandPayable, PipePayableHook {
36
+ string private constant NAME = "pipePayable";
37
+
29
38
  uint internal immutable pipePayableId = commandId(NAME);
30
39
 
31
40
  constructor() {
@@ -43,7 +52,7 @@ abstract contract PipePayable is CommandPayable, PipePayableHook {
43
52
  while (input.i < input.bound) {
44
53
  (uint target, uint value, bytes calldata request) = input.unpackStep();
45
54
  uint spend = Values.use(budget, value);
46
- state = dispatchStep(target, account, state, request, spend);
55
+ state = dispatchCommand(target, account, state, request, spend);
47
56
  }
48
57
 
49
58
  settleValue(account, budget);
@@ -52,11 +61,7 @@ abstract contract PipePayable is CommandPayable, PipePayableHook {
52
61
  }
53
62
 
54
63
  /// @notice Execute the pipePayable command.
55
- function pipePayable(
56
- CommandContext calldata c
57
- ) external payable onlyCommand(c.account) returns (bytes memory) {
58
- if (Accounts.isAdmin(c.account)) revert Accounts.InvalidAccount();
59
- Budget memory budget = Values.fromMsg();
60
- return pipe(c.account, c.state, c.request, budget);
64
+ function pipePayable(CommandContext calldata c) external payable onlyCommand(c.account) returns (bytes memory) {
65
+ return pipe(Accounts.ensureNotAdmin(c.account), c.state, c.request, Values.fromMsg());
61
66
  }
62
67
  }
@@ -7,9 +7,6 @@ import {Budget, Values} from "../utils/Value.sol";
7
7
  using Cursors for Cur;
8
8
  using Writers for Writer;
9
9
 
10
- string constant PROVISION = "provision";
11
- string constant PP = "provisionPayable";
12
-
13
10
  /// @notice Shared provision hook used by `Provision`.
14
11
  abstract contract ProvisionHook {
15
12
  /// @notice Override to send or provision a custody value.
@@ -32,13 +29,15 @@ abstract contract ProvisionPayableHook {
32
29
  }
33
30
 
34
31
  /// @title Provision
35
- /// @notice Command that provisions assets to remote hosts from ALLOCATION request blocks.
32
+ /// @notice Command that provisions assets to peer hosts from ALLOCATION request blocks.
36
33
  /// Each request block supplies the target host plus an asset amount; the output is a CUSTODY state stream.
37
34
  abstract contract Provision is CommandBase, ProvisionHook {
38
- uint internal immutable provisionId = commandId(PROVISION);
35
+ string private constant NAME = "provision";
36
+
37
+ uint internal immutable provisionId = commandId(NAME);
39
38
 
40
39
  constructor() {
41
- emit Command(host, provisionId, PROVISION, Schemas.Allocation, Keys.Empty, Keys.Custody, false);
40
+ emit Command(host, provisionId, NAME, Schemas.Allocation, Keys.Empty, Keys.Custody, false);
42
41
  }
43
42
 
44
43
  function provision(CommandContext calldata c) external onlyCommand(c.account) returns (bytes memory) {
@@ -56,14 +55,16 @@ abstract contract Provision is CommandBase, ProvisionHook {
56
55
  }
57
56
 
58
57
  /// @title ProvisionPayable
59
- /// @notice Command that provisions assets to remote hosts from ALLOCATION request blocks.
58
+ /// @notice Command that provisions assets to peer hosts from ALLOCATION request blocks.
60
59
  /// Each request block supplies the target host plus an asset amount; the output is a CUSTODY state stream.
61
60
  /// The hook receives a mutable native-value budget drawn from `msg.value`.
62
61
  abstract contract ProvisionPayable is CommandPayable, ProvisionPayableHook {
63
- uint internal immutable provisionPayableId = commandId(PP);
62
+ string private constant NAME = "provisionPayable";
63
+
64
+ uint internal immutable provisionPayableId = commandId(NAME);
64
65
 
65
66
  constructor() {
66
- emit Command(host, provisionPayableId, PP, Schemas.Allocation, Keys.Empty, Keys.Custody, true);
67
+ emit Command(host, provisionPayableId, NAME, Schemas.Allocation, Keys.Empty, Keys.Custody, true);
67
68
  }
68
69
 
69
70
  function provisionPayable(
@@ -6,8 +6,6 @@ import { Cursors, Cur, Schemas, Tx } from "../Cursors.sol";
6
6
  import { Accounts } from "../utils/Accounts.sol";
7
7
  using Cursors for Cur;
8
8
 
9
- string constant NAME = "transfer";
10
-
11
9
  abstract contract TransferHook {
12
10
  /// @notice Override to execute a single transfer record from the request pipeline.
13
11
  /// Called once per PAYOUT block in the request.
@@ -20,6 +18,8 @@ abstract contract TransferHook {
20
18
  /// PAYOUT request blocks. Produces no state output.
21
19
  /// The virtual `transfer(value)` hook is called once per entry.
22
20
  abstract contract Transfer is CommandBase, TransferHook {
21
+ string private constant NAME = "transfer";
22
+
23
23
  uint internal immutable transferId = commandId(NAME);
24
24
 
25
25
  constructor() {
@@ -5,8 +5,6 @@ import { CommandContext, CommandBase, Keys } from "./Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../Cursors.sol";
6
6
  using Cursors for Cur;
7
7
 
8
- string constant NAME = "withdraw";
9
-
10
8
  abstract contract WithdrawHook {
11
9
  /// @notice Override to send funds to `account`.
12
10
  /// Called once per BALANCE block in state.
@@ -22,6 +20,8 @@ abstract contract WithdrawHook {
22
20
  /// Use `withdraw` for assets being sent outside the protocol (e.g. ERC-20 transfers, ETH sends).
23
21
  /// For internal balance credits, use `creditAccount` instead.
24
22
  abstract contract Withdraw is CommandBase, WithdrawHook {
23
+ string private constant NAME = "withdraw";
24
+
25
25
  uint internal immutable withdrawId = commandId(NAME);
26
26
 
27
27
  constructor() {
@@ -3,24 +3,25 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
6
7
  using Cursors for Cur;
7
8
 
8
- string constant NAME = "allowAssets";
9
-
10
9
  abstract contract AllowAssetsHook {
11
10
  /// @dev Override to allow a single asset/meta pair.
12
11
  /// Called once per ASSET block in the request.
13
- function allowAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
12
+ function allowAsset(bytes32 asset, bytes32 meta) internal virtual;
14
13
  }
15
14
 
16
15
  /// @title AllowAssets
17
16
  /// @notice Admin command that permits a list of (asset, meta) pairs via a virtual hook.
18
17
  /// Each ASSET block in the request calls `allowAsset`. Only callable by the admin account.
19
- abstract contract AllowAssets is CommandBase, AllowAssetsHook {
18
+ abstract contract AllowAssets is CommandBase, AdminEvent, AllowAssetsHook {
19
+ string private constant NAME = "allowAssets";
20
+
20
21
  uint internal immutable allowAssetsId = commandId(NAME);
21
22
 
22
23
  constructor() {
23
- emit Command(host, allowAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
+ emit Admin(host, allowAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
25
  }
25
26
 
26
27
  function allowAssets(
@@ -1,12 +1,11 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
- import { Cursors, Cur, Schemas } from "../../Cursors.sol";
4
+ import {CommandBase, CommandContext, Keys} from "../Base.sol";
5
+ import {Cursors, Cur, Schemas} from "../../Cursors.sol";
6
+ import {AdminEvent} from "../../events/Admin.sol";
6
7
  using Cursors for Cur;
7
8
 
8
- string constant NAME = "allowance";
9
-
10
9
  abstract contract AllowanceHook {
11
10
  /// @notice Apply or revoke one host-scoped allowance.
12
11
  /// Called once per ALLOWANCE block in the request. Implementations decide
@@ -22,11 +21,12 @@ abstract contract AllowanceHook {
22
21
  /// @title Allowance
23
22
  /// @notice Admin command that applies cross-host allowance entries via a virtual hook.
24
23
  /// Each ALLOWANCE block grants or updates a host-scoped asset cap. Only callable by the admin account.
25
- abstract contract Allowance is CommandBase, AllowanceHook {
24
+ abstract contract Allowance is CommandBase, AdminEvent, AllowanceHook {
25
+ string private constant NAME = "allowance";
26
26
  uint internal immutable allowanceId = commandId(NAME);
27
27
 
28
28
  constructor() {
29
- emit Command(host, allowanceId, NAME, Schemas.Allowance, Keys.Empty, Keys.Empty, false);
29
+ emit Admin(host, allowanceId, NAME, 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) {
@@ -3,19 +3,20 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
6
7
  using Cursors for Cur;
7
8
 
8
- string constant NAME = "authorize";
9
-
10
9
  /// @title Authorize
11
10
  /// @notice Admin command that grants authorization to a list of node IDs.
12
11
  /// Each NODE block in the request is authorized on the host.
13
12
  /// Only callable by the admin account.
14
- abstract contract Authorize is CommandBase {
13
+ abstract contract Authorize is CommandBase, AdminEvent {
14
+ string private constant NAME = "authorize";
15
+
15
16
  uint internal immutable authorizeId = commandId(NAME);
16
17
 
17
18
  constructor() {
18
- emit Command(host, authorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, authorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
20
  }
20
21
 
21
22
  function authorize(
@@ -3,24 +3,25 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
6
7
  using Cursors for Cur;
7
8
 
8
- string constant NAME = "denyAssets";
9
-
10
9
  abstract contract DenyAssetsHook {
11
10
  /// @dev Override to deny a single asset/meta pair.
12
11
  /// Called once per ASSET block in the request.
13
- function denyAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
12
+ function denyAsset(bytes32 asset, bytes32 meta) internal virtual;
14
13
  }
15
14
 
16
15
  /// @title DenyAssets
17
16
  /// @notice Admin command that blocks a list of (asset, meta) pairs via a virtual hook.
18
17
  /// Each ASSET block in the request calls `denyAsset`. Only callable by the admin account.
19
- abstract contract DenyAssets is CommandBase, DenyAssetsHook {
18
+ abstract contract DenyAssets is CommandBase, AdminEvent, DenyAssetsHook {
19
+ string private constant NAME = "denyAssets";
20
+
20
21
  uint internal immutable denyAssetsId = commandId(NAME);
21
22
 
22
23
  constructor() {
23
- emit Command(host, denyAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
+ emit Admin(host, denyAssetsId, NAME, Schemas.Asset, Keys.Empty, Keys.Empty, false);
24
25
  }
25
26
 
26
27
  function denyAssets(
@@ -3,8 +3,7 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur } from "../../Cursors.sol";
6
-
7
- string constant NAME = "destroy";
6
+ import { AdminEvent } from "../../events/Admin.sol";
8
7
 
9
8
  using Cursors for Cur;
10
9
 
@@ -17,11 +16,13 @@ abstract contract DestroyHook {
17
16
  /// @title Destroy
18
17
  /// @notice Admin command that runs host teardown logic via a virtual hook.
19
18
  /// The full request is passed to `destroy` as a cursor. Only callable by the admin account.
20
- abstract contract Destroy is CommandBase, DestroyHook {
19
+ abstract contract Destroy is CommandBase, AdminEvent, DestroyHook {
20
+ string private constant NAME = "destroy";
21
+
21
22
  uint internal immutable destroyId = commandId(NAME);
22
23
 
23
24
  constructor(string memory input) {
24
- emit Command(host, destroyId, NAME, input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, destroyId, NAME, input, Keys.Empty, Keys.Empty, false);
25
26
  }
26
27
 
27
28
  function destroy(
@@ -3,22 +3,23 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {CommandContext, CommandPayable, Keys} from "../Base.sol";
5
5
  import {Cursors, Cur, Schemas} from "../../Cursors.sol";
6
+ import {AdminEvent} from "../../events/Admin.sol";
6
7
  import {Budget, Values} from "../../utils/Value.sol";
7
8
  import {Ids} from "../../utils/Ids.sol";
8
9
 
9
10
  using Cursors for Cur;
10
11
 
11
- string constant NAME = "executePayable";
12
-
13
12
  /// @title ExecutePayable
14
13
  /// @notice Admin command that forwards raw calldata to one or more target nodes.
15
14
  /// Each CALL block specifies a target node ID, native value, and raw calldata payload.
16
15
  /// Only callable by the admin account.
17
- abstract contract ExecutePayable is CommandPayable {
16
+ abstract contract ExecutePayable is CommandPayable, AdminEvent {
17
+ string private constant NAME = "executePayable";
18
+
18
19
  uint internal immutable executePayableId = commandId(NAME);
19
20
 
20
21
  constructor() {
21
- emit Command(host, executePayableId, NAME, Schemas.Call, Keys.Empty, Keys.Empty, true);
22
+ emit Admin(host, executePayableId, NAME, Schemas.Call, Keys.Empty, Keys.Empty, true);
22
23
  }
23
24
 
24
25
  function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
@@ -3,8 +3,7 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur } from "../../Cursors.sol";
6
-
7
- string constant NAME = "init";
6
+ import { AdminEvent } from "../../events/Admin.sol";
8
7
 
9
8
  using Cursors for Cur;
10
9
 
@@ -17,11 +16,13 @@ abstract contract InitHook {
17
16
  /// @title Init
18
17
  /// @notice Admin command that runs host initialization logic via a virtual hook.
19
18
  /// The full request is passed to `init` as a cursor. Only callable by the admin account.
20
- abstract contract Init is CommandBase, InitHook {
19
+ abstract contract Init is CommandBase, AdminEvent, InitHook {
20
+ string private constant NAME = "init";
21
+
21
22
  uint internal immutable initId = commandId(NAME);
22
23
 
23
24
  constructor(string memory input) {
24
- emit Command(host, initId, NAME, input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, initId, NAME, input, Keys.Empty, Keys.Empty, false);
25
26
  }
26
27
 
27
28
  function init(
@@ -3,19 +3,20 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "../Base.sol";
5
5
  import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
+ import { AdminEvent } from "../../events/Admin.sol";
6
7
  using Cursors for Cur;
7
8
 
8
- string constant NAME = "unauthorize";
9
-
10
9
  /// @title Unauthorize
11
10
  /// @notice Admin command that revokes authorization from a list of node IDs.
12
11
  /// Each NODE block in the request is deauthorized on the host.
13
12
  /// Only callable by the admin account.
14
- abstract contract Unauthorize is CommandBase {
13
+ abstract contract Unauthorize is CommandBase, AdminEvent {
14
+ string private constant NAME = "unauthorize";
15
+
15
16
  uint internal immutable unauthorizeId = commandId(NAME);
16
17
 
17
18
  constructor() {
18
- emit Command(host, unauthorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, unauthorizeId, NAME, Schemas.Node, Keys.Empty, Keys.Empty, false);
19
20
  }
20
21
 
21
22
  function unauthorize(
package/core/Calls.sol CHANGED
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {AccessControl} from "./Access.sol";
5
+ import {CommandContext} from "../commands/Base.sol";
5
6
  import {Ids} from "../utils/Ids.sol";
6
7
 
7
8
  /// @dev Emitted when a trusted inter-node call fails.
@@ -53,7 +54,8 @@ abstract contract NodeCalls is AccessControl {
53
54
  /// @param data Encoded calldata to send.
54
55
  /// @return out Return data from the successful call.
55
56
  function callTo(uint node, uint value, bytes memory data) internal returns (bytes memory out) {
56
- address addr = Ids.nodeAddr(ensureTrusted(node));
57
+ ensureTrusted(node);
58
+ address addr = Ids.nodeAddr(node);
57
59
  return callAddr(addr, value, data);
58
60
  }
59
61
 
@@ -64,7 +66,19 @@ abstract contract NodeCalls is AccessControl {
64
66
  /// @param data Encoded calldata to send.
65
67
  /// @return out Return data from the successful query.
66
68
  function queryTo(uint node, bytes memory data) internal view returns (bytes memory out) {
67
- address addr = Ids.nodeAddr(ensureTrusted(node));
69
+ ensureTrusted(node);
70
+ address addr = Ids.nodeAddr(node);
68
71
  return queryAddr(addr, data);
69
72
  }
73
+
74
+ /// @notice Encode and call a trusted command node.
75
+ /// @param ctx Command execution context.
76
+ /// @param cid Command node ID embedding the target selector.
77
+ /// @param value Native value to forward in wei.
78
+ /// @return Decoded command output block stream.
79
+ function callCommand(CommandContext memory ctx, uint cid, uint value) internal returns (bytes memory) {
80
+ bytes4 selector = Ids.commandSelector(cid);
81
+ bytes memory data = abi.encodeWithSelector(selector, ctx);
82
+ return abi.decode(callTo(cid, value, data), (bytes));
83
+ }
70
84
  }
package/core/Host.sol CHANGED
@@ -5,7 +5,6 @@ import {AccessControl} from "./Access.sol";
5
5
  import {Authorize} from "../commands/admin/Authorize.sol";
6
6
  import {Unauthorize} from "../commands/admin/Unauthorize.sol";
7
7
  import {ExecutePayable} from "../commands/admin/Execute.sol";
8
- import {RelocatePayable} from "../commands/admin/Relocate.sol";
9
8
  import {HostAnnouncedEvent} from "../events/Host.sol";
10
9
  import {IHostDiscovery} from "../interfaces/IHostDiscovery.sol";
11
10
  import {Ids} from "../utils/Ids.sol";
@@ -26,10 +25,10 @@ abstract contract HostDiscovery is HostAnnouncedEvent, IHostDiscovery {
26
25
 
27
26
  /// @title Host
28
27
  /// @notice Abstract base contract for rootzero host implementations.
29
- /// Inherits admin command support (authorize, unauthorize, executePayable, relocatePayable) and
28
+ /// Inherits admin command support (authorize, unauthorize, executePayable) and
30
29
  /// optionally announces itself to a discovery contract at deployment.
31
30
  /// Accepts native ETH payments via the `receive` function.
32
- abstract contract Host is Authorize, Unauthorize, ExecutePayable, RelocatePayable {
31
+ abstract contract Host is Authorize, Unauthorize, ExecutePayable {
33
32
  /// @param cmdr Commander address; passed to `AccessControl`.
34
33
  /// If `cmdr` is a deployed contract, the host calls `announceHost`
35
34
  /// on it during construction to register with the discovery registry.
@@ -0,0 +1,31 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { EventEmitter } from "./Emitter.sol";
5
+
6
+ string constant ABI =
7
+ "event Admin(uint indexed host, uint id, string name, string request, bytes4 state, bytes4 output, bool acceptsValue)";
8
+
9
+ /// @notice Emitted once per admin command during host deployment to publish its request schema and state keys.
10
+ abstract contract AdminEvent is EventEmitter {
11
+ /// @param host Host node ID that owns this admin command.
12
+ /// @param id Command node ID.
13
+ /// @param name Human-readable command name.
14
+ /// @param request Schema DSL string describing the request shape.
15
+ /// @param state Block key expected for input state, or `Keys.Empty`.
16
+ /// @param output Block key produced for output state, or `Keys.Empty`.
17
+ /// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
18
+ event Admin(
19
+ uint indexed host,
20
+ uint id,
21
+ string name,
22
+ string request,
23
+ bytes4 state,
24
+ bytes4 output,
25
+ bool acceptsValue
26
+ );
27
+
28
+ constructor() {
29
+ emit EventAbi(ABI);
30
+ }
31
+ }
package/events/Peer.sol CHANGED
@@ -4,16 +4,16 @@ pragma solidity ^0.8.33;
4
4
  import { EventEmitter } from "./Emitter.sol";
5
5
 
6
6
  string constant ABI =
7
- "event Peer(uint indexed host, uint id, string name, string schema, bool acceptsValue)";
7
+ "event Peer(uint indexed host, uint id, string name, string request, bool acceptsValue)";
8
8
 
9
- /// @notice Emitted once per peer during host deployment to publish its schema.
9
+ /// @notice Emitted once per peer during host deployment to publish its request schema.
10
10
  abstract contract PeerEvent is EventEmitter {
11
11
  /// @param host Host node ID that owns this peer.
12
12
  /// @param id Peer node ID.
13
13
  /// @param name Human-readable peer name.
14
- /// @param schema Schema DSL string describing the peer request shape.
14
+ /// @param request Schema DSL string describing the peer request shape.
15
15
  /// @param acceptsValue Whether the peer entrypoint accepts nonzero `msg.value`.
16
- event Peer(uint indexed host, uint id, string name, string schema, bool acceptsValue);
16
+ event Peer(uint indexed host, uint id, string name, string request, bool acceptsValue);
17
17
 
18
18
  constructor() {
19
19
  emit EventAbi(ABI);
@@ -3,14 +3,14 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { EventEmitter } from "./Emitter.sol";
5
5
 
6
- string constant ABI = "event RootZero(bytes32 indexed account, uint deadline, uint value)";
6
+ string constant ABI = "event Piped(bytes32 indexed account, uint deadline, uint value)";
7
7
 
8
8
  /// @notice Emitted for root-level protocol actions (e.g. governance or protocol-wide operations).
9
- abstract contract RootZeroEvent is EventEmitter {
9
+ abstract contract PipedEvent is EventEmitter {
10
10
  /// @param account Account identifier associated with the action.
11
11
  /// @param deadline Expiry timestamp of the action.
12
12
  /// @param value Native value associated with the action.
13
- event RootZero(bytes32 indexed account, uint deadline, uint value);
13
+ event Piped(bytes32 indexed account, uint deadline, uint value);
14
14
 
15
15
  constructor() {
16
16
  emit EventAbi(ABI);
package/events/Query.sol CHANGED
@@ -3,16 +3,16 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {EventEmitter} from "./Emitter.sol";
5
5
 
6
- string constant ABI = "event Query(uint indexed host, uint id, string name, string input, string output)";
6
+ string constant ABI = "event Query(uint indexed host, uint id, string name, string request, string response)";
7
7
 
8
8
  /// @notice Emitted once per query during host deployment to publish its request and response schemas.
9
9
  abstract contract QueryEvent is EventEmitter {
10
10
  /// @param host Host node ID that owns this query.
11
11
  /// @param id Query node ID.
12
12
  /// @param name Human-readable query name.
13
- /// @param input Schema DSL string describing the query request shape.
14
- /// @param output Schema DSL string describing the query response shape.
15
- event Query(uint indexed host, uint id, string name, string input, string output);
13
+ /// @param request Schema DSL string describing the query request shape.
14
+ /// @param response Schema DSL string describing the query response shape.
15
+ event Query(uint indexed host, uint id, string name, string request, string response);
16
16
 
17
17
  constructor() {
18
18
  emit EventAbi(ABI);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
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",
@@ -7,12 +7,11 @@ import { Cursors, Cur, Schemas } from "../Cursors.sol";
7
7
 
8
8
  using Cursors for Cur;
9
9
 
10
- string constant NAME = "peerAllowAssets";
11
-
12
10
  /// @title PeerAllowAssets
13
- /// @notice Peer that permits a list of (asset, meta) pairs on behalf of a remote host.
11
+ /// @notice Peer that permits a list of (asset, meta) pairs on behalf of a peer host.
14
12
  /// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
15
13
  abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
14
+ string private constant NAME = "peerAllowAssets";
16
15
  uint internal immutable peerAllowAssetsId = peerId(NAME);
17
16
 
18
17
  constructor() {
@@ -7,13 +7,12 @@ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
7
 
8
8
  using Cursors for Cur;
9
9
 
10
- string constant NAME = "peerAllowance";
11
-
12
10
  /// @title PeerAllowance
13
- /// @notice Peer that lets a trusted remote host request or refresh its own allowance.
14
- /// Each AMOUNT block in the request is scoped to `caller()` and passed to the shared
15
- /// allowance hook as a host-scoped allowance. Restricted to trusted peers.
11
+ /// @notice Peer that lets a trusted peer host request or refresh its own allowance.
12
+ /// Each AMOUNT block in the request is scoped to the peer host and passed to the
13
+ /// shared allowance hook as a host-scoped allowance. Restricted to trusted peers.
16
14
  abstract contract PeerAllowance is PeerBase, AllowanceHook {
15
+ string private constant NAME = "peerAllowance";
17
16
  uint internal immutable peerAllowanceId = peerId(NAME);
18
17
 
19
18
  constructor() {
@@ -4,24 +4,23 @@ pragma solidity ^0.8.33;
4
4
  import {PeerBase} from "./Base.sol";
5
5
  import {Cursors, Cur, Schemas} from "../Cursors.sol";
6
6
 
7
- string constant NAME = "peerAssetPull";
8
-
9
7
  using Cursors for Cur;
10
8
 
11
- abstract contract PeerAssetPullHook {
12
- /// @notice Override to process one incoming amount-based asset pull request from a remote host.
13
- /// @param peer Host node ID derived from the caller address.
9
+ abstract contract AssetPullHook {
10
+ /// @notice Override to process one incoming amount-based asset pull request from a peer host.
11
+ /// @param peer Peer host node ID for this request.
14
12
  /// @param asset Requested asset identifier.
15
13
  /// @param meta Requested asset metadata slot.
16
14
  /// @param amount Requested amount in the asset's native units.
17
- function peerAssetPull(uint peer, bytes32 asset, bytes32 meta, uint amount) internal virtual;
15
+ function assetPull(uint peer, bytes32 asset, bytes32 meta, uint amount) internal virtual;
18
16
  }
19
17
 
20
18
  /// @title PeerAssetPull
21
- /// @notice Peer that pulls requested asset amounts from a remote host into this one.
22
- /// Each AMOUNT block in the request calls `peerAssetPull(peer, asset, meta, amount)`, where `peer`
23
- /// is derived from `msg.sender`. Restricted to trusted peers.
24
- abstract contract PeerAssetPull is PeerBase, PeerAssetPullHook {
19
+ /// @notice Peer that pulls requested asset amounts from a peer host into this one.
20
+ /// Each AMOUNT block in the request calls `assetPull(peer, asset, meta, amount)`.
21
+ /// Restricted to trusted peers.
22
+ abstract contract PeerAssetPull is PeerBase, AssetPullHook {
23
+ string private constant NAME = "peerAssetPull";
25
24
  uint internal immutable peerAssetPullId = peerId(NAME);
26
25
 
27
26
  constructor() {
@@ -35,7 +34,7 @@ abstract contract PeerAssetPull is PeerBase, PeerAssetPullHook {
35
34
 
36
35
  while (assets.i < assets.bound) {
37
36
  (bytes32 asset, bytes32 meta, uint amount) = assets.unpackAmount();
38
- peerAssetPull(peer, asset, meta, amount);
37
+ assetPull(peer, asset, meta, amount);
39
38
  }
40
39
 
41
40
  assets.complete();
@@ -7,12 +7,11 @@ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
7
 
8
8
  using Cursors for Cur;
9
9
 
10
- string constant NAME = "peerDenyAssets";
11
-
12
10
  /// @title PeerDenyAssets
13
- /// @notice Peer that blocks a list of (asset, meta) pairs on behalf of a remote host.
11
+ /// @notice Peer that blocks a list of (asset, meta) pairs on behalf of a peer host.
14
12
  /// Each ASSET block in the request calls `denyAsset`. Restricted to trusted peers.
15
13
  abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook {
14
+ string private constant NAME = "peerDenyAssets";
16
15
  uint internal immutable peerDenyAssetsId = peerId(NAME);
17
16
 
18
17
  constructor() {
package/peer/Settle.sol CHANGED
@@ -7,12 +7,11 @@ import { Cursors, Cur, Schemas } from "../Cursors.sol";
7
7
 
8
8
  using Cursors for Cur;
9
9
 
10
- string constant NAME = "peerSettle";
11
-
12
10
  /// @title PeerSettle
13
11
  /// @notice Peer that consumes peer-supplied TRANSACTION blocks through the shared transfer hook.
14
12
  /// Each TRANSACTION block in the request calls `transfer(value)`. Restricted to trusted peers.
15
13
  abstract contract PeerSettle is PeerBase, TransferHook {
14
+ string private constant NAME = "peerSettle";
16
15
  uint internal immutable peerSettleId = peerId(NAME);
17
16
 
18
17
  constructor() {
@@ -8,8 +8,6 @@ import {QueryBase} from "./Base.sol";
8
8
  using Cursors for Cur;
9
9
  using Writers for Writer;
10
10
 
11
- string constant NAME = "isAllowedAsset";
12
-
13
11
  abstract contract IsAllowedAssetHook {
14
12
  /// @notice Resolve whether one asset tuple is allowed.
15
13
  /// Concrete implementations define the allowlist policy.
@@ -24,6 +22,7 @@ abstract contract IsAllowedAssetHook {
24
22
  /// The request is a run of `ASSET` blocks.
25
23
  /// The response returns one `STATUS` form block per query entry, preserving request order.
26
24
  abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
25
+ string private constant NAME = "isAllowedAsset";
27
26
  uint public immutable isAllowedAssetId = queryId(NAME);
28
27
 
29
28
  constructor() {
@@ -7,8 +7,6 @@ import {QueryBase} from "./Base.sol";
7
7
  using Cursors for Cur;
8
8
  using Writers for Writer;
9
9
 
10
- string constant NAME = "getBalances";
11
-
12
10
  abstract contract GetBalancesHook {
13
11
  /// @notice Resolve one account's balance for one supported asset.
14
12
  /// Concrete implementations define how assets are resolved.
@@ -24,6 +22,7 @@ abstract contract GetBalancesHook {
24
22
  /// The request is a run of `ACCOUNT_ASSET` form blocks.
25
23
  /// The response returns one `ACCOUNT_AMOUNT` form block per requested position, preserving request order.
26
24
  abstract contract GetBalances is QueryBase, GetBalancesHook {
25
+ string private constant NAME = "getBalances";
27
26
  uint public immutable getBalancesId = queryId(NAME);
28
27
 
29
28
  constructor() {
@@ -7,8 +7,6 @@ import {QueryBase} from "./Base.sol";
7
7
 
8
8
  using Cursors for Cur;
9
9
 
10
- string constant NAME = "getPosition";
11
-
12
10
  abstract contract GetPositionHook {
13
11
  /// @notice Resolve the position payload for one requested position.
14
12
  /// Concrete implementations must append exactly one `RESPONSE` block whose payload
@@ -30,6 +28,7 @@ abstract contract GetPositionHook {
30
28
  /// The request is a run of `ACCOUNT_ASSET` form blocks.
31
29
  /// The response returns one dynamic `RESPONSE` block per position entry, preserving request order.
32
30
  abstract contract GetPosition is QueryBase, GetPositionHook {
31
+ string private constant NAME = "getPosition";
33
32
  uint public immutable getPositionId = queryId(NAME);
34
33
  uint internal immutable positionResponseSize;
35
34
 
@@ -92,6 +92,14 @@ library Accounts {
92
92
  return account;
93
93
  }
94
94
 
95
+ /// @notice Assert that `account` is not an admin account and return it unchanged.
96
+ /// @param account Account ID to validate.
97
+ /// @return The same `account` value if valid.
98
+ function ensureNotAdmin(bytes32 account) internal pure returns (bytes32) {
99
+ if (isAdmin(account)) revert InvalidAccount();
100
+ return account;
101
+ }
102
+
95
103
  /// @notice Extract the EVM address embedded in an EVM-family account ID.
96
104
  /// Reverts if `account` is not an EVM-family account.
97
105
  /// @param account EVM-family account ID.
package/utils/Ids.sol CHANGED
@@ -47,6 +47,11 @@ library Ids {
47
47
  return uint32(id >> 224) == Query;
48
48
  }
49
49
 
50
+ /// @notice Return true if `id` is a local node ID for this contract.
51
+ function isLocalNode(uint id) internal view returns (bool) {
52
+ return isLocalFamily(id, Node) && address(uint160(id)) == address(this);
53
+ }
54
+
50
55
  /// @notice Assert that `id` is a command ID and return it unchanged.
51
56
  /// @param id Node ID to validate.
52
57
  /// @return cid The same `id` value if it is a command.
package/utils/Utils.sol CHANGED
@@ -170,7 +170,7 @@ function isFamily(uint value, uint24 family) pure returns (bool) {
170
170
  /// @notice Check whether `value` was created on the current chain.
171
171
  /// @param value ID to test.
172
172
  /// @return True if bits [223:192] of `value` equal `block.chainid`.
173
- function isLocal(uint value) view returns (bool) {
173
+ function isLocalChain(uint value) view returns (bool) {
174
174
  return uint32(value >> 192) == block.chainid;
175
175
  }
176
176
 
@@ -179,7 +179,7 @@ function isLocal(uint value) view returns (bool) {
179
179
  /// @param family Expected 24-bit family tag.
180
180
  /// @return True if both the family and chainid fields match.
181
181
  function isLocalFamily(uint value, uint24 family) view returns (bool) {
182
- return isFamily(value, family) && isLocal(value);
182
+ return isFamily(value, family) && isLocalChain(value);
183
183
  }
184
184
 
185
185
  /// @notice Check whether two IDs share the same 64-bit base (type tag + chainid).
@@ -1,43 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import { CommandContext, CommandPayable, Keys } from "../Base.sol";
5
- import { Cursors, Cur, Schemas } from "../../Cursors.sol";
6
- import { Budget, Values } from "../../utils/Value.sol";
7
- using Cursors for Cur;
8
-
9
- string constant NAME = "relocatePayable";
10
-
11
- /// @title RelocatePayable
12
- /// @notice Admin command that forwards native value (ETH) to one or more destination hosts.
13
- /// Each RELOCATION block in the request specifies a target host node ID and an amount to forward.
14
- /// Only callable by the admin account.
15
- abstract contract RelocatePayable is CommandPayable {
16
- uint internal immutable relocatePayableId = commandId(NAME);
17
-
18
- constructor() {
19
- emit Command(host, relocatePayableId, NAME, Schemas.Relocation, Keys.Empty, Keys.Empty, true);
20
- }
21
-
22
- function relocatePayable(
23
- CommandContext calldata c
24
- ) external payable onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, , ) = cursor(c.request, 1);
26
- Budget memory budget = Values.fromMsg();
27
-
28
- while (request.i < request.bound) {
29
- (uint peer, uint amount) = request.unpackRelocation();
30
- callTo(peer, Values.use(budget, amount), "");
31
- }
32
-
33
- request.complete();
34
- settleValue(c.account, budget);
35
- return "";
36
- }
37
- }
38
-
39
-
40
-
41
-
42
-
43
-