@rootzero/contracts 0.9.8 → 1.0.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 (56) hide show
  1. package/Core.sol +1 -1
  2. package/Endpoints.sol +3 -1
  3. package/Events.sol +2 -0
  4. package/README.md +19 -18
  5. package/blocks/Cursors.sol +152 -162
  6. package/blocks/Keys.sol +10 -6
  7. package/blocks/Schema.sol +17 -9
  8. package/blocks/Writers.sol +4 -4
  9. package/commands/Burn.sol +7 -4
  10. package/commands/Credit.sol +9 -9
  11. package/commands/Debit.sol +7 -4
  12. package/commands/Deposit.sol +14 -8
  13. package/commands/Payout.sol +49 -0
  14. package/commands/Provision.sol +14 -8
  15. package/commands/Relay.sol +58 -0
  16. package/commands/Withdraw.sol +10 -9
  17. package/commands/admin/AllowAssets.sol +9 -4
  18. package/commands/admin/Allowance.sol +7 -4
  19. package/commands/admin/Appoint.sol +7 -4
  20. package/commands/admin/Authorize.sol +7 -4
  21. package/commands/admin/DenyAssets.sol +9 -4
  22. package/commands/admin/Destroy.sol +5 -3
  23. package/commands/admin/Dismiss.sol +7 -4
  24. package/commands/admin/Execute.sol +8 -5
  25. package/commands/admin/Init.sol +5 -3
  26. package/commands/admin/Unauthorize.sol +7 -4
  27. package/core/Host.sol +10 -1
  28. package/core/Pipeline.sol +4 -3
  29. package/core/Runtime.sol +3 -36
  30. package/core/Types.sol +1 -1
  31. package/docs/Schema.md +0 -1
  32. package/events/Admin.sol +11 -8
  33. package/events/Chain.sol +20 -0
  34. package/events/Command.sol +11 -8
  35. package/events/Peer.sol +5 -5
  36. package/events/Query.sol +2 -4
  37. package/events/Transfer.sol +22 -0
  38. package/guards/Revoke.sol +3 -3
  39. package/package.json +1 -1
  40. package/peer/AllowAssets.sol +5 -3
  41. package/peer/Allowance.sol +5 -3
  42. package/peer/BalancePull.sol +5 -3
  43. package/peer/DenyAssets.sol +5 -3
  44. package/peer/Dispatch.sol +51 -0
  45. package/peer/Pipe.sol +7 -5
  46. package/peer/Settle.sol +13 -8
  47. package/queries/Assets.sol +3 -3
  48. package/queries/Balances.sol +3 -3
  49. package/queries/Positions.sol +3 -3
  50. package/utils/Accounts.sol +6 -31
  51. package/utils/Actions.sol +11 -9
  52. package/utils/Assets.sol +21 -21
  53. package/utils/Ids.sol +12 -2
  54. package/utils/Layout.sol +21 -17
  55. package/utils/Utils.sol +2 -2
  56. package/commands/Transfer.sol +0 -54
@@ -22,14 +22,16 @@ 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, "1:0:0", input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, destroyId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false, false);
26
26
  }
27
27
 
28
+ /// @notice Run host teardown logic over the full admin request.
29
+ /// @param c Admin command context; `c.request` is passed through as a cursor.
30
+ /// @return Empty output state.
28
31
  function destroy(
29
32
  CommandContext calldata c
30
33
  ) external onlyAdmin(c.account) returns (bytes memory) {
31
- Cur memory input = cursor(c.request);
32
- destroy(input);
34
+ destroy(Cursors.open(c.request));
33
35
  return "";
34
36
  }
35
37
  }
@@ -16,20 +16,23 @@ abstract contract Dismiss is CommandBase, AdminEvent {
16
16
  uint internal immutable dismissId = commandId(NAME);
17
17
 
18
18
  constructor() {
19
- emit Admin(host, dismissId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, dismissId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false, false);
20
20
  }
21
21
 
22
+ /// @notice Dismiss each ACCOUNT block in the admin request from guardian status.
23
+ /// @param c Admin command context; `c.request` must contain ACCOUNT blocks.
24
+ /// @return Empty output state.
22
25
  function dismiss(
23
26
  CommandContext calldata c
24
27
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, ) = cursor(c.request, 1);
28
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
26
29
 
27
- while (request.i < request.bound) {
30
+ while (request.i < request.len) {
28
31
  bytes32 account = request.unpackAccount();
29
32
  setGuardian(account, false);
30
33
  }
31
34
 
32
- request.close();
35
+ request.complete();
33
36
  return "";
34
37
  }
35
38
  }
@@ -14,27 +14,30 @@ using Cursors for Cur;
14
14
  /// @notice Admin command that forwards raw calldata to one or more target nodes.
15
15
  /// Each CALL block specifies a target node ID, native value, and raw calldata payload.
16
16
  /// Only callable by the admin account.
17
+ /// Unspent top-level `msg.value` remains on this host.
17
18
  abstract contract ExecutePayable is CommandBase, Payable, AdminEvent {
18
19
  string private constant NAME = "executePayable";
19
20
 
20
21
  uint internal immutable executePayableId = commandId(NAME);
21
22
 
22
23
  constructor() {
23
- emit Admin(host, executePayableId, NAME, "1:0:0", Schemas.Call, Keys.Empty, Keys.Empty, true);
24
+ emit Admin(host, executePayableId, NAME, "1:0:0", Schemas.Call, Keys.Empty, Keys.Empty, false, true);
24
25
  }
25
26
 
27
+ /// @notice Execute each CALL block in the admin request.
28
+ /// @param c Admin command context; `c.request` must contain CALL blocks.
29
+ /// @return Empty output state.
26
30
  function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
27
- (Cur memory request, ) = cursor(c.request, 1);
31
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
28
32
  Budget memory budget = valueBudget();
29
33
 
30
- while (request.i < request.bound) {
34
+ while (request.i < request.len) {
31
35
  (uint target, uint value, bytes calldata data) = request.unpackCall();
32
36
  address addr = Ids.nodeAddr(target);
33
37
  callAddr(addr, useValue(budget, value), data);
34
38
  }
35
39
 
36
- request.close();
37
- settleValue(c.account, budget);
40
+ request.complete();
38
41
  return "";
39
42
  }
40
43
  }
@@ -22,14 +22,16 @@ 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, "1:0:0", input, Keys.Empty, Keys.Empty, false);
25
+ emit Admin(host, initId, NAME, "1:0:0", input, Keys.Empty, Keys.Empty, false, false);
26
26
  }
27
27
 
28
+ /// @notice Run host initialization logic over the full admin request.
29
+ /// @param c Admin command context; `c.request` is passed through as a cursor.
30
+ /// @return Empty output state.
28
31
  function init(
29
32
  CommandContext calldata c
30
33
  ) external onlyAdmin(c.account) returns (bytes memory) {
31
- Cur memory input = cursor(c.request);
32
- init(input);
34
+ init(Cursors.open(c.request));
33
35
  return "";
34
36
  }
35
37
  }
@@ -16,20 +16,23 @@ 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, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, unauthorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false, false);
20
20
  }
21
21
 
22
+ /// @notice Unauthorize each NODE block in the admin request.
23
+ /// @param c Admin command context; `c.request` must contain NODE blocks.
24
+ /// @return Empty output state.
22
25
  function unauthorize(
23
26
  CommandContext calldata c
24
27
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, ) = cursor(c.request, 1);
28
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
26
29
 
27
- while (request.i < request.bound) {
30
+ while (request.i < request.len) {
28
31
  uint node = request.unpackNode();
29
32
  setNode(node, false);
30
33
  }
31
34
 
32
- request.close();
35
+ request.complete();
33
36
  return "";
34
37
  }
35
38
  }
package/core/Host.sol CHANGED
@@ -28,7 +28,16 @@ interface IHostIntroduction {
28
28
  /// guardian management, the default guardian revoke action, and
29
29
  /// optionally introduces itself to a commander host at deployment.
30
30
  /// Accepts native ETH payments via the `receive` function.
31
- abstract contract Host is Authorize, Unauthorize, Revoke, Appoint, Dismiss, ExecutePayable, IntroductionEvent, IHostIntroduction {
31
+ abstract contract Host is
32
+ Authorize,
33
+ Unauthorize,
34
+ Revoke,
35
+ Appoint,
36
+ Dismiss,
37
+ ExecutePayable,
38
+ IntroductionEvent,
39
+ IHostIntroduction
40
+ {
32
41
  /// @param cmdr Commander address; passed to `AccessControl`.
33
42
  /// If `cmdr` is a deployed contract, the host calls `introduce`
34
43
  /// on it during construction.
package/core/Pipeline.sol CHANGED
@@ -10,6 +10,7 @@ using Cursors for Cur;
10
10
  /// @title Pipeline
11
11
  /// @notice Core pipeline functionality shared by higher-level surfaces.
12
12
  abstract contract Pipeline is Payable {
13
+ /// @dev Thrown when the pipeline finishes with non-empty threaded state.
13
14
  error UnexpectedState();
14
15
 
15
16
  /// @notice Override to dispatch one piped step.
@@ -41,15 +42,15 @@ abstract contract Pipeline is Payable {
41
42
  bytes calldata steps,
42
43
  Budget memory budget
43
44
  ) internal {
44
- (Cur memory input, ) = Cursors.init(steps, 1);
45
+ (Cur memory input, , ) = Cursors.init(steps, 0, 1);
45
46
 
46
- while (input.i < input.bound) {
47
+ while (input.i < input.len) {
47
48
  (uint target, uint value, bytes calldata request) = input.unpackStep();
48
49
  state = dispatch(target, account, state, request, useValue(budget, value));
49
50
  }
50
51
 
51
52
  if (state.length != 0) revert UnexpectedState();
52
53
  settleValue(account, budget);
53
- input.close();
54
+ input.complete();
54
55
  }
55
56
  }
package/core/Runtime.sol CHANGED
@@ -1,47 +1,14 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Cur, Cursors} from "../Cursors.sol";
5
4
  import {Assets} from "../utils/Assets.sol";
6
5
  import {Ids} from "../utils/Ids.sol";
7
6
 
8
- using Cursors for Cur;
9
-
10
7
  /// @title Runtime
11
- /// @notice Shared runtime for host identity, native value identity, and block-stream cursors.
8
+ /// @notice Shared runtime for host identity and native asset identity.
12
9
  abstract contract Runtime {
13
10
  /// @dev This contract's host node ID, set to `Ids.toHost(address(this))` at construction.
14
11
  uint public immutable host = Ids.toHost(address(this));
15
- /// @dev Asset ID for the native chain value (ETH), bound to the current chain at deployment.
16
- bytes32 internal immutable valueAsset = Assets.toValue();
17
-
18
- /// @notice Open a cursor over a calldata block stream.
19
- /// @param source Calldata slice to parse.
20
- /// @return cur Cursor positioned at the beginning of `source`.
21
- function cursor(bytes calldata source) internal pure returns (Cur memory cur) {
22
- return Cursors.open(source);
23
- }
24
-
25
- /// @notice Open a cursor and prime it for a grouped iteration pass in one call.
26
- /// Equivalent to `open(source)` followed by `primeRun(group)`.
27
- /// @param source Calldata slice to parse.
28
- /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
29
- /// @return cur Cursor with `bound` set to the end of the first run.
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);
33
- }
34
-
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.
38
- /// @param source Calldata slice to parse.
39
- /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
40
- /// @param expectedGroups Required number of groups in the first run.
41
- /// @return cur Cursor with `bound` set to the end of the first run.
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();
46
- }
12
+ /// @dev Asset ID for the native chain coin/token, bound to the current chain at deployment.
13
+ bytes32 internal immutable nativeAsset = Assets.toNative();
47
14
  }
package/core/Types.sol CHANGED
@@ -21,7 +21,7 @@ struct AccountAsset {
21
21
  bytes32 meta;
22
22
  }
23
23
 
24
- /// @notice Account-scoped amount shape used by payout blocks and query responses.
24
+ /// @notice Account-scoped amount shape for requests, responses, and reporting.
25
25
  struct AccountAmount {
26
26
  /// @dev Account identifier.
27
27
  bytes32 account;
package/docs/Schema.md CHANGED
@@ -185,7 +185,6 @@ Common protocol schemas live in `contracts/blocks/Schema.sol`:
185
185
  #amount { bytes32 asset, bytes32 meta, uint amount }
186
186
  #balance { bytes32 asset, bytes32 meta, uint amount }
187
187
  #custody { uint host, bytes32 asset, bytes32 meta, uint amount }
188
- #payout { bytes32 account, bytes32 asset, bytes32 meta, uint amount }
189
188
  #call { uint target, uint value, #bytes as payload }
190
189
  #step { uint target, uint value, #bytes as request }
191
190
  #context { bytes32 account, #bytes as state, #bytes as request }
package/events/Admin.sol CHANGED
@@ -6,18 +6,20 @@ import { EventEmitter } from "./Emitter.sol";
6
6
  /// @notice Emitted once per admin command during host deployment to publish its request schema and state keys.
7
7
  abstract contract AdminEvent is EventEmitter {
8
8
  string private constant ABI =
9
- "event Admin(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool acceptsValue)";
9
+ "event Admin(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool postcheck, bool funded)";
10
10
 
11
11
  /// @param host Host node ID that owns this admin command.
12
12
  /// @param id Command node ID.
13
13
  /// @param name Human-readable command name.
14
- /// @param shape Per-operation prime block counts encoded as `request:state:output`.
15
- /// Blocks outside the prime runs are global batch blocks and are excluded
16
- /// from the counts.
17
- /// @param request Schema DSL string describing the request shape.
18
- /// @param state Block key expected for input state, or `Keys.Empty`.
14
+ /// @param shape Per-operation block counts encoded as `request:state:output`.
15
+ /// The request count covers only the input request run. If `postcheck` is true,
16
+ /// a constraint run follows the input run, or starts the request when `request`
17
+ /// is empty. State globals may follow the state run and are excluded.
18
+ /// @param request Schema DSL string describing the input request run, or empty if none.
19
+ /// @param state Block key expected for input state, `Keys.Empty`, or `Keys.Any`.
19
20
  /// @param output Block key produced for output state, or `Keys.Empty`.
20
- /// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
21
+ /// @param postcheck Whether command output is validated after execution.
22
+ /// @param funded Whether the command entrypoint accepts nonzero `msg.value`.
21
23
  event Admin(
22
24
  uint indexed host,
23
25
  uint id,
@@ -26,7 +28,8 @@ abstract contract AdminEvent is EventEmitter {
26
28
  string request,
27
29
  bytes4 state,
28
30
  bytes4 output,
29
- bool acceptsValue
31
+ bool postcheck,
32
+ bool funded
30
33
  );
31
34
 
32
35
  constructor() {
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {EventEmitter} from "./Emitter.sol";
5
+
6
+ /// @notice Emitted when a chain/domain node is announced.
7
+ abstract contract ChainEvent is EventEmitter {
8
+ string private constant ABI = "event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin, string name)";
9
+
10
+ /// @param chain Chain node ID.
11
+ /// @param native Native asset ID for the chain.
12
+ /// @param commander Commander host node ID for the chain.
13
+ /// @param admin Admin account for the commander host on the chain.
14
+ /// @param name Chain or domain name.
15
+ event Chain(uint indexed chain, bytes32 native, uint commander, bytes32 admin, string name);
16
+
17
+ constructor() {
18
+ emit EventAbi(ABI);
19
+ }
20
+ }
@@ -6,18 +6,20 @@ import { EventEmitter } from "./Emitter.sol";
6
6
  /// @notice Emitted once per command during host deployment to publish its request schema and state keys.
7
7
  abstract contract CommandEvent is EventEmitter {
8
8
  string private constant ABI =
9
- "event Command(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool acceptsValue)";
9
+ "event Command(uint indexed host, uint id, string name, bytes32 shape, string request, bytes4 state, bytes4 output, bool postcheck, bool funded)";
10
10
 
11
11
  /// @param host Host node ID that owns this command.
12
12
  /// @param id Command node ID.
13
13
  /// @param name Human-readable command name.
14
- /// @param shape Per-operation prime block counts encoded as `request:state:output`.
15
- /// Blocks outside the prime runs are global batch blocks and are excluded
16
- /// from the counts.
17
- /// @param request Schema string describing the request shape.
18
- /// @param state Block key expected for input state, or `Keys.Empty`.
14
+ /// @param shape Per-operation block counts encoded as `request:state:output`.
15
+ /// The request count covers only the input request run. If `postcheck` is true,
16
+ /// a constraint run follows the input run, or starts the request when `request`
17
+ /// is empty. State globals may follow the state run and are excluded.
18
+ /// @param request Schema string describing the input request run, or empty if none.
19
+ /// @param state Block key expected for input state, `Keys.Empty`, or `Keys.Any`.
19
20
  /// @param output Block key produced for output state, or `Keys.Empty`.
20
- /// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
21
+ /// @param postcheck Whether command output is validated after execution.
22
+ /// @param funded Whether the command entrypoint accepts nonzero `msg.value`.
21
23
  event Command(
22
24
  uint indexed host,
23
25
  uint id,
@@ -26,7 +28,8 @@ abstract contract CommandEvent is EventEmitter {
26
28
  string request,
27
29
  bytes4 state,
28
30
  bytes4 output,
29
- bool acceptsValue
31
+ bool postcheck,
32
+ bool funded
30
33
  );
31
34
 
32
35
  constructor() {
package/events/Peer.sol CHANGED
@@ -6,15 +6,15 @@ import {EventEmitter} from "./Emitter.sol";
6
6
  /// @notice Emitted once per peer during host deployment to publish its request and response schemas.
7
7
  abstract contract PeerEvent is EventEmitter {
8
8
  string private constant ABI =
9
- "event Peer(uint indexed host, uint id, string name, bytes32 shape, string request, string response, bool acceptsValue)";
9
+ "event Peer(uint indexed host, uint id, string name, bytes32 shape, string request, string response, bool funded)";
10
10
 
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 shape Prime block counts as `request:response`; global blocks are excluded.
15
- /// @param request Schema DSL string describing the peer request shape.
14
+ /// @param shape Per-operation block counts encoded as `request:response`.
15
+ /// @param request Schema DSL string describing the input request run, or empty if none.
16
16
  /// @param response Schema DSL string describing the peer response shape.
17
- /// @param acceptsValue Whether the peer entrypoint accepts nonzero `msg.value`.
17
+ /// @param funded Whether the peer entrypoint accepts nonzero `msg.value`.
18
18
  event Peer(
19
19
  uint indexed host,
20
20
  uint id,
@@ -22,7 +22,7 @@ abstract contract PeerEvent is EventEmitter {
22
22
  bytes32 shape,
23
23
  string request,
24
24
  string response,
25
- bool acceptsValue
25
+ bool funded
26
26
  );
27
27
 
28
28
  constructor() {
package/events/Query.sol CHANGED
@@ -10,10 +10,8 @@ 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 shape Per-operation prime block counts encoded as `request:response`.
14
- /// Blocks outside the prime runs are global batch blocks and are excluded
15
- /// from the counts.
16
- /// @param request Schema DSL string describing the query request shape.
13
+ /// @param shape Per-operation block counts encoded as `request:response`.
14
+ /// @param request Schema DSL string describing the input request run, or empty if none.
17
15
  /// @param response Schema DSL string describing the query response shape.
18
16
  event Query(uint indexed host, uint id, string name, bytes32 shape, string request, string response);
19
17
 
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { EventEmitter } from "./Emitter.sol";
5
+
6
+ /// @notice Emitted when an asset moves from one account to another.
7
+ abstract contract TransferEvent is EventEmitter {
8
+ string private constant ABI = "event Transfer(bytes32 indexed from, bytes32 to, bytes32 asset, bytes32 meta, uint amount, uint32 action, uint context)";
9
+
10
+ /// @param account Source account identifier.
11
+ /// @param to Destination account identifier.
12
+ /// @param asset Asset identifier.
13
+ /// @param meta Asset metadata slot.
14
+ /// @param amount Amount transferred.
15
+ /// @param action Primary operation hint from `Actions`.
16
+ /// @param context Reserved context value for future use.
17
+ event Transfer(bytes32 indexed account, bytes32 to, bytes32 asset, bytes32 meta, uint amount, uint32 action, uint context);
18
+
19
+ constructor() {
20
+ emit EventAbi(ABI);
21
+ }
22
+ }
package/guards/Revoke.sol CHANGED
@@ -19,13 +19,13 @@ abstract contract Revoke is GuardBase {
19
19
  }
20
20
 
21
21
  function revoke(bytes calldata request) external onlyGuardian {
22
- (Cur memory input, ) = cursor(request, 1);
22
+ (Cur memory input, , ) = Cursors.init(request, 0, 1);
23
23
 
24
- while (input.i < input.bound) {
24
+ while (input.i < input.len) {
25
25
  uint node = input.unpackNode();
26
26
  setNode(node, false);
27
27
  }
28
28
 
29
- input.close();
29
+ input.complete();
30
30
  }
31
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "0.9.8",
3
+ "version": "1.0.0",
4
4
  "description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
5
5
  "private": false,
6
6
  "license": "GPL-3.0-only",
@@ -19,15 +19,17 @@ abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
19
19
  }
20
20
 
21
21
  /// @notice Execute the allow-assets peer call.
22
+ /// @param request ASSET block stream supplied by the trusted peer.
23
+ /// @return Empty response bytes.
22
24
  function peerAllowAssets(bytes calldata request) external onlyPeer returns (bytes memory) {
23
- (Cur memory assets, ) = cursor(request, 1);
25
+ (Cur memory assets, , ) = Cursors.init(request, 0, 1);
24
26
 
25
- while (assets.i < assets.bound) {
27
+ while (assets.i < assets.len) {
26
28
  (bytes32 asset, bytes32 meta) = assets.unpackAsset();
27
29
  allowAsset(asset, meta);
28
30
  }
29
31
 
30
- assets.close();
32
+ assets.complete();
31
33
  return "";
32
34
  }
33
35
  }
@@ -20,16 +20,18 @@ abstract contract PeerAllowance is PeerBase, AllowanceHook {
20
20
  }
21
21
 
22
22
  /// @notice Execute the allowance peer call.
23
+ /// @param request AMOUNT block stream requested by the trusted peer.
24
+ /// @return Empty response bytes.
23
25
  function peerAllowance(bytes calldata request) external onlyPeer returns (bytes memory) {
24
- (Cur memory amounts, ) = cursor(request, 1);
26
+ (Cur memory amounts, , ) = Cursors.init(request, 0, 1);
25
27
  uint peer = caller();
26
28
 
27
- while (amounts.i < amounts.bound) {
29
+ while (amounts.i < amounts.len) {
28
30
  (bytes32 asset, bytes32 meta, uint amount) = amounts.unpackAmount();
29
31
  allowance(peer, asset, meta, amount);
30
32
  }
31
33
 
32
- amounts.close();
34
+ amounts.complete();
33
35
  return "";
34
36
  }
35
37
  }
@@ -28,16 +28,18 @@ abstract contract PeerBalancePull is PeerBase, BalancePullHook {
28
28
  }
29
29
 
30
30
  /// @notice Execute the balance-pull peer call.
31
+ /// @param request BALANCE block stream requested by the trusted peer.
32
+ /// @return Empty response bytes.
31
33
  function peerBalancePull(bytes calldata request) external onlyPeer returns (bytes memory) {
32
- (Cur memory input, ) = cursor(request, 1);
34
+ (Cur memory input, , ) = Cursors.init(request, 0, 1);
33
35
  uint peer = caller();
34
36
 
35
- while (input.i < input.bound) {
37
+ while (input.i < input.len) {
36
38
  (bytes32 asset, bytes32 meta, uint amount) = input.unpackBalance();
37
39
  balancePull(peer, asset, meta, amount);
38
40
  }
39
41
 
40
- input.close();
42
+ input.complete();
41
43
  return "";
42
44
  }
43
45
  }
@@ -19,15 +19,17 @@ abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook {
19
19
  }
20
20
 
21
21
  /// @notice Execute the deny-assets peer call.
22
+ /// @param request ASSET block stream supplied by the trusted peer.
23
+ /// @return Empty response bytes.
22
24
  function peerDenyAssets(bytes calldata request) external onlyPeer returns (bytes memory) {
23
- (Cur memory assets, ) = cursor(request, 1);
25
+ (Cur memory assets, , ) = Cursors.init(request, 0, 1);
24
26
 
25
- while (assets.i < assets.bound) {
27
+ while (assets.i < assets.len) {
26
28
  (bytes32 asset, bytes32 meta) = assets.unpackAsset();
27
29
  denyAsset(asset, meta);
28
30
  }
29
31
 
30
- assets.close();
32
+ assets.complete();
31
33
  return "";
32
34
  }
33
35
  }
@@ -0,0 +1,51 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { PeerBase } from "./Base.sol";
5
+ import { Payable } from "../core/Payable.sol";
6
+ import { Cursors, Cur, Schemas } from "../Cursors.sol";
7
+ import { Budget } from "../utils/Value.sol";
8
+
9
+ using Cursors for Cur;
10
+
11
+ abstract contract PeerDispatchPayableHook {
12
+ /// @notice Override to dispatch an already encoded payload to `chain`.
13
+ /// @param chain Destination chain node ID.
14
+ /// @param endowment Native value requested for the destination dispatch. The
15
+ /// hook decides how much source-chain budget must be spent to fund this
16
+ /// value on the destination chain.
17
+ /// @param payload Encoded payload ready for the transport layer.
18
+ /// @param budget Source-chain native-value budget available for transport
19
+ /// fees and destination endowment funding.
20
+ function dispatch(uint chain, uint endowment, bytes calldata payload, Budget memory budget) internal virtual;
21
+ }
22
+
23
+ /// @title PeerDispatchPayable
24
+ /// @notice Peer endpoint that forwards DISPATCH blocks to a host-defined dispatch hook.
25
+ abstract contract PeerDispatchPayable is PeerBase, Payable, PeerDispatchPayableHook {
26
+ string private constant NAME = "peerDispatchPayable";
27
+ uint internal immutable peerDispatchPayableId = peerId(NAME);
28
+
29
+ constructor() {
30
+ emit Peer(host, peerDispatchPayableId, NAME, "1:0", Schemas.Dispatch, "", true);
31
+ }
32
+
33
+ /// @notice Forward peer-supplied dispatches to the host-defined dispatch hook.
34
+ /// @dev Dispatch hooks receive the shared top-level source-chain value
35
+ /// budget. Any `msg.value` not spent by the hook remains on this host.
36
+ /// @param request DISPATCH block stream supplied by the trusted peer.
37
+ /// @return output Empty response bytes.
38
+ function peerDispatchPayable(bytes calldata request) external payable onlyPeer returns (bytes memory output) {
39
+ (Cur memory input, , ) = Cursors.init(request, 0, 1);
40
+ Budget memory budget = valueBudget();
41
+
42
+ while (input.i < input.len) {
43
+ (uint chain, uint endowment, bytes calldata payload) = input.unpackDispatch();
44
+ dispatch(chain, endowment, payload, budget);
45
+ }
46
+
47
+ input.complete();
48
+ return "";
49
+ }
50
+
51
+ }
package/peer/Pipe.sol CHANGED
@@ -9,9 +9,9 @@ import {Budget} from "../utils/Value.sol";
9
9
  using Cursors for Cur;
10
10
 
11
11
  /// @title PeerPipePayable
12
- /// @notice Peer that consumes PIPE blocks and executes each context request as a STEP stream.
12
+ /// @notice Peer that consumes PIPE blocks and executes each context step stream.
13
13
  /// Each PIPE block carries a value budget plus a CONTEXT block; the nested
14
- /// context request is passed to the shared pipeline as the step stream.
14
+ /// context steps are passed to the shared pipeline as the step stream.
15
15
  abstract contract PeerPipePayable is PeerBase, Pipeline {
16
16
  string private constant NAME = "peerPipePayable";
17
17
  uint internal immutable peerPipePayableId = peerId(NAME);
@@ -23,16 +23,18 @@ abstract contract PeerPipePayable is PeerBase, Pipeline {
23
23
  /// @notice Execute peer-supplied pipes through the shared payable pipe.
24
24
  /// @dev Each pipe receives its own explicit value sub-budget. Any top-level
25
25
  /// `msg.value` not assigned to a pipe remains on this host.
26
+ /// @param request PIPE block stream supplied by the trusted peer.
27
+ /// @return Empty response bytes.
26
28
  function peerPipePayable(bytes calldata request) external payable onlyPeer returns (bytes memory) {
27
- (Cur memory input, ) = cursor(request, 1);
29
+ (Cur memory input, , ) = Cursors.init(request, 0, 1);
28
30
  Budget memory budget = valueBudget();
29
31
 
30
- while (input.i < input.bound) {
32
+ while (input.i < input.len) {
31
33
  (uint value, bytes32 account, bytes calldata state, bytes calldata steps) = input.unpackPipe();
32
34
  pipe(account, state, steps, allocateValue(budget, value));
33
35
  }
34
36
 
35
- input.close();
37
+ input.complete();
36
38
  return "";
37
39
  }
38
40
  }