@rootzero/contracts 1.15.0 → 1.17.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 (53) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -1
  4. package/Core.sol +4 -4
  5. package/Endpoints.sol +9 -7
  6. package/Events.sol +0 -1
  7. package/README.md +88 -30
  8. package/Utils.sol +1 -1
  9. package/codec/Blocks.sol +111 -21
  10. package/codec/Buffers.sol +3 -3
  11. package/codec/Decoders.sol +48 -10
  12. package/codec/Descriptors.sol +0 -1
  13. package/codec/Keys.sol +4 -2
  14. package/codec/Readers.sol +25 -0
  15. package/codec/Schema.sol +6 -1
  16. package/codec/Specs.sol +7 -2
  17. package/codec/Writers.sol +28 -2
  18. package/commands/Base.sol +8 -15
  19. package/commands/Burn.sol +2 -2
  20. package/commands/Credit.sol +38 -3
  21. package/commands/Debit.sol +50 -14
  22. package/commands/Deposit.sol +4 -4
  23. package/commands/Provision.sol +4 -4
  24. package/commands/Recover.sol +15 -8
  25. package/commands/Relay.sol +37 -12
  26. package/commands/Settle.sol +130 -0
  27. package/commands/Withdraw.sol +2 -2
  28. package/commands/admin/AllowAssets.sol +2 -2
  29. package/commands/admin/Allowance.sol +3 -3
  30. package/commands/admin/Annotate.sol +2 -2
  31. package/commands/admin/Appoint.sol +2 -2
  32. package/commands/admin/Authorize.sol +2 -2
  33. package/commands/admin/DenyAssets.sol +2 -2
  34. package/commands/admin/Dismiss.sol +2 -2
  35. package/commands/admin/Execute.sol +3 -3
  36. package/commands/admin/Unauthorize.sol +2 -2
  37. package/core/Endpoint.sol +15 -13
  38. package/core/Pipeline.sol +6 -6
  39. package/core/Settlement.sol +37 -8
  40. package/core/Types.sol +21 -1
  41. package/docs/Schema.md +52 -2
  42. package/execution/Budget.sol +12 -4
  43. package/execution/Execution.sol +119 -11
  44. package/guards/Base.sol +2 -2
  45. package/guards/Revoke.sol +21 -0
  46. package/package.json +1 -1
  47. package/ports/Base.sol +2 -2
  48. package/ports/Dispatch.sol +6 -6
  49. package/ports/{Settle.sol → Post.sol} +10 -10
  50. package/queries/Base.sol +2 -2
  51. package/utils/Actions.sol +1 -0
  52. package/utils/Cursors.sol +16 -30
  53. package/events/Position.sol +0 -22
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, Tx} from "../core/Types.sol";
4
+ import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Position, Tx} from "../core/Types.sol";
5
5
  import {Blocks} from "./Blocks.sol";
6
6
  import {Sizes, Specs} from "./Specs.sol";
7
7
  import {Cursors, Cur} from "../utils/Cursors.sol";
@@ -32,20 +32,17 @@ library Decoders {
32
32
  cur.state = Cursors.wrap(source[i:], 0, 0);
33
33
  }
34
34
 
35
- /// @notice Open the first homogeneous run in `source` using `stride`.
35
+ /// @notice Open the first homogeneous run in `source`.
36
36
  /// @param source Calldata block stream to open.
37
- /// @param stride Number of blocks per group, or zero for an ungrouped stream.
38
37
  /// @return cur Cursor spanning the first homogeneous run.
39
- function open(
40
- bytes calldata source,
41
- uint stride
42
- ) internal pure returns (Cur memory cur) {
38
+ function open(bytes calldata source) internal pure returns (Cur memory cur) {
43
39
  (uint abs, uint limit) = Cursors.bounds(source);
44
- if (stride == 0 && abs == limit) return cur;
40
+ if (abs == limit) revert Blocks.EmptyRun();
45
41
 
46
42
  bytes4 key = bytes4(source);
47
- (uint groups, uint end) = Blocks.scope(abs, limit, key, stride);
48
- cur.state = Cursors.create(abs, end - abs, groups, 0, 0);
43
+ (uint count, uint end) = Blocks.run(abs, limit, key);
44
+ if (count == 0) revert Blocks.EmptyRun();
45
+ cur.state = Cursors.create(abs, end - abs, count, 0, 0);
49
46
  }
50
47
 
51
48
  /// @notice Return whether `cur` has unread bytes.
@@ -163,6 +160,16 @@ library Decoders {
163
160
  return Blocks.find(offset + i, offset + end, key) - offset;
164
161
  }
165
162
 
163
+ /// @notice Count consecutive blocks with `key` from the current cursor position.
164
+ /// @dev Does not advance the cursor.
165
+ /// @param cur Cursor to inspect.
166
+ /// @param key Block key forming the run.
167
+ /// @return count Number of consecutive matching blocks.
168
+ function run(Cur memory cur, bytes4 key) internal pure returns (uint count) {
169
+ (uint i, uint offset, uint len) = cur.state.decode();
170
+ (count, ) = Blocks.run(offset + i, offset + len, key);
171
+ }
172
+
166
173
  /// @notice Consume one LIST block and return a cursor over its items.
167
174
  /// @param cur Cursor advanced past the list.
168
175
  /// @return items Cursor spanning the list payload.
@@ -269,6 +276,16 @@ library Decoders {
269
276
  (account, asset) = Blocks.unpackAccountAsset(abs);
270
277
  }
271
278
 
279
+ /// @notice Decode and consume one HOST_ASSET block.
280
+ /// @param cur Cursor advanced past the block.
281
+ /// @return host Decoded host identifier.
282
+ /// @return asset Decoded asset identifier.
283
+ function unpackHostAsset(Cur memory cur) internal pure returns (uint host, bytes32 asset) {
284
+ uint abs;
285
+ (cur.state, abs) = cur.state.consume(Sizes.HostAsset);
286
+ (host, asset) = Blocks.unpackHostAsset(abs);
287
+ }
288
+
272
289
  /// @notice Decode and consume one AMOUNT block.
273
290
  /// @param cur Cursor advanced past the block.
274
291
  /// @return asset Decoded asset identifier.
@@ -455,6 +472,13 @@ library Decoders {
455
472
  (value.account, value.asset) = unpackAccountAsset(cur);
456
473
  }
457
474
 
475
+ /// @notice Decode one HOST_ASSET block into its structured value.
476
+ /// @param cur Cursor advanced past the block.
477
+ /// @return value Structured host and asset.
478
+ function unpackHostAssetValue(Cur memory cur) internal pure returns (HostAsset memory value) {
479
+ (value.host, value.asset) = unpackHostAsset(cur);
480
+ }
481
+
458
482
  /// @notice Decode one AMOUNT block into its structured value.
459
483
  /// @param cur Cursor advanced past the block.
460
484
  /// @return value Structured asset amount.
@@ -532,6 +556,20 @@ library Decoders {
532
556
  (value.host, value.asset, value.amount) = unpackCustody(cur);
533
557
  }
534
558
 
559
+ /// @notice Decode and consume one POSITION block.
560
+ function unpackPosition(
561
+ Cur memory cur
562
+ ) internal pure returns (bytes32 asset, uint amount, bytes32 liability, uint debt) {
563
+ uint abs;
564
+ (cur.state, abs) = cur.state.consume(Sizes.Position);
565
+ (asset, amount, liability, debt) = Blocks.unpackPosition(abs);
566
+ }
567
+
568
+ /// @notice Decode one POSITION block into its structured value.
569
+ function unpackPositionValue(Cur memory cur) internal pure returns (Position memory value) {
570
+ (value.asset, value.amount, value.liability, value.debt) = unpackPosition(cur);
571
+ }
572
+
535
573
  /// @notice Decode one TRANSACTION block into its structured value.
536
574
  /// @param cur Cursor advanced past the block.
537
575
  /// @return value Structured transaction.
@@ -37,7 +37,6 @@ library Descriptors {
37
37
  state = Specs.normalize(state, true);
38
38
  input = Specs.normalize(input, false);
39
39
  output = Specs.normalize(output, true);
40
-
41
40
  descriptor = pack(state, input, output, transactions, flags);
42
41
  }
43
42
 
package/codec/Keys.sol CHANGED
@@ -9,8 +9,6 @@ pragma solidity ^0.8.33;
9
9
  library Keys {
10
10
  /// @dev Empty / unset key.
11
11
  bytes4 constant Empty = bytes4(0);
12
- /// @dev Wildcard key used in discovery when any block stream is accepted.
13
- bytes4 constant Any = 0xffffffff;
14
12
  /// @dev Input amount - (bytes32 asset, uint amount)
15
13
  bytes4 constant Amount = bytes4(keccak256("#amount"));
16
14
  /// @dev Ledger balance - (bytes32 asset, uint amount)
@@ -21,6 +19,8 @@ library Keys {
21
19
  bytes4 constant Allowance = bytes4(keccak256("#allowance"));
22
20
  /// @dev Cross-host custody state - (uint host, bytes32 asset, uint amount)
23
21
  bytes4 constant Custody = bytes4(keccak256("#custody"));
22
+ /// @dev Asset-liability position state - (bytes32 asset, uint amount, bytes32 liability, uint debt)
23
+ bytes4 constant Position = bytes4(keccak256("#position"));
24
24
  /// @dev List wrapper; payload is an embedded repeated block stream
25
25
  bytes4 constant List = bytes4(keccak256("#list"));
26
26
  /// @dev EVM-encoded payload field; layout follows standard ABI tuple encoding
@@ -62,6 +62,8 @@ library Keys {
62
62
  bytes4 constant Status = bytes4(keccak256("#status"));
63
63
  /// @dev Structural account asset form - (bytes32 account, bytes32 asset)
64
64
  bytes4 constant AccountAsset = bytes4(keccak256("#accountAsset"));
65
+ /// @dev Structural host asset form - (uint host, bytes32 asset)
66
+ bytes4 constant HostAsset = bytes4(keccak256("#hostAsset"));
65
67
  /// @dev Structural account amount form - (bytes32 account, bytes32 asset, uint amount)
66
68
  bytes4 constant AccountAmount = bytes4(keccak256("#accountAmount"));
67
69
  /// @dev Structural host amount form - (uint host, bytes32 asset, uint amount)
package/codec/Readers.sol CHANGED
@@ -94,6 +94,31 @@ library Readers {
94
94
  }
95
95
  }
96
96
 
97
+ /// @notice Consume a HOST_ASSET block and return its fields.
98
+ /// @param cur Reader; advanced past the block.
99
+ /// @return host Host identifier.
100
+ /// @return asset Asset identifier.
101
+ function unpackHostAsset(Reader memory cur) internal pure returns (uint host, bytes32 asset) {
102
+ uint abs = consume(cur, Keys.HostAsset, 64, 64);
103
+ assembly ("memory-safe") {
104
+ host := mload(abs)
105
+ asset := mload(add(abs, 0x20))
106
+ }
107
+ }
108
+
109
+ /// @notice Consume a POSITION block and return its fields.
110
+ function unpackPosition(
111
+ Reader memory cur
112
+ ) internal pure returns (bytes32 asset, uint amount, bytes32 liability, uint debt) {
113
+ uint abs = consume(cur, Keys.Position, 128, 128);
114
+ assembly ("memory-safe") {
115
+ asset := mload(abs)
116
+ amount := mload(add(abs, 0x20))
117
+ liability := mload(add(abs, 0x40))
118
+ debt := mload(add(abs, 0x60))
119
+ }
120
+ }
121
+
97
122
  /// @notice Consume a TRANSACTION block and return its fields.
98
123
  /// @param cur Reader; advanced past the block.
99
124
  /// @return from Source account identifier.
package/codec/Schema.sol CHANGED
@@ -43,10 +43,13 @@ pragma solidity ^0.8.33;
43
43
  // - command input and state streams are each a single run of blocks under the
44
44
  // current protocol convention; the block format may support other shapes in
45
45
  // future protocol surfaces
46
- // - `balance(...)` and `custody(...)` are live, linear state in the active command pipeline
46
+ // - `balance(...)`, `custody(...)`, and `position(...)` are live, linear state in the active command pipeline
47
47
  // - pipeline state belongs to the active account while the pipeline is executing
48
48
  // - while a balance or custody is in-flight as pipeline state, it is not simultaneously persisted
49
49
  // in another ledger/store by this protocol
50
+ // - a position pairs live asset and liability sides; commands may transform either side
51
+ // - position state is transient and does not itself create or erase an externally persisted obligation
52
+ // - positions support backward composition, but pipeline steps always execute in encoded order
50
53
  // - commands must preserve, transform, settle, or intentionally consume pipeline state
51
54
  // - input blocks such as `amount(...)`, `allocation(...)`, and `allowance(...)`
52
55
  // express intent, constraints, or references
@@ -78,6 +81,7 @@ library Schemas {
78
81
  string constant Amount = "{ bytes32 asset, uint amount }";
79
82
  string constant Balance = "{ bytes32 asset, uint amount }";
80
83
  string constant AccountAsset = "{ bytes32 account, bytes32 asset }";
84
+ string constant HostAsset = "{ uint host, bytes32 asset }";
81
85
 
82
86
  // Three-word payloads
83
87
 
@@ -90,6 +94,7 @@ library Schemas {
90
94
 
91
95
  // Four-word payloads
92
96
 
97
+ string constant Position = "{ bytes32 asset, uint amount, bytes32 liability, uint debt }";
93
98
  string constant Transaction = "{ bytes32 from, bytes32 to, bytes32 asset, uint amount }";
94
99
  string constant HostAccountAmount = "{ uint host, bytes32 account, bytes32 asset, uint amount }";
95
100
 
package/codec/Specs.sol CHANGED
@@ -27,10 +27,14 @@ library Sizes {
27
27
  uint constant Amount = B64;
28
28
  /// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
29
29
  uint constant Balance = B64;
30
+ /// @dev HOST_ASSET block: 8 header + 32 host + 32 asset = 72 bytes
31
+ uint constant HostAsset = B64;
30
32
  /// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 amount = 104 bytes
31
33
  uint constant HostAmount = B96;
32
34
  /// @dev TRANSACTION block: 8 header + 32 from + 32 to + 32 asset + 32 amount = 136 bytes
33
35
  uint constant Transaction = B128;
36
+ /// @dev POSITION block: 8 header + four-word asset-liability pair = 136 bytes
37
+ uint constant Position = B128;
34
38
  }
35
39
 
36
40
  /// @title Specs
@@ -44,7 +48,6 @@ library Specs {
44
48
  error InvalidSpec();
45
49
  /// @dev A direct specification cannot contain a wrapper block.
46
50
  error InvalidContainer();
47
-
48
51
  uint private constant SizeFields = (uint(1) << 192) | (uint(1) << 160) | (uint(1) << 136);
49
52
 
50
53
  // Reusable field shapes keep public specs readable while remaining valid
@@ -60,12 +63,12 @@ library Specs {
60
63
  uint private constant UnboundedMin104Hint256 = (uint(104) << 192) | (uint(256) << 136);
61
64
 
62
65
  uint constant Empty = uint(bytes32(Keys.Empty));
63
- uint constant Any = uint(bytes32(Keys.Any)) | UnboundedHint128;
64
66
  uint constant Amount = uint(bytes32(Keys.Amount)) | Exact64;
65
67
  uint constant Balance = uint(bytes32(Keys.Balance)) | Exact64;
66
68
  uint constant Allocation = uint(bytes32(Keys.Allocation)) | Exact96;
67
69
  uint constant Allowance = uint(bytes32(Keys.Allowance)) | Exact96;
68
70
  uint constant Custody = uint(bytes32(Keys.Custody)) | Exact96;
71
+ uint constant Position = uint(bytes32(Keys.Position)) | Exact128;
69
72
  uint constant List = uint(bytes32(Keys.List)) | UnboundedHint128;
70
73
  uint constant Evm = uint(bytes32(Keys.Evm)) | UnboundedHint128;
71
74
  uint constant Bytes = uint(bytes32(Keys.Bytes)) | UnboundedHint128;
@@ -87,6 +90,7 @@ library Specs {
87
90
 
88
91
  uint constant Status = uint(bytes32(Keys.Status)) | Exact32;
89
92
  uint constant AccountAsset = uint(bytes32(Keys.AccountAsset)) | Exact64;
93
+ uint constant HostAsset = uint(bytes32(Keys.HostAsset)) | Exact64;
90
94
  uint constant AccountAmount = uint(bytes32(Keys.AccountAmount)) | Exact96;
91
95
  uint constant HostAmount = uint(bytes32(Keys.HostAmount)) | Exact96;
92
96
  uint constant HostAccountAsset = uint(bytes32(Keys.HostAccountAsset)) | Exact96;
@@ -237,4 +241,5 @@ library Specs {
237
241
  function group(uint spec, uint8 n) internal pure returns (uint) {
238
242
  return replace8(spec, 128, n);
239
243
  }
244
+
240
245
  }
package/codec/Writers.sol CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AssetAmount, AccountAmount, HostAmount, Tx} from "../core/Types.sol";
4
+ import {AssetAmount, AccountAmount, HostAmount, Position, Tx} from "../core/Types.sol";
5
5
  import {Blocks} from "./Blocks.sol";
6
6
  import {Buffers} from "./Buffers.sol";
7
7
  import {Sizes, Specs} from "./Specs.sol";
@@ -44,7 +44,7 @@ library Writers {
44
44
  (uint capacity, bool growable) = Specs.allocation(spec, groups);
45
45
  if (capacity == 0) return writer;
46
46
 
47
- writer.cur = Buffers.cursor(capacity, groups, growable, 0);
47
+ writer.cur = Buffers.cursor(capacity, Specs.count(spec, groups), growable, 0);
48
48
  }
49
49
 
50
50
  // -------------------------------------------------------------------------
@@ -204,6 +204,15 @@ library Writers {
204
204
  Blocks.writeAccountAsset(writer.dst, i, account, asset);
205
205
  }
206
206
 
207
+ /// @notice Append a HOST_ASSET block.
208
+ /// @param writer Destination writer.
209
+ /// @param host Host identifier to encode.
210
+ /// @param asset Asset identifier to encode.
211
+ function appendHostAsset(Writer memory writer, uint host, bytes32 asset) internal pure {
212
+ uint i = reserve(writer, Sizes.HostAsset);
213
+ Blocks.writeHostAsset(writer.dst, i, host, asset);
214
+ }
215
+
207
216
  /// @notice Append an ALLOCATION block.
208
217
  /// @param writer Destination writer.
209
218
  /// @param host Host identifier to encode.
@@ -291,6 +300,23 @@ library Writers {
291
300
  Blocks.writeHostAccountAsset(writer.dst, i, host, account, asset);
292
301
  }
293
302
 
303
+ /// @notice Append a POSITION block.
304
+ function appendPosition(
305
+ Writer memory writer,
306
+ bytes32 asset,
307
+ uint amount,
308
+ bytes32 liability,
309
+ uint debt
310
+ ) internal pure {
311
+ uint i = reserve(writer, Sizes.Position);
312
+ Blocks.writePosition(writer.dst, i, asset, amount, liability, debt);
313
+ }
314
+
315
+ /// @notice Append a structured POSITION value.
316
+ function appendPosition(Writer memory writer, Position memory value) internal pure {
317
+ appendPosition(writer, value.asset, value.amount, value.liability, value.debt);
318
+ }
319
+
294
320
  /// @notice Append a TRANSACTION block.
295
321
  /// @param writer Destination writer.
296
322
  /// @param from Debit account identifier.
package/commands/Base.sol CHANGED
@@ -5,7 +5,7 @@ import {CallerAccess} from "../core/Access.sol";
5
5
  import {EndpointBase} from "../core/Endpoint.sol";
6
6
  import {Blocks} from "../codec/Blocks.sol";
7
7
  import {Specs} from "../codec/Specs.sol";
8
- import {HostAmount} from "../core/Types.sol";
8
+ import {HostAmount, Position} from "../core/Types.sol";
9
9
  import {Execution, Executions, Lanes} from "../execution/Execution.sol";
10
10
  import {ReceivedEvent} from "../events/Received.sol";
11
11
  import {Actions} from "../utils/Actions.sol";
@@ -22,6 +22,8 @@ using Executions for Execution;
22
22
  abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
23
23
  /// @dev Thrown when `onlyActive` finds that `deadline` has already passed.
24
24
  error Expired();
25
+ /// @dev Thrown when a non-funded internal command receives native value.
26
+ error ValueNotAllowed();
25
27
 
26
28
  /// @dev Restrict execution to trusted callers.
27
29
  modifier onlyCommand() {
@@ -74,20 +76,11 @@ abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
74
76
  published = endpoint(id, name, descriptor);
75
77
  }
76
78
 
77
- /// @notice Open a command state stream and return the expected output block count.
78
- /// @param source State block stream to open.
79
- /// @param descriptor Packed command endpoint descriptor.
80
- /// @param batches Required batch count, or zero to accept the state count.
81
- /// @return exec Execution with its output buffer metadata initialized for the state batch count.
82
- function openState(
83
- bytes calldata source,
84
- uint descriptor,
85
- uint batches
86
- ) internal view returns (Execution memory exec) {
87
- return Executions.openState(source, descriptor, batches);
88
- }
89
-
90
- /// @notice Open a command execution with batches derived from its input and state lanes.
79
+ /// @notice Open and validate both command lanes, including lanes declared EMPTY.
80
+ /// Batches are derived from the input and state lanes. A non-empty stream for
81
+ /// a lane whose descriptor has zero stride reverts instead of being ignored.
82
+ /// Commands must account for the complete validated state by consuming it,
83
+ /// transforming and returning it, forwarding it intact, or reverting.
91
84
  /// @param state Current command state block stream.
92
85
  /// @param input Command input block stream.
93
86
  /// @param descriptor Packed command endpoint descriptor.
package/commands/Burn.sol CHANGED
@@ -36,9 +36,9 @@ abstract contract Burn is CommandBase, BurnHook, Action {
36
36
  function burn(
37
37
  bytes32 account,
38
38
  bytes calldata state,
39
- bytes calldata
39
+ bytes calldata input
40
40
  ) external onlyCommand returns (bytes memory, bytes memory) {
41
- Execution memory exec = openState(state, descriptor, 0);
41
+ Execution memory exec = openCommand(state, input, descriptor, 0);
42
42
 
43
43
  while (exec.more()) {
44
44
  (bytes32 asset, uint amount) = exec.unpackBalance(Lanes.State);
@@ -3,12 +3,15 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
5
5
  import {CreditAccountHook} from "../core/Settlement.sol";
6
+ import {Blocks} from "../codec/Blocks.sol";
7
+ import {Reader, Readers} from "../codec/Readers.sol";
6
8
 
7
9
  using Executions for Execution;
10
+ using Readers for Reader;
8
11
 
9
12
  /// @title CreditAccount
10
13
  /// @notice Command that delivers BALANCE state blocks to an account via a virtual hook.
11
- /// Use for internally recording credits that have already been settled externally.
14
+ /// Use for internally recording credits that have already been posted externally.
12
15
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
13
16
  uint private immutable descriptor;
14
17
  uint private immutable id;
@@ -29,9 +32,9 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
29
32
  function creditAccount(
30
33
  bytes32 account,
31
34
  bytes calldata state,
32
- bytes calldata
35
+ bytes calldata input
33
36
  ) external onlyCommand returns (bytes memory, bytes memory) {
34
- Execution memory exec = openState(state, descriptor, 0);
37
+ Execution memory exec = openCommand(state, input, descriptor, 0);
35
38
 
36
39
  while (exec.more()) {
37
40
  (bytes32 asset, uint amount) = exec.unpackBalance(Lanes.State);
@@ -41,3 +44,35 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
41
44
  return close(exec, account);
42
45
  }
43
46
  }
47
+
48
+ /// @title InternalCreditAccount
49
+ /// @notice Extends the advertised credit-account command with memory-state pipeline dispatch.
50
+ /// @dev This adapter is not a separate command. It uses the command ID and account hook
51
+ /// inherited from `CreditAccount` while accepting the state location used by `Pipeline`.
52
+ abstract contract InternalCreditAccount is CreditAccount {
53
+ /// @notice Execute the inherited credit-account command from an internal pipeline.
54
+ /// @param account Account credited by each balance.
55
+ /// @param state BALANCE block stream held in pipeline memory.
56
+ /// @param input Empty input required by the command schema.
57
+ /// @param value Native value assigned to the command; must be zero.
58
+ /// @return output Empty output state.
59
+ /// @return transactions Empty transaction stream.
60
+ function executeCreditAccount(
61
+ bytes32 account,
62
+ bytes memory state,
63
+ bytes calldata input,
64
+ uint128 value
65
+ ) internal returns (bytes memory, bytes memory) {
66
+ if (value != 0) revert ValueNotAllowed();
67
+ if (input.length != 0) revert Executions.ZeroStride();
68
+ if (state.length == 0) revert Blocks.EmptyRun();
69
+
70
+ Reader memory reader = Readers.open(state);
71
+ while (reader.more()) {
72
+ (bytes32 asset, uint amount) = reader.unpackBalance();
73
+ creditAccount(account, asset, amount);
74
+ }
75
+
76
+ return ("", "");
77
+ }
78
+ }
@@ -3,13 +3,18 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
5
5
  import {DebitAccountHook} from "../core/Settlement.sol";
6
+ import {Blocks} from "../codec/Blocks.sol";
7
+ import {Sizes} from "../codec/Specs.sol";
8
+ import {Decoders} from "../codec/Decoders.sol";
9
+ import {Cur} from "../utils/Cursors.sol";
6
10
 
7
11
  using Executions for Execution;
12
+ using Decoders for Cur;
8
13
 
9
14
  /// @title DebitAccount
10
15
  /// @notice Command that deducts AMOUNT blocks from an account and emits matching BALANCE state.
11
16
  /// Use for internally recording debits. The virtual `debitAccount` hook is called once per
12
- /// AMOUNT block; the default batch implementation handles the full input loop.
17
+ /// AMOUNT block.
13
18
  abstract contract DebitAccount is CommandBase, DebitAccountHook {
14
19
  uint private immutable descriptor;
15
20
  uint private immutable id;
@@ -23,11 +28,16 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
23
28
  return id;
24
29
  }
25
30
 
26
- /// @notice Override to customize input parsing or batching for debits.
27
- /// The default implementation iterates AMOUNT blocks, calls
28
- /// `debitAccount`, and emits matching BALANCE blocks.
29
- function debitAccount(bytes32 account, bytes calldata input) internal virtual returns (bytes memory, bytes memory) {
30
- Execution memory exec = openInput(input, descriptor, 0);
31
+ /// @notice Debit AMOUNT input blocks from the command account and output matching BALANCE blocks.
32
+ /// @param input AMOUNT block stream.
33
+ /// @return BALANCE block stream matching the debited amounts.
34
+ /// @return Empty transaction stream.
35
+ function debitAccount(
36
+ bytes32 account,
37
+ bytes calldata state,
38
+ bytes calldata input
39
+ ) external onlyCommand returns (bytes memory, bytes memory) {
40
+ Execution memory exec = openCommand(state, input, descriptor, 0);
31
41
 
32
42
  while (exec.more()) {
33
43
  (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
@@ -37,16 +47,42 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
37
47
 
38
48
  return close(exec, account);
39
49
  }
50
+ }
40
51
 
41
- /// @notice Debit AMOUNT input blocks from the command account and output matching BALANCE blocks.
52
+ /// @title InternalDebitAccount
53
+ /// @notice Extends the advertised debit-account command with memory-state pipeline dispatch.
54
+ /// @dev This adapter is not a separate command. It uses the command ID and account hook
55
+ /// inherited from `DebitAccount` while accepting the state location used by `Pipeline`.
56
+ abstract contract InternalDebitAccount is DebitAccount {
57
+ /// @notice Execute the inherited debit-account command from an internal pipeline.
58
+ /// @param account Account whose funds are debited.
59
+ /// @param state Empty pipeline state required by the command schema.
42
60
  /// @param input AMOUNT block stream.
43
- /// @return BALANCE block stream matching the debited amounts.
44
- /// @return Empty transaction stream.
45
- function debitAccount(
61
+ /// @param value Native value assigned to the command; must be zero.
62
+ /// @return output BALANCE block stream matching the debited amounts.
63
+ /// @return transactions Empty transaction stream.
64
+ function executeDebitAccount(
46
65
  bytes32 account,
47
- bytes calldata,
48
- bytes calldata input
49
- ) external onlyCommand returns (bytes memory, bytes memory) {
50
- return debitAccount(account, input);
66
+ bytes memory state,
67
+ bytes calldata input,
68
+ uint128 value
69
+ ) internal returns (bytes memory, bytes memory) {
70
+ if (value != 0) revert ValueNotAllowed();
71
+ if (state.length != 0) revert Executions.ZeroStride();
72
+ if (input.length == 0) revert Blocks.EmptyRun();
73
+
74
+ Cur memory cur = Decoders.wrap(input);
75
+ uint count = input.length / Sizes.Amount;
76
+ bytes memory output = new bytes(count * Sizes.Balance);
77
+ uint i;
78
+
79
+ while (cur.more()) {
80
+ (bytes32 asset, uint amount) = cur.unpackAmount();
81
+ debitAccount(account, asset, amount);
82
+ Blocks.writeBalance(output, i, asset, amount);
83
+ i += Sizes.Balance;
84
+ }
85
+
86
+ return (output, "");
51
87
  }
52
88
  }
@@ -49,10 +49,10 @@ abstract contract Deposit is CommandBase, DepositHook, Action {
49
49
  /// @return Empty transaction stream.
50
50
  function deposit(
51
51
  bytes32 account,
52
- bytes calldata,
52
+ bytes calldata state,
53
53
  bytes calldata input
54
54
  ) external onlyCommand returns (bytes memory, bytes memory) {
55
- Execution memory exec = openInput(input, descriptor, 0);
55
+ Execution memory exec = openCommand(state, input, descriptor, 0);
56
56
 
57
57
  while (exec.more()) {
58
58
  (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
@@ -82,10 +82,10 @@ abstract contract DepositPayable is CommandBase, DepositPayableHook, Action {
82
82
  /// @return Remaining native value as a refund transaction stream.
83
83
  function depositPayable(
84
84
  bytes32 account,
85
- bytes calldata,
85
+ bytes calldata state,
86
86
  bytes calldata input
87
87
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
88
- Execution memory exec = openInput(input, descriptor, 0);
88
+ Execution memory exec = openCommand(state, input, descriptor, 0);
89
89
 
90
90
  while (exec.more()) {
91
91
  (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
@@ -41,10 +41,10 @@ abstract contract Provision is CommandBase, ProvisionHook {
41
41
  /// @return Empty transaction stream.
42
42
  function provision(
43
43
  bytes32 account,
44
- bytes calldata,
44
+ bytes calldata state,
45
45
  bytes calldata input
46
46
  ) external onlyCommand returns (bytes memory, bytes memory) {
47
- Execution memory exec = openInput(input, descriptor, 0);
47
+ Execution memory exec = openCommand(state, input, descriptor, 0);
48
48
 
49
49
  while (exec.more()) {
50
50
  HostAmount memory allocation = exec.unpackAllocationValue(Lanes.Input);
@@ -73,10 +73,10 @@ abstract contract ProvisionPayable is CommandBase, ProvisionPayableHook {
73
73
  /// @return Remaining native value as a refund transaction stream.
74
74
  function provisionPayable(
75
75
  bytes32 account,
76
- bytes calldata,
76
+ bytes calldata state,
77
77
  bytes calldata input
78
78
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
79
- Execution memory exec = openInput(input, descriptor, 0);
79
+ Execution memory exec = openCommand(state, input, descriptor, 0);
80
80
 
81
81
  while (exec.more()) {
82
82
  HostAmount memory allocation = exec.unpackAllocationValue(Lanes.Input);
@@ -6,21 +6,28 @@ import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
6
6
  using Executions for Execution;
7
7
 
8
8
  /// @notice Hook implemented by hosts that recover previously undelivered payloads.
9
- abstract contract RecoverHook {
9
+ abstract contract RecoverPayableHook {
10
10
  /// @notice Override to recover a witness through `handler`.
11
11
  /// @param handler Port that should attempt recovery.
12
+ /// @param resources Chain-specific resources assigned to the recovery attempt.
12
13
  /// @param key Recovery lookup key.
13
14
  /// @param witness Witness payload used to prove and replay recovery.
14
- /// @param value Native EVM value assigned to the recovery attempt.
15
- function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual;
15
+ /// @param funds Shared execution containing the source value budget.
16
+ function recover(
17
+ uint handler,
18
+ uint resources,
19
+ bytes32 key,
20
+ bytes calldata witness,
21
+ Execution memory funds
22
+ ) internal virtual;
16
23
  }
17
24
 
18
25
  /// @title RecoverPayable
19
26
  /// @notice Command that forwards recover input blocks to a virtual hook.
20
27
  /// Recovery is witness-driven: the command account pays and receives leftover
21
- /// value settlement, but the recovered subject is defined by each witness.
28
+ /// value posting, but the recovered subject is defined by each witness.
22
29
  /// Produces no output state.
23
- abstract contract RecoverPayable is CommandBase, RecoverHook {
30
+ abstract contract RecoverPayable is CommandBase, RecoverPayableHook {
24
31
  uint private immutable descriptor;
25
32
 
26
33
  constructor() {
@@ -33,14 +40,14 @@ abstract contract RecoverPayable is CommandBase, RecoverHook {
33
40
  /// @return Remaining native value as a refund transaction stream.
34
41
  function recoverPayable(
35
42
  bytes32 account,
36
- bytes calldata,
43
+ bytes calldata state,
37
44
  bytes calldata input
38
45
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
39
- Execution memory exec = openInput(input, descriptor, 0);
46
+ Execution memory exec = openCommand(state, input, descriptor, 0);
40
47
 
41
48
  while (exec.more()) {
42
49
  (uint handler, uint resources, bytes32 key, bytes calldata witness) = exec.unpackRecover(Lanes.Input);
43
- recover(handler, key, witness, exec.useValue(resources));
50
+ recover(handler, resources, key, witness, exec);
44
51
  }
45
52
 
46
53
  return close(exec, account);