@rootzero/contracts 1.25.0 → 1.27.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 (71) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/Codec.sol +1 -2
  3. package/Endpoints.sol +2 -0
  4. package/README.md +73 -51
  5. package/Utils.sol +2 -1
  6. package/codec/Blocks.sol +276 -88
  7. package/codec/Buffers.sol +24 -20
  8. package/codec/Decoders.sol +35 -37
  9. package/codec/Descriptors.sol +75 -72
  10. package/codec/Keys.sol +5 -1
  11. package/codec/Schema.sol +7 -1
  12. package/codec/Specs.sol +19 -9
  13. package/codec/Writers.sol +14 -19
  14. package/commands/Allocate.sol +8 -7
  15. package/commands/Base.sol +14 -49
  16. package/commands/Bootstrap.sol +81 -0
  17. package/commands/Burn.sol +7 -7
  18. package/commands/Cashout.sol +86 -0
  19. package/commands/Credit.sol +20 -17
  20. package/commands/Debit.sol +25 -20
  21. package/commands/Deposit.sol +13 -13
  22. package/commands/Payout.sol +8 -8
  23. package/commands/Provision.sol +13 -13
  24. package/commands/Recover.sol +7 -7
  25. package/commands/Relay.sol +22 -23
  26. package/commands/Repay.sol +38 -35
  27. package/commands/Settle.sol +26 -23
  28. package/commands/Withdraw.sol +7 -7
  29. package/commands/admin/AllowAssets.sol +7 -7
  30. package/commands/admin/Allowance.sol +7 -7
  31. package/commands/admin/Annotate.sol +7 -7
  32. package/commands/admin/Appoint.sol +7 -7
  33. package/commands/admin/Authorize.sol +7 -7
  34. package/commands/admin/Base.sol +5 -8
  35. package/commands/admin/DenyAssets.sol +7 -7
  36. package/commands/admin/Dismiss.sol +7 -7
  37. package/commands/admin/Execute.sol +8 -8
  38. package/commands/admin/Unauthorize.sol +7 -7
  39. package/core/Access.sol +1 -0
  40. package/core/Calls.sol +3 -3
  41. package/core/Endpoint.sol +7 -7
  42. package/core/Pipeline.sol +19 -23
  43. package/core/Settlement.sol +2 -1
  44. package/core/Types.sol +1 -0
  45. package/execution/Budget.sol +2 -3
  46. package/execution/Execution.sol +270 -511
  47. package/guards/Base.sol +1 -1
  48. package/guards/Revoke.sol +7 -7
  49. package/package.json +1 -1
  50. package/ports/Allowance.sol +3 -3
  51. package/ports/Assets.sol +7 -7
  52. package/ports/Base.sol +1 -1
  53. package/ports/Credit.sol +3 -3
  54. package/ports/Debit.sol +3 -3
  55. package/ports/Dispatch.sol +3 -3
  56. package/ports/Pipe.sol +3 -3
  57. package/ports/Post.sol +3 -3
  58. package/ports/Redeem.sol +3 -3
  59. package/queries/Assets.sol +3 -3
  60. package/queries/Balances.sol +3 -3
  61. package/queries/Base.sol +1 -1
  62. package/utils/Accounts.sol +17 -10
  63. package/utils/Actions.sol +2 -0
  64. package/utils/Assets.sol +1 -10
  65. package/utils/Cursors.sol +60 -116
  66. package/utils/Errors.sol +56 -0
  67. package/utils/Ids.sol +2 -5
  68. package/utils/Lanes.sol +0 -2
  69. package/utils/Nodes.sol +1 -3
  70. package/utils/Utils.sol +2 -9
  71. package/codec/Readers.sol +0 -190
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Annotate
@@ -12,23 +12,23 @@ abstract contract Annotate is AdminBase {
12
12
  uint private immutable descriptor;
13
13
 
14
14
  constructor() {
15
- (, descriptor) = command("annotate", Specs.Empty, Specs.Annotation, Specs.Empty, 0, Flags.Admin);
15
+ (, descriptor) = command("annotate", Specs.Empty, Specs.Annotation, Specs.Empty, Flags.Admin);
16
16
  }
17
17
 
18
18
  /// @notice Publish each ANNOTATION block in the admin input.
19
19
  /// @param context Admin command context carrying the ANNOTATION input stream.
20
20
  /// @return Empty output state.
21
- /// @return Empty transaction stream.
21
+ /// @return Zero native budget credit.
22
22
  function annotate(
23
23
  bytes calldata context
24
- ) external returns (bytes memory, bytes memory) {
25
- Execution memory exec = openAdminCommand(context, descriptor, 0);
24
+ ) external returns (bytes memory, uint) {
25
+ Execution memory exec = openAdminCommand(context, descriptor);
26
26
 
27
27
  while (exec.more()) {
28
- (uint entity, bytes calldata data) = exec.unpackAnnotation(Lanes.Input);
28
+ (uint entity, bytes calldata data) = exec.unpackAnnotation();
29
29
  emit Annotation(entity, data);
30
30
  }
31
31
 
32
- return closeCommand(exec);
32
+ return exec.close();
33
33
  }
34
34
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Flags, Lanes, Specs } from "./Base.sol";
4
+ import { AdminBase, Execution, Executions, Flags, Specs } from "./Base.sol";
5
5
  import { GuardianAccess } from "../../core/Access.sol";
6
6
  using Executions for Execution;
7
7
 
@@ -13,23 +13,23 @@ abstract contract Appoint is GuardianAccess, AdminBase {
13
13
  uint private immutable descriptor;
14
14
 
15
15
  constructor() {
16
- (, descriptor) = command("appoint", Specs.Empty, Specs.Account, Specs.Empty, 0, Flags.Admin);
16
+ (, descriptor) = command("appoint", Specs.Empty, Specs.Account, Specs.Empty, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Appoint each user ACCOUNT block in the admin input as a guardian.
20
20
  /// @param context Admin command context carrying the ACCOUNT input stream.
21
21
  /// @return Empty output state.
22
- /// @return Empty transaction stream.
22
+ /// @return Zero native budget credit.
23
23
  function appoint(
24
24
  bytes calldata context
25
- ) external returns (bytes memory, bytes memory) {
26
- Execution memory exec = openAdminCommand(context, descriptor, 0);
25
+ ) external returns (bytes memory, uint) {
26
+ Execution memory exec = openAdminCommand(context, descriptor);
27
27
 
28
28
  while (exec.more()) {
29
- bytes32 guardian = exec.unpackAccount(Lanes.Input);
29
+ bytes32 guardian = exec.unpackAccount();
30
30
  setGuardian(guardian, true);
31
31
  }
32
32
 
33
- return closeCommand(exec);
33
+ return exec.close();
34
34
  }
35
35
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Authorize
@@ -13,7 +13,7 @@ abstract contract Authorize is AdminBase {
13
13
  uint private immutable id;
14
14
 
15
15
  constructor() {
16
- (id, descriptor) = command("authorize", Specs.Empty, Specs.Node, Specs.Empty, 0, Flags.Admin);
16
+ (id, descriptor) = command("authorize", Specs.Empty, Specs.Node, Specs.Empty, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Return the registered AUTHORIZE command ID.
@@ -24,17 +24,17 @@ abstract contract Authorize is AdminBase {
24
24
  /// @notice Authorize each NODE block in the admin input.
25
25
  /// @param context Admin command context carrying the NODE input stream.
26
26
  /// @return Empty output state.
27
- /// @return Empty transaction stream.
27
+ /// @return Zero native budget credit.
28
28
  function authorize(
29
29
  bytes calldata context
30
- ) external returns (bytes memory, bytes memory) {
31
- Execution memory exec = openAdminCommand(context, descriptor, 0);
30
+ ) external returns (bytes memory, uint) {
31
+ Execution memory exec = openAdminCommand(context, descriptor);
32
32
 
33
33
  while (exec.more()) {
34
- uint node = exec.unpackNode(Lanes.Input);
34
+ uint node = exec.unpackNode();
35
35
  setNode(node, true);
36
36
  }
37
37
 
38
- return closeCommand(exec);
38
+ return exec.close();
39
39
  }
40
40
  }
@@ -1,21 +1,18 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {CommandBase, Execution, Executions, Flags, Lanes, Specs} from "../Base.sol";
4
+ import {CommandBase, Execution, Executions, Flags, Specs} from "../Base.sol";
5
5
  import {NodeAccess} from "../../core/Access.sol";
6
6
 
7
7
  /// @title AdminBase
8
8
  /// @notice Shared base for admin commands.
9
9
  abstract contract AdminBase is NodeAccess, CommandBase {
10
- /// @notice Decode, authorize, and open one admin command context.
10
+ /// @notice Open and authorize one admin command context.
11
11
  function openAdminCommand(
12
12
  bytes calldata context,
13
- uint descriptor,
14
- uint batches
13
+ uint descriptor
15
14
  ) internal view returns (Execution memory exec) {
16
- (bytes32 account, bytes calldata state, bytes calldata input) = unpackCommandContext(context);
17
- enforceAdmin(account, msg.sender);
18
- exec = Executions.open(state, input, descriptor, batches);
19
- exec.account = account;
15
+ exec = openCommand(context, descriptor);
16
+ enforceAdmin(exec.account, msg.sender);
20
17
  }
21
18
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @notice Hook implemented by hosts that deny assets.
@@ -19,23 +19,23 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
19
19
  uint private immutable descriptor;
20
20
 
21
21
  constructor() {
22
- (, descriptor) = command("denyAssets", Specs.Empty, Specs.Asset, Specs.Empty, 0, Flags.Admin);
22
+ (, descriptor) = command("denyAssets", Specs.Empty, Specs.Asset, Specs.Empty, Flags.Admin);
23
23
  }
24
24
 
25
25
  /// @notice Deny each ASSET block in the admin input.
26
26
  /// @param context Admin command context carrying the ASSET input stream.
27
27
  /// @return Empty output state.
28
- /// @return Empty transaction stream.
28
+ /// @return Zero native budget credit.
29
29
  function denyAssets(
30
30
  bytes calldata context
31
- ) external returns (bytes memory, bytes memory) {
32
- Execution memory exec = openAdminCommand(context, descriptor, 0);
31
+ ) external returns (bytes memory, uint) {
32
+ Execution memory exec = openAdminCommand(context, descriptor);
33
33
 
34
34
  while (exec.more()) {
35
- bytes32 asset = exec.unpackAsset(Lanes.Input);
35
+ bytes32 asset = exec.unpackAsset();
36
36
  denyAsset(asset);
37
37
  }
38
38
 
39
- return closeCommand(exec);
39
+ return exec.close();
40
40
  }
41
41
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Flags, Lanes, Specs } from "./Base.sol";
4
+ import { AdminBase, Execution, Executions, Flags, Specs } from "./Base.sol";
5
5
  import { GuardianAccess } from "../../core/Access.sol";
6
6
  using Executions for Execution;
7
7
 
@@ -13,23 +13,23 @@ abstract contract Dismiss is GuardianAccess, AdminBase {
13
13
  uint private immutable descriptor;
14
14
 
15
15
  constructor() {
16
- (, descriptor) = command("dismiss", Specs.Empty, Specs.Account, Specs.Empty, 0, Flags.Admin);
16
+ (, descriptor) = command("dismiss", Specs.Empty, Specs.Account, Specs.Empty, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Dismiss each user ACCOUNT block in the admin input from guardian status.
20
20
  /// @param context Admin command context carrying the ACCOUNT input stream.
21
21
  /// @return Empty output state.
22
- /// @return Empty transaction stream.
22
+ /// @return Zero native budget credit.
23
23
  function dismiss(
24
24
  bytes calldata context
25
- ) external returns (bytes memory, bytes memory) {
26
- Execution memory exec = openAdminCommand(context, descriptor, 0);
25
+ ) external returns (bytes memory, uint) {
26
+ Execution memory exec = openAdminCommand(context, descriptor);
27
27
 
28
28
  while (exec.more()) {
29
- bytes32 guardian = exec.unpackAccount(Lanes.Input);
29
+ bytes32 guardian = exec.unpackAccount();
30
30
  setGuardian(guardian, false);
31
31
  }
32
32
 
33
- return closeCommand(exec);
33
+ return exec.close();
34
34
  }
35
35
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Specs} from "./Base.sol";
5
5
  import {RawNodeCalls} from "../../core/Calls.sol";
6
6
 
7
7
  using Executions for Execution;
@@ -10,28 +10,28 @@ using Executions for Execution;
10
10
  /// @notice Admin command that forwards raw calldata to one or more target nodes.
11
11
  /// Each CALL block specifies a target node ID, packed resources, and raw calldata payload.
12
12
  /// Only callable by the admin account.
13
- /// Unspent top-level `msg.value` is returned as a refund transaction.
13
+ /// Unspent top-level `msg.value` is returned as native budget credit.
14
14
  abstract contract ExecutePayable is RawNodeCalls, AdminBase {
15
15
  uint private immutable descriptor;
16
16
 
17
17
  constructor() {
18
- (, descriptor) = command("executePayable", Specs.Empty, Specs.Call, Specs.Empty, 0, Flags.AdminFunded);
18
+ (, descriptor) = command("executePayable", Specs.Empty, Specs.Call, Specs.Empty, Flags.AdminFunded);
19
19
  }
20
20
 
21
21
  /// @notice Execute each CALL block in the admin input.
22
22
  /// @param context Admin command context carrying the CALL input stream.
23
23
  /// @return Empty output state.
24
- /// @return Remaining native value as a refund transaction stream.
24
+ /// @return Native value to add to the caller's budget.
25
25
  function executePayable(
26
26
  bytes calldata context
27
- ) external payable returns (bytes memory, bytes memory) {
28
- Execution memory exec = openAdminCommand(context, descriptor, 0);
27
+ ) external payable returns (bytes memory, uint) {
28
+ Execution memory exec = openAdminCommand(context, descriptor);
29
29
 
30
30
  while (exec.more()) {
31
- (uint target, uint resources, bytes calldata data) = exec.unpackCall(Lanes.Input);
31
+ (uint target, uint resources, bytes calldata data) = exec.unpackCall();
32
32
  rawCall(target, exec.useResourceValue(resources), data);
33
33
  }
34
34
 
35
- return closeCommand(exec);
35
+ return exec.close();
36
36
  }
37
37
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Unauthorize
@@ -13,7 +13,7 @@ abstract contract Unauthorize is AdminBase {
13
13
  uint private immutable id;
14
14
 
15
15
  constructor() {
16
- (id, descriptor) = command("unauthorize", Specs.Empty, Specs.Node, Specs.Empty, 0, Flags.Admin);
16
+ (id, descriptor) = command("unauthorize", Specs.Empty, Specs.Node, Specs.Empty, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Return the registered UNAUTHORIZE command ID.
@@ -24,17 +24,17 @@ abstract contract Unauthorize is AdminBase {
24
24
  /// @notice Unauthorize each NODE block in the admin input.
25
25
  /// @param context Admin command context carrying the NODE input stream.
26
26
  /// @return Empty output state.
27
- /// @return Empty transaction stream.
27
+ /// @return Zero native budget credit.
28
28
  function unauthorize(
29
29
  bytes calldata context
30
- ) external returns (bytes memory, bytes memory) {
31
- Execution memory exec = openAdminCommand(context, descriptor, 0);
30
+ ) external returns (bytes memory, uint) {
31
+ Execution memory exec = openAdminCommand(context, descriptor);
32
32
 
33
33
  while (exec.more()) {
34
- uint node = exec.unpackNode(Lanes.Input);
34
+ uint node = exec.unpackNode();
35
35
  setNode(node, false);
36
36
  }
37
37
 
38
- return closeCommand(exec);
38
+ return exec.close();
39
39
  }
40
40
  }
package/core/Access.sol CHANGED
@@ -89,6 +89,7 @@ abstract contract NodeAccess is AdminAccess, TrustAccess, NodeEvent {
89
89
  /// @param node Node ID to update.
90
90
  /// @param active True to authorize the node, false to revoke it.
91
91
  function setNode(uint node, bool active) internal {
92
+ node = Nodes.local(node);
92
93
  nodes[node] = active;
93
94
  emit Node(host, node, active);
94
95
  }
package/core/Calls.sol CHANGED
@@ -135,17 +135,17 @@ abstract contract CommandCalls is NodeCalls {
135
135
  /// @param state Current command state block stream.
136
136
  /// @param input Command input block stream.
137
137
  /// @return nextState Decoded command output state block stream.
138
- /// @return transactions Decoded command transaction block stream.
138
+ /// @return credit Trusted native value to add to the caller's execution budget.
139
139
  function callCommand(
140
140
  uint command,
141
141
  uint128 value,
142
142
  bytes32 account,
143
143
  bytes memory state,
144
144
  bytes calldata input
145
- ) internal returns (bytes memory nextState, bytes memory transactions) {
145
+ ) internal returns (bytes memory nextState, uint credit) {
146
146
  bytes4 selector = Nodes.commandSelector(command);
147
147
  bytes memory data = encodeCommandCall(selector, account, state, input);
148
- return abi.decode(trustedCall(command, value, data), (bytes, bytes));
148
+ return abi.decode(trustedCall(command, value, data), (bytes, uint));
149
149
  }
150
150
  }
151
151
 
package/core/Endpoint.sol CHANGED
@@ -7,6 +7,8 @@ import {Label} from "../annotations/Label.sol";
7
7
  import {Schema} from "../annotations/Schema.sol";
8
8
  import {Descriptors} from "../codec/Descriptors.sol";
9
9
 
10
+ using Descriptors for uint;
11
+
10
12
  /// @title EndpointBase
11
13
  /// @notice Shared endpoint metadata helpers.
12
14
  abstract contract EndpointBase is EndpointEvent, Label, Schema {
@@ -16,7 +18,6 @@ abstract contract EndpointBase is EndpointEvent, Label, Schema {
16
18
  /// @param state State block specification.
17
19
  /// @param input Input block specification.
18
20
  /// @param output Output block specification.
19
- /// @param transactions Number of transaction blocks produced per batch, or zero for none.
20
21
  /// @param flags Packed endpoint behavior flags.
21
22
  /// @return descriptor Packed endpoint lane metadata and flags.
22
23
  function endpoint(
@@ -25,10 +26,9 @@ abstract contract EndpointBase is EndpointEvent, Label, Schema {
25
26
  uint state,
26
27
  uint input,
27
28
  uint output,
28
- uint8 transactions,
29
29
  uint8 flags
30
30
  ) internal returns (uint descriptor) {
31
- descriptor = Descriptors.create(state, input, output, transactions, flags);
31
+ descriptor = Descriptors.create(state, input, output, flags);
32
32
  return endpoint(id, name, descriptor);
33
33
  }
34
34
 
@@ -56,12 +56,12 @@ abstract contract EndpointBase is EndpointEvent, Label, Schema {
56
56
  /// Commands intentionally do not inherit this base because they must open state
57
57
  /// and input together through `openCommand`.
58
58
  abstract contract InputEndpointBase is EndpointBase {
59
- /// @notice Open an endpoint input stream with an expected batch count.
59
+ /// @notice Open a bounded endpoint input stream.
60
60
  function openInput(
61
61
  bytes calldata input,
62
- uint descriptor,
63
- uint batches
62
+ uint descriptor
64
63
  ) internal view returns (Execution memory exec) {
65
- return Executions.openInput(input, descriptor, batches);
64
+ exec.budget = msg.value;
65
+ (exec.decoders, exec.writer) = descriptor.openInput(input);
66
66
  }
67
67
  }
package/core/Pipeline.sol CHANGED
@@ -1,12 +1,9 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Decoders, Cur, Readers, Reader} from "../Codec.sol";
5
- import {PostHook} from "./Settlement.sol";
6
- import {Budgets} from "../execution/Budget.sol";
7
-
8
- using Decoders for Cur;
9
- using Readers for Reader;
4
+ import {Blocks} from "../codec/Blocks.sol";
5
+ import {Cursors} from "../utils/Cursors.sol";
6
+ import {InsufficientValue, OutOfBounds, UnexpectedState} from "../utils/Errors.sol";
10
7
 
11
8
  /// @notice Hook implemented by hosts that execute encoded step streams.
12
9
  abstract contract PipeHook {
@@ -21,28 +18,25 @@ abstract contract PipeHook {
21
18
 
22
19
  /// @title Pipeline
23
20
  /// @notice Core pipeline functionality shared by higher-level surfaces.
24
- abstract contract Pipeline is PipeHook, PostHook {
25
- /// @dev Thrown when the pipeline finishes with non-empty threaded state.
26
- error UnexpectedState();
27
-
21
+ abstract contract Pipeline is PipeHook {
28
22
  /// @notice Override to dispatch one piped step.
29
23
  /// Called once per STEP block. The returned state becomes the state passed to
30
24
  /// the next step, and the final returned state must be empty. Returned
31
- /// transactions are decoded and passed individually to `post` before the next step runs.
25
+ /// credit is added to the shared native-value budget before the next step runs.
32
26
  /// @param cmd Command node ID to invoke or handle.
33
27
  /// @param account Account identifier for the piped context.
34
28
  /// @param state Current threaded state block stream.
35
29
  /// @param input Step input block stream.
36
30
  /// @param value Native EVM value assigned to this step.
37
31
  /// @return output Updated state block stream for the next step.
38
- /// @return transactions Transaction block stream produced by the command.
32
+ /// @return credit Trusted native value to add to the pipeline budget.
39
33
  function dispatch(
40
34
  uint cmd,
41
35
  bytes32 account,
42
36
  bytes memory state,
43
37
  bytes calldata input,
44
38
  uint128 value
45
- ) internal virtual returns (bytes memory output, bytes memory transactions);
39
+ ) internal virtual returns (bytes memory output, uint credit);
46
40
 
47
41
  /// @notice Execute a STEP block stream through the pipeline.
48
42
  /// @dev Reverts with `UnexpectedState` if the final threaded state is non-empty.
@@ -58,19 +52,21 @@ abstract contract Pipeline is PipeHook, PostHook {
58
52
  bytes calldata steps,
59
53
  uint budget
60
54
  ) internal virtual override returns (uint remaining) {
61
- Cur memory cur = Decoders.open(steps);
55
+ (uint abs, uint end) = Cursors.bounds(steps);
62
56
 
63
- while (cur.more()) {
64
- (uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
57
+ while (abs < end) {
58
+ uint cmd;
65
59
  uint128 value;
66
- (budget, value) = Budgets.useResourceValue(budget, resources);
67
- Reader memory txs;
68
- (state, txs.source) = dispatch(cmd, account, state, input, value);
69
-
70
- while (txs.more()) {
71
- (bytes32 from, bytes32 to, bytes32 asset, uint amount) = txs.unpackTransaction();
72
- post(from, to, asset, amount);
60
+ bytes calldata input;
61
+ (cmd, value, input, abs) = Blocks.unpackStep(abs);
62
+ if (abs > end) revert OutOfBounds();
63
+ if (value > budget) revert InsufficientValue();
64
+ unchecked {
65
+ budget -= value;
73
66
  }
67
+ uint credit;
68
+ (state, credit) = dispatch(cmd, account, state, input, value);
69
+ budget += credit;
74
70
  }
75
71
 
76
72
  if (state.length != 0) revert UnexpectedState();
@@ -61,7 +61,8 @@ abstract contract Settlement is PostHook, SettleHook, RepayHook, DebitAccountHoo
61
61
  }
62
62
 
63
63
  /// @notice Settle one position by crediting its asset and debiting its liability.
64
- /// Skips either operation when its corresponding amount is zero.
64
+ /// Skips either operation when its corresponding amount is zero, including
65
+ /// position sides encoded as absent with a zero identifier and quantity.
65
66
  function settle(
66
67
  bytes32 account,
67
68
  bytes32 asset,
package/core/Types.sol CHANGED
@@ -76,6 +76,7 @@ struct Debt {
76
76
  }
77
77
 
78
78
  /// @notice Asset and liability pair threaded as live pipeline state.
79
+ /// Either side may be absent by setting both its identifier and quantity to zero.
79
80
  struct Position {
80
81
  /// @dev Identifier for the asset side.
81
82
  bytes32 asset;
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
+ import {InsufficientValue} from "../utils/Errors.sol";
5
+
4
6
  /// @notice Mutable native-value budget shared across internal calls.
5
7
  struct Budget {
6
8
  /// @dev Remaining unspent native value in wei.
@@ -10,9 +12,6 @@ struct Budget {
10
12
  /// @title Budgets
11
13
  /// @notice Opening and mutation helpers for standalone native-value budgets.
12
14
  library Budgets {
13
- /// @dev Thrown when an operation attempts to spend more value than remains.
14
- error InsufficientValue();
15
-
16
15
  /// @notice Open a standalone budget containing the current call value.
17
16
  /// @return budget Budget initialized with `msg.value`.
18
17
  function open() internal view returns (Budget memory budget) {