@rootzero/contracts 0.9.3 → 0.9.5

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 (49) hide show
  1. package/Commands.sol +3 -2
  2. package/Core.sol +4 -3
  3. package/Cursors.sol +1 -1
  4. package/Events.sol +2 -3
  5. package/README.md +18 -24
  6. package/blocks/Cursors.sol +316 -337
  7. package/blocks/Keys.sol +40 -57
  8. package/blocks/Schema.sol +57 -208
  9. package/blocks/Writers.sol +376 -135
  10. package/commands/Base.sol +6 -48
  11. package/commands/Burn.sol +2 -2
  12. package/commands/Credit.sol +3 -3
  13. package/commands/Debit.sol +3 -2
  14. package/commands/Deposit.sol +11 -8
  15. package/commands/Provision.sol +11 -8
  16. package/commands/Transfer.sol +2 -2
  17. package/commands/Withdraw.sol +3 -3
  18. package/commands/admin/AllowAssets.sol +1 -1
  19. package/commands/admin/Allowance.sol +1 -1
  20. package/commands/admin/Authorize.sol +1 -1
  21. package/commands/admin/DenyAssets.sol +1 -1
  22. package/commands/admin/Execute.sol +7 -6
  23. package/commands/admin/Unauthorize.sol +1 -1
  24. package/core/Access.sol +11 -0
  25. package/core/Calls.sol +5 -5
  26. package/core/Context.sol +3 -4
  27. package/core/Host.sol +25 -20
  28. package/core/Payable.sol +57 -0
  29. package/core/Pipeline.sol +55 -0
  30. package/docs/Schema.md +196 -0
  31. package/events/Command.sol +1 -2
  32. package/events/Introduction.sol +22 -0
  33. package/events/{Piped.sol → Rooted.sol} +3 -3
  34. package/package.json +2 -2
  35. package/peer/AllowAssets.sol +1 -1
  36. package/peer/Allowance.sol +1 -1
  37. package/peer/BalancePull.sol +1 -1
  38. package/peer/DenyAssets.sol +1 -1
  39. package/peer/Pipe.sol +38 -0
  40. package/peer/Settle.sol +1 -1
  41. package/queries/Assets.sol +4 -3
  42. package/queries/Balances.sol +2 -1
  43. package/queries/Positions.sol +12 -12
  44. package/utils/Value.sol +8 -14
  45. package/commands/Pipe.sol +0 -67
  46. package/docs/GETTING_STARTED.md +0 -294
  47. package/events/Governed.sol +0 -21
  48. package/events/Host.sol +0 -22
  49. package/interfaces/IHostDiscovery.sol +0 -16
package/docs/Schema.md ADDED
@@ -0,0 +1,196 @@
1
+ # Schema
2
+
3
+ Rootzero request and response data is encoded as a stream of typed blocks. A
4
+ schema string describes payload layout for discovery events and tooling; the
5
+ runtime block key is derived only from the block name.
6
+
7
+ ## Wire Format
8
+
9
+ Every block uses the same header:
10
+
11
+ ```txt
12
+ [bytes4 key][uint32 payloadLen][payload]
13
+ ```
14
+
15
+ `payloadLen` is big-endian and counts only payload bytes. Child blocks and list
16
+ items use the same header format.
17
+
18
+ The block key is:
19
+
20
+ ```txt
21
+ bytes4(keccak256("#name"))
22
+ ```
23
+
24
+ For example, `#amount { bytes32 asset, bytes32 meta, uint amount }` uses the key
25
+ derived from `#amount`. Blocks must not be overloaded: one block name should have
26
+ one protocol meaning.
27
+
28
+ ## Block Syntax
29
+
30
+ A block starts with `#`. Fixed fields are written in braces:
31
+
32
+ ```txt
33
+ #amount { bytes32 asset, bytes32 meta, uint amount }
34
+ #account { bytes32 account }
35
+ ```
36
+
37
+ A block without braces has no payload:
38
+
39
+ ```txt
40
+ #unit
41
+ #bytes
42
+ ```
43
+
44
+ Empty braces are invalid. A zero-payload block must omit braces.
45
+
46
+ A schema is a comma-separated list of items. Order is significant.
47
+
48
+ ```txt
49
+ #amount { bytes32 asset, bytes32 meta, uint amount },
50
+ maybe #account { bytes32 account }
51
+ ```
52
+
53
+ ## Payload Layout
54
+
55
+ A block payload has fixed fields first, followed by an optional child-block tail.
56
+ Once a child block appears, no more fixed fields may follow.
57
+
58
+ ```txt
59
+ #call { uint target, uint value, #bytes as payload }
60
+ #context { bytes32 account, #bytes as state, #bytes as request }
61
+ #relay { uint target, uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
62
+ ```
63
+
64
+ The tail is embedded directly as child block bytes. There is no wrapper around a
65
+ child-block tail.
66
+
67
+ Raw dynamic bytes are represented with the reserved `#bytes` child block. Use an
68
+ alias to give those bytes a presentation name:
69
+
70
+ ```txt
71
+ #bytes as payload
72
+ ```
73
+
74
+ ## Modifiers
75
+
76
+ Cardinality is expressed with prefix keywords:
77
+
78
+ ```txt
79
+ #balance { bytes32 asset, bytes32 meta, uint amount }
80
+ maybe #balance { bytes32 asset, bytes32 meta, uint amount }
81
+ many #balance { bytes32 asset, bytes32 meta, uint amount }
82
+ maybe many #balance { bytes32 asset, bytes32 meta, uint amount }
83
+ ```
84
+
85
+ - no prefix: one required item
86
+ - `maybe`: optional item
87
+ - `many`: one `#list` block whose payload contains repeated items
88
+ - `maybe many`: optional `#list` block
89
+
90
+ `maybe` emits no placeholder when absent. `many` wraps repeated items in one
91
+ generic list block; it does not repeat the item in place.
92
+
93
+ ## Prime Items
94
+
95
+ The empty string `""` means no schema. Whitespace-only schemas are invalid.
96
+
97
+ For a non-empty schema, the first top-level item is the prime item. Prime items
98
+ may repeat at the top level for batching. Later top-level items are globals for
99
+ the whole batch and are not counted as per-operation prime blocks.
100
+
101
+ The prime item cannot be optional. If a command needs a per-operation marker with
102
+ no payload, use a zero-payload block such as `#unit`.
103
+
104
+ ## Aliases
105
+
106
+ Aliases are presentation metadata for tooling. They do not change payload layout
107
+ or runtime keys.
108
+
109
+ ```txt
110
+ maybe #account { bytes32 account } as recipient
111
+ #call { uint target, uint value, #bytes as payload }
112
+ ```
113
+
114
+ Aliases may be used on any block item, including child blocks and prime items.
115
+
116
+ ## Field Types
117
+
118
+ Supported field types are chain-neutral:
119
+
120
+ ```txt
121
+ uint, uint8, uint16, uint32, uint64, uint128, uint256
122
+ int, int8, int16, int32, int64, int128, int256
123
+ bool
124
+ bytes1 through bytes32
125
+ ```
126
+
127
+ `uint` means `uint256`; `int` means `int256`. Other integer widths, unsized
128
+ `bytes`, `string`, and array syntax are not part of the core schema DSL.
129
+
130
+ Integers are encoded big-endian. Signed integers use two's-complement encoding
131
+ for their declared width. `bool` is one byte: `0x00` for false and `0x01` for
132
+ true. `bytesN` values are encoded as exactly `N` bytes with no padding.
133
+
134
+ ## Identifiers
135
+
136
+ Block names, field names, and aliases use lower camelCase ASCII identifiers:
137
+
138
+ ```txt
139
+ [a-z][a-zA-Z0-9]*
140
+ ```
141
+
142
+ Invalid examples:
143
+
144
+ ```txt
145
+ Amount
146
+ asset_meta
147
+ asset-meta
148
+ 0account
149
+ ```
150
+
151
+ Reserved words include `maybe`, `many`, `as`, all field type names, and the
152
+ reserved block names `bytes`, `data`, and `list`.
153
+
154
+ ## Reserved Blocks
155
+
156
+ - `#bytes`: raw dynamic bytes, written without a body
157
+ - `#data`: generic/custom payload block
158
+ - `#list`: generic list wrapper emitted by `many`
159
+
160
+ Use `#data` when a local schema needs a stable generic key:
161
+
162
+ ```txt
163
+ #data { uint foo, bytes32 tag }
164
+ #data { #bytes as payload }
165
+ ```
166
+
167
+ If a schema string starts with a fixed field type, it is shorthand for one
168
+ top-level `#data` block:
169
+
170
+ ```txt
171
+ uint foo, bytes32 tag
172
+ ```
173
+
174
+ expands to:
175
+
176
+ ```txt
177
+ #data { uint foo, bytes32 tag }
178
+ ```
179
+
180
+ ## Standard Blocks
181
+
182
+ Common protocol schemas live in `contracts/blocks/Schema.sol`:
183
+
184
+ ```txt
185
+ #amount { bytes32 asset, bytes32 meta, uint amount }
186
+ #balance { bytes32 asset, bytes32 meta, uint amount }
187
+ #custody { uint host, bytes32 asset, bytes32 meta, uint amount }
188
+ #payout { bytes32 account, bytes32 asset, bytes32 meta, uint amount }
189
+ #call { uint target, uint value, #bytes as payload }
190
+ #step { uint target, uint value, #bytes as request }
191
+ #context { bytes32 account, #bytes as state, #bytes as request }
192
+ #relay { uint target, uint value, #context { bytes32 account, #bytes as state, #bytes as request } }
193
+ #auth { uint cid, uint deadline, #bytes as proof }
194
+ ```
195
+
196
+ `Keys.sol` contains the corresponding runtime keys.
@@ -14,8 +14,7 @@ abstract contract CommandEvent is EventEmitter {
14
14
  /// @param shape Per-operation prime block counts encoded as `request:state:output`.
15
15
  /// Blocks outside the prime runs are global batch blocks and are excluded
16
16
  /// from the counts.
17
- /// @param request Schema DSL string describing the request shape; use `empty;...`
18
- /// when the request has global blocks but no prime blocks.
17
+ /// @param request Schema string describing the request shape.
19
18
  /// @param state Block key expected for input state, or `Keys.Empty`.
20
19
  /// @param output Block key produced for output state, or `Keys.Empty`.
21
20
  /// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
@@ -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
+ string constant ABI = "event Introduction(uint indexed host, uint blocknum, uint16 version, string namespace)";
7
+
8
+ /// @notice Emitted when a host introduces itself to another host.
9
+ abstract contract IntroductionEvent is EventEmitter {
10
+ /// @param host Host node ID of the introducing contract.
11
+ /// @param blocknum Block number at which the host was deployed.
12
+ /// @param version Protocol version the host implements.
13
+ /// @param namespace Human-readable namespace string for the host.
14
+ event Introduction(uint indexed host, uint blocknum, uint16 version, string namespace);
15
+
16
+ constructor() {
17
+ emit EventAbi(ABI);
18
+ }
19
+ }
20
+
21
+
22
+
@@ -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 Piped(bytes32 indexed account, uint deadline, uint value)";
6
+ string constant ABI = "event Rooted(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 PipedEvent is EventEmitter {
9
+ abstract contract RootedEvent 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 Piped(bytes32 indexed account, uint deadline, uint value);
13
+ event Rooted(bytes32 indexed account, uint deadline, uint value);
14
14
 
15
15
  constructor() {
16
16
  emit EventAbi(ABI);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
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",
@@ -9,7 +9,7 @@
9
9
  "**/*.sol",
10
10
  "README.md",
11
11
  "LICENSE",
12
- "docs/GETTING_STARTED.md"
12
+ "docs/Schema.md"
13
13
  ],
14
14
  "publishConfig": {
15
15
  "access": "public"
@@ -27,7 +27,7 @@ abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
27
27
  allowAsset(asset, meta);
28
28
  }
29
29
 
30
- assets.complete();
30
+ assets.close();
31
31
  return "";
32
32
  }
33
33
  }
@@ -29,7 +29,7 @@ abstract contract PeerAllowance is PeerBase, AllowanceHook {
29
29
  allowance(peer, asset, meta, amount);
30
30
  }
31
31
 
32
- amounts.complete();
32
+ amounts.close();
33
33
  return "";
34
34
  }
35
35
  }
@@ -37,7 +37,7 @@ abstract contract PeerBalancePull is PeerBase, BalancePullHook {
37
37
  balancePull(peer, asset, meta, amount);
38
38
  }
39
39
 
40
- input.complete();
40
+ input.close();
41
41
  return "";
42
42
  }
43
43
  }
@@ -27,7 +27,7 @@ abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook {
27
27
  denyAsset(asset, meta);
28
28
  }
29
29
 
30
- assets.complete();
30
+ assets.close();
31
31
  return "";
32
32
  }
33
33
  }
package/peer/Pipe.sol ADDED
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {PeerBase} from "./Base.sol";
5
+ import {Pipeline} from "../core/Pipeline.sol";
6
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
+ import {Budget} from "../utils/Value.sol";
8
+
9
+ using Cursors for Cur;
10
+
11
+ /// @title PeerPipePayable
12
+ /// @notice Peer that consumes RELAY blocks and executes each context request as a STEP stream.
13
+ /// Each RELAY block carries a value budget plus a CONTEXT block; the relay target is ignored
14
+ /// and the nested context request is passed to the shared pipeline as the step stream.
15
+ abstract contract PeerPipePayable is PeerBase, Pipeline {
16
+ string private constant NAME = "peerPipePayable";
17
+ uint internal immutable peerPipePayableId = peerId(NAME);
18
+
19
+ constructor() {
20
+ emit Peer(host, peerPipePayableId, NAME, "1:0", Schemas.Relay, "", true);
21
+ }
22
+
23
+ /// @notice Execute peer-supplied relays through the shared payable pipe.
24
+ /// @dev Each relay receives its own explicit value sub-budget. Any top-level
25
+ /// `msg.value` not assigned to a relay remains on this host.
26
+ function peerPipePayable(bytes calldata request) external payable onlyPeer returns (bytes memory) {
27
+ (Cur memory input, ) = cursor(request, 1);
28
+ Budget memory budget = valueBudget();
29
+
30
+ while (input.i < input.bound) {
31
+ (, uint value, bytes32 account, bytes calldata state, bytes calldata steps) = input.unpackRelay();
32
+ pipe(account, state, steps, allocateValue(budget, value));
33
+ }
34
+
35
+ input.close();
36
+ return "";
37
+ }
38
+ }
package/peer/Settle.sol CHANGED
@@ -26,7 +26,7 @@ abstract contract PeerSettle is PeerBase, TransferHook {
26
26
  transfer(state.unpackTxValue());
27
27
  }
28
28
 
29
- state.complete();
29
+ state.close();
30
30
  return "";
31
31
  }
32
32
  }
@@ -30,8 +30,8 @@ abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
30
30
  }
31
31
 
32
32
  /// @notice Resolve allowlist status for a run of requested `(asset, meta)` tuples.
33
- /// @param request Block-stream request consisting of `asset(asset, meta)*`.
34
- /// @return Block-stream response containing one `status(ok)` per asset block.
33
+ /// @param request Block-stream request consisting of `#asset { bytes32 asset, bytes32 meta }` blocks.
34
+ /// @return Block-stream response containing one `#status { bool ok }` per asset block.
35
35
  function isAllowedAsset(bytes calldata request) external view returns (bytes memory) {
36
36
  (Cur memory query, uint groups) = cursor(request, 1);
37
37
  Writer memory response = Writers.allocStatuses(groups);
@@ -42,6 +42,7 @@ abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
42
42
  response.appendStatus(allowed);
43
43
  }
44
44
 
45
- return query.complete(response);
45
+ query.close();
46
+ return response.finish();
46
47
  }
47
48
  }
@@ -42,6 +42,7 @@ abstract contract GetBalances is QueryBase, GetBalancesHook {
42
42
  response.appendAccountAmount(account, asset, meta, amount);
43
43
  }
44
44
 
45
- return query.complete(response);
45
+ query.close();
46
+ return response.finish();
46
47
  }
47
48
  }
@@ -6,11 +6,12 @@ import {Forms} from "../blocks/Schema.sol";
6
6
  import {QueryBase} from "./Base.sol";
7
7
 
8
8
  using Cursors for Cur;
9
+ using Writers for Writer;
9
10
 
10
11
  abstract contract GetPositionHook {
11
- /// @notice Resolve the position payload for one requested position.
12
- /// Concrete implementations must append exactly one `RESPONSE` block whose payload
13
- /// length matches `positionResponseSize`.
12
+ /// @notice Append the position response for one requested position.
13
+ /// Concrete implementations must append exactly one response block matching
14
+ /// the query output schema.
14
15
  /// @param account Requested account identifier.
15
16
  /// @param asset Requested asset identifier.
16
17
  /// @param meta Requested asset metadata slot.
@@ -26,31 +27,30 @@ abstract contract GetPositionHook {
26
27
  /// @title GetPosition
27
28
  /// @notice Rootzero query that resolves one dynamic position response for each requested position.
28
29
  /// The request is a run of `ACCOUNT_ASSET` form blocks.
29
- /// The response returns one dynamic `RESPONSE` block per position entry, preserving request order.
30
+ /// The response returns one output-schema block per position entry, preserving request order.
30
31
  abstract contract GetPosition is QueryBase, GetPositionHook {
31
32
  string private constant NAME = "getPosition";
33
+
32
34
  uint public immutable getPositionId = queryId(NAME);
33
- uint internal immutable positionResponseSize;
34
35
 
35
- constructor(string memory output, uint responseSize) {
36
- positionResponseSize = responseSize;
36
+ constructor(string memory output) {
37
37
  emit Query(host, getPositionId, NAME, "1:1", Forms.AccountAsset, output);
38
38
  }
39
39
 
40
40
  /// @notice Resolve positions for a run of requested `(account, asset, meta)` tuples.
41
- /// @dev Allocates from the configured fixed response payload length so each hook call
42
- /// can append one `RESPONSE` block directly into the output stream.
41
+ /// @dev Allocates from a per-block capacity hint and grows when position outputs exceed it.
43
42
  /// @param request Block-stream request consisting of `accountAsset(account, asset, meta)*`.
44
- /// @return Block-stream response containing one `response(bytes data)` block per position block.
43
+ /// @return Block-stream response containing one output-schema block per position block.
45
44
  function getPosition(bytes calldata request) external view returns (bytes memory) {
46
45
  (Cur memory query, uint groups) = cursor(request, 1);
47
- Writer memory response = Writers.allocBytes(groups, positionResponseSize);
46
+ Writer memory response = Writers.allocAny(groups);
48
47
 
49
48
  while (query.i < query.bound) {
50
49
  (bytes32 account, bytes32 asset, bytes32 meta) = query.unpackAccountAsset();
51
50
  appendPosition(account, asset, meta, response);
52
51
  }
53
52
 
54
- return query.complete(response);
53
+ query.close();
54
+ return response.finish();
55
55
  }
56
56
  }
package/utils/Value.sol CHANGED
@@ -8,17 +8,11 @@ struct Budget {
8
8
  }
9
9
 
10
10
  /// @title Values
11
- /// @notice Native-value (ETH) budget tracking for commands that accept `msg.value`.
11
+ /// @notice Native-value budget mutation helpers.
12
12
  library Values {
13
13
  /// @dev Thrown when a call attempts to spend more native value than remains in the budget.
14
14
  error InsufficientValue();
15
15
 
16
- /// @notice Create a budget from the current call's `msg.value`.
17
- /// @return Budget initialised with the full `msg.value`.
18
- function fromMsg() internal view returns (Budget memory) {
19
- return Budget({remaining: msg.value});
20
- }
21
-
22
16
  /// @notice Deduct `amount` from the budget and return it.
23
17
  /// Reverts if `amount` exceeds `budget.remaining`.
24
18
  /// @param budget Mutable budget to deduct from.
@@ -30,12 +24,12 @@ library Values {
30
24
  return amount;
31
25
  }
32
26
 
33
- /// @notice Deduct all remaining native value from the budget and return it.
34
- /// @param budget Mutable budget to drain.
35
- /// @return Remaining native value before the budget was emptied.
36
- function drain(Budget memory budget) internal pure returns (uint) {
37
- uint amount = budget.remaining;
38
- budget.remaining = 0;
39
- return amount;
27
+ /// @notice Deduct `amount` from the budget and return it as a new sub-budget.
28
+ /// Reverts if `amount` exceeds `budget.remaining`.
29
+ /// @param budget Mutable parent budget to deduct from.
30
+ /// @param amount Native value to assign to the sub-budget, in wei.
31
+ /// @return A new budget with `amount` remaining.
32
+ function allocate(Budget memory budget, uint amount) internal pure returns (Budget memory) {
33
+ return Budget({remaining: use(budget, amount)});
40
34
  }
41
35
  }
package/commands/Pipe.sol DELETED
@@ -1,67 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {CommandBase, CommandContext, CommandPayable, Keys} from "./Base.sol";
5
- import {Cursors, Cur, Schemas} from "../Cursors.sol";
6
- import {Accounts} from "../utils/Accounts.sol";
7
- import {Budget, Values} from "../utils/Value.sol";
8
-
9
- using Cursors for Cur;
10
-
11
- abstract contract PipePayableHook {
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,
23
- bytes32 account,
24
- bytes memory state,
25
- bytes calldata request,
26
- uint value
27
- ) internal virtual returns (bytes memory);
28
- }
29
-
30
- /// @title PipePayable
31
- /// @notice Command that sequences multiple sub-command STEP invocations in a single transaction.
32
- /// Each STEP block carries a command node, native value to forward, and an embedded request.
33
- /// State threads through the steps: each step's output becomes the next step's state.
34
- /// Admin accounts are not permitted to use `pipePayable`.
35
- abstract contract PipePayable is CommandPayable, PipePayableHook {
36
- string private constant NAME = "pipePayable";
37
-
38
- uint internal immutable pipePayableId = commandId(NAME);
39
-
40
- constructor() {
41
- emit Command(host, pipePayableId, NAME, "1:0:0", Schemas.Step, Keys.Empty, Keys.Empty, true);
42
- }
43
-
44
- function pipe(
45
- bytes32 account,
46
- bytes memory state,
47
- bytes calldata steps,
48
- Budget memory budget
49
- ) internal returns (bytes memory) {
50
- (Cur memory input, ) = cursor(steps, 1);
51
-
52
- while (input.i < input.bound) {
53
- (uint target, uint value, bytes calldata request) = input.unpackStep();
54
- uint spend = Values.use(budget, value);
55
- state = dispatchCommand(target, account, state, request, spend);
56
- }
57
-
58
- settleValue(account, budget);
59
- input.complete();
60
- return state;
61
- }
62
-
63
- /// @notice Execute the pipePayable command.
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());
66
- }
67
- }