@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
@@ -5,28 +5,53 @@ import {Blocks, Execution, Executions, CommandBase, Lanes, Specs} from "./Base.s
5
5
 
6
6
  using Executions for Execution;
7
7
 
8
- /// @notice Hook implemented by hosts that route funded relay payloads.
9
- abstract contract RoutePayableHook {
10
- /// @notice Override to route an encoded payload through `portal`.
8
+ /// @notice Hook implemented by hosts that forward funded relay payloads.
9
+ abstract contract RelayPayableHook {
10
+ /// @notice Override to relay an encoded payload to `portal`.
11
11
  /// @param portal Destination portal identifier, often the destination host ID.
12
12
  /// @param resources Chain-specific destination resources. EVM adapters
13
13
  /// may interpret this as packed execution gas and destination value.
14
14
  /// @param payload Encoded payload ready for the transport layer.
15
- /// @param funds Execution used only for source value available for transport
16
- /// fees and destination resource funding.
17
- function route(uint portal, uint resources, bytes memory payload, Execution memory funds) internal virtual;
15
+ /// @param funds Execution used for source value available for transport fees
16
+ /// and destination resource funding.
17
+ function relayTo(uint portal, uint resources, bytes memory payload, Execution memory funds) internal virtual;
18
18
  }
19
19
 
20
20
  /// @title RelayPayable
21
- /// @notice Command that forwards one RELAY block to a host-defined relay hook.
21
+ /// @notice Command that forwards one RELAY block without pipeline state.
22
+ abstract contract RelayPayable is CommandBase, RelayPayableHook {
23
+ uint private immutable descriptor;
24
+
25
+ constructor() {
26
+ (, descriptor) = command("relayPayable", Specs.Empty, Specs.Relay, Specs.Empty, 0, true, false);
27
+ }
28
+
29
+ /// @notice Relay one RELAY input block with the command account and empty state.
30
+ function relayPayable(
31
+ bytes32 account,
32
+ bytes calldata state,
33
+ bytes calldata input
34
+ ) external payable onlyCommand returns (bytes memory, bytes memory) {
35
+ Execution memory exec = openCommand(state, input, descriptor, 1);
36
+ (uint portal, uint resources, bytes calldata payload) = exec.unpackRelay(Lanes.Input);
37
+ bytes memory context = Blocks.context(account, bytes(state), bytes(payload));
38
+
39
+ relayTo(portal, resources, context, exec);
40
+
41
+ return close(exec, account);
42
+ }
43
+ }
44
+
45
+ /// @title RelayBalancePayable
46
+ /// @notice Command that forwards required BALANCE state with one RELAY block.
22
47
  /// Reverts unless the input contains exactly one RELAY block, preventing
23
48
  /// the same state from being duplicated across multiple relays.
24
49
  /// Produces no output state.
25
- abstract contract RelayPayable is CommandBase, RoutePayableHook {
50
+ abstract contract RelayBalancePayable is CommandBase, RelayPayableHook {
26
51
  uint private immutable descriptor;
27
52
 
28
53
  constructor() {
29
- (, descriptor) = command("relayPayable", Specs.Any, Specs.Relay, Specs.Empty, 0, true, false);
54
+ (, descriptor) = command("relayBalancePayable", Specs.Balance, Specs.Relay, Specs.Empty, 0, true, false);
30
55
  }
31
56
 
32
57
  /// @notice Relay one RELAY input block with the command account and current state.
@@ -34,16 +59,16 @@ abstract contract RelayPayable is CommandBase, RoutePayableHook {
34
59
  /// @param input Exactly one RELAY block.
35
60
  /// @return Empty output state.
36
61
  /// @return Remaining native value as a refund transaction stream.
37
- function relayPayable(
62
+ function relayBalancePayable(
38
63
  bytes32 account,
39
64
  bytes calldata state,
40
65
  bytes calldata input
41
66
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
42
- Execution memory exec = openInput(input, descriptor, 1);
67
+ Execution memory exec = openCommand(state, input, descriptor, 1);
43
68
  (uint portal, uint resources, bytes calldata payload) = exec.unpackRelay(Lanes.Input);
44
69
  bytes memory context = Blocks.context(account, bytes(state), bytes(payload));
45
70
 
46
- route(portal, resources, context, exec);
71
+ relayTo(portal, resources, context, exec);
47
72
 
48
73
  return close(exec, account);
49
74
  }
@@ -0,0 +1,130 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
5
+ import {SettleHook} from "../core/Settlement.sol";
6
+ import {Action} from "../annotations/Action.sol";
7
+ import {Actions} from "../utils/Actions.sol";
8
+ import {Blocks} from "../codec/Blocks.sol";
9
+ import {Reader, Readers} from "../codec/Readers.sol";
10
+
11
+ using Executions for Execution;
12
+ using Readers for Reader;
13
+
14
+ /// @notice Hook implemented by hosts that settle positions using native value.
15
+ abstract contract SettlePayableHook {
16
+ /// @notice Override to settle one position for `account` with a shared value budget.
17
+ /// @param account Account whose position is being settled.
18
+ /// @param asset Identifier for the asset side.
19
+ /// @param amount Quantity on the asset side.
20
+ /// @param liability Identifier for the liability side.
21
+ /// @param debt Quantity on the liability side.
22
+ /// @param funds Mutable execution used only for its remaining native-value budget.
23
+ function settle(
24
+ bytes32 account,
25
+ bytes32 asset,
26
+ uint amount,
27
+ bytes32 liability,
28
+ uint debt,
29
+ Execution memory funds
30
+ ) internal virtual;
31
+ }
32
+
33
+ /// @title Settle
34
+ /// @notice Command that consumes POSITION state blocks through a virtual hook.
35
+ abstract contract Settle is CommandBase, SettleHook, Action {
36
+ uint private immutable descriptor;
37
+ uint private immutable id;
38
+
39
+ constructor() {
40
+ (id, descriptor) = command("settle", Specs.Position, Specs.Empty, Specs.Empty, 0, false, false);
41
+ action(id, Actions.Settle);
42
+ }
43
+
44
+ /// @notice Return the registered SETTLE command ID.
45
+ function settleId() internal view returns (uint) {
46
+ return id;
47
+ }
48
+
49
+ /// @notice Settle each POSITION block from the command state.
50
+ /// @param state POSITION block stream.
51
+ /// @return Empty output state.
52
+ /// @return Empty transaction stream.
53
+ function settle(
54
+ bytes32 account,
55
+ bytes calldata state,
56
+ bytes calldata input
57
+ ) external onlyCommand returns (bytes memory, bytes memory) {
58
+ Execution memory exec = openCommand(state, input, descriptor, 0);
59
+
60
+ while (exec.more()) {
61
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
62
+ settle(account, asset, amount, liability, debt);
63
+ }
64
+
65
+ return close(exec, account);
66
+ }
67
+ }
68
+
69
+ /// @title SettlePayable
70
+ /// @notice Funded command that consumes POSITION state blocks through a virtual hook.
71
+ abstract contract SettlePayable is CommandBase, SettlePayableHook, Action {
72
+ uint private immutable descriptor;
73
+
74
+ constructor() {
75
+ uint id;
76
+ (id, descriptor) = command("settlePayable", Specs.Position, Specs.Empty, Specs.Empty, 0, true, false);
77
+ action(id, Actions.Settle);
78
+ }
79
+
80
+ /// @notice Settle each POSITION block with access to a shared native-value budget.
81
+ /// @param state POSITION block stream.
82
+ /// @return Empty output state.
83
+ /// @return Remaining native value as a refund transaction stream.
84
+ function settlePayable(
85
+ bytes32 account,
86
+ bytes calldata state,
87
+ bytes calldata input
88
+ ) external payable onlyCommand returns (bytes memory, bytes memory) {
89
+ Execution memory exec = openCommand(state, input, descriptor, 0);
90
+
91
+ while (exec.more()) {
92
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
93
+ settle(account, asset, amount, liability, debt, exec);
94
+ }
95
+
96
+ return close(exec, account);
97
+ }
98
+ }
99
+
100
+ /// @title InternalSettle
101
+ /// @notice Extends the advertised settle command with memory-state pipeline dispatch.
102
+ /// @dev This adapter is not a separate command. It uses the command ID and settlement hook
103
+ /// inherited from `Settle` while accepting the state location used by `Pipeline`.
104
+ abstract contract InternalSettle is Settle {
105
+ /// @notice Execute the inherited settle command from an internal pipeline.
106
+ /// @param account Account for which each position is settled.
107
+ /// @param state POSITION block stream held in pipeline memory.
108
+ /// @param input Empty input required by the command schema.
109
+ /// @param value Native value assigned to the command; must be zero.
110
+ /// @return output Empty output state.
111
+ /// @return transactions Empty transaction stream.
112
+ function executeSettle(
113
+ bytes32 account,
114
+ bytes memory state,
115
+ bytes calldata input,
116
+ uint128 value
117
+ ) internal returns (bytes memory, bytes memory) {
118
+ if (value != 0) revert ValueNotAllowed();
119
+ if (input.length != 0) revert Executions.ZeroStride();
120
+ if (state.length == 0) revert Blocks.EmptyRun();
121
+
122
+ Reader memory reader = Readers.open(state);
123
+ while (reader.more()) {
124
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = reader.unpackPosition();
125
+ settle(account, asset, amount, liability, debt);
126
+ }
127
+
128
+ return ("", "");
129
+ }
130
+ }
@@ -36,9 +36,9 @@ abstract contract Withdraw is CommandBase, WithdrawHook, Action {
36
36
  function withdraw(
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);
@@ -28,10 +28,10 @@ abstract contract AllowAssets is AdminBase, AllowAssetsHook {
28
28
  /// @return Empty transaction stream.
29
29
  function allowAssets(
30
30
  bytes32 account,
31
- bytes calldata,
31
+ bytes calldata state,
32
32
  bytes calldata input
33
33
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
34
- Execution memory exec = openInput(input, descriptor, 0);
34
+ Execution memory exec = openCommand(state, input, descriptor, 0);
35
35
 
36
36
  while (exec.more()) {
37
37
  bytes32 asset = exec.unpackAsset(Lanes.Input);
@@ -12,7 +12,7 @@ abstract contract AllowanceHook {
12
12
  /// or another host-specific authorization record.
13
13
  /// @param peer Host node receiving the allowed cap.
14
14
  /// @param asset Asset identifier.
15
- /// @param amount Allowed cap amount.
15
+ /// @param amount Allowed cap amount. A zero amount MUST revoke the allowance.
16
16
  function allowance(uint peer, bytes32 asset, uint amount) internal virtual;
17
17
  }
18
18
 
@@ -32,10 +32,10 @@ abstract contract Allowance is AdminBase, AllowanceHook {
32
32
  /// @return Empty transaction stream.
33
33
  function allowance(
34
34
  bytes32 account,
35
- bytes calldata,
35
+ bytes calldata state,
36
36
  bytes calldata input
37
37
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
38
- Execution memory exec = openInput(input, descriptor, 0);
38
+ Execution memory exec = openCommand(state, input, descriptor, 0);
39
39
 
40
40
  while (exec.more()) {
41
41
  (uint peer, bytes32 asset, uint amount) = exec.unpackAllowance(Lanes.Input);
@@ -21,10 +21,10 @@ abstract contract Annotate is AdminBase {
21
21
  /// @return Empty transaction stream.
22
22
  function annotate(
23
23
  bytes32 account,
24
- bytes calldata,
24
+ bytes calldata state,
25
25
  bytes calldata input
26
26
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
27
- Execution memory exec = openInput(input, descriptor, 0);
27
+ Execution memory exec = openCommand(state, input, descriptor, 0);
28
28
 
29
29
  while (exec.more()) {
30
30
  (uint entity, bytes calldata data) = exec.unpackAnnotation(Lanes.Input);
@@ -22,10 +22,10 @@ abstract contract Appoint is GuardianAccess, AdminBase {
22
22
  /// @return Empty transaction stream.
23
23
  function appoint(
24
24
  bytes32 account,
25
- bytes calldata,
25
+ bytes calldata state,
26
26
  bytes calldata input
27
27
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
28
- Execution memory exec = openInput(input, descriptor, 0);
28
+ Execution memory exec = openCommand(state, input, descriptor, 0);
29
29
 
30
30
  while (exec.more()) {
31
31
  bytes32 guardian = exec.unpackAccount(Lanes.Input);
@@ -27,10 +27,10 @@ abstract contract Authorize is AdminBase {
27
27
  /// @return Empty transaction stream.
28
28
  function authorize(
29
29
  bytes32 account,
30
- bytes calldata,
30
+ bytes calldata state,
31
31
  bytes calldata input
32
32
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
33
- Execution memory exec = openInput(input, descriptor, 0);
33
+ Execution memory exec = openCommand(state, input, descriptor, 0);
34
34
 
35
35
  while (exec.more()) {
36
36
  uint node = exec.unpackNode(Lanes.Input);
@@ -28,10 +28,10 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
28
28
  /// @return Empty transaction stream.
29
29
  function denyAssets(
30
30
  bytes32 account,
31
- bytes calldata,
31
+ bytes calldata state,
32
32
  bytes calldata input
33
33
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
34
- Execution memory exec = openInput(input, descriptor, 0);
34
+ Execution memory exec = openCommand(state, input, descriptor, 0);
35
35
 
36
36
  while (exec.more()) {
37
37
  bytes32 asset = exec.unpackAsset(Lanes.Input);
@@ -22,10 +22,10 @@ abstract contract Dismiss is GuardianAccess, AdminBase {
22
22
  /// @return Empty transaction stream.
23
23
  function dismiss(
24
24
  bytes32 account,
25
- bytes calldata,
25
+ bytes calldata state,
26
26
  bytes calldata input
27
27
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
28
- Execution memory exec = openInput(input, descriptor, 0);
28
+ Execution memory exec = openCommand(state, input, descriptor, 0);
29
29
 
30
30
  while (exec.more()) {
31
31
  bytes32 guardian = exec.unpackAccount(Lanes.Input);
@@ -24,14 +24,14 @@ abstract contract ExecutePayable is RawNodeCalls, AdminBase {
24
24
  /// @return Remaining native value as a refund transaction stream.
25
25
  function executePayable(
26
26
  bytes32 account,
27
- bytes calldata,
27
+ bytes calldata state,
28
28
  bytes calldata input
29
29
  ) external payable onlyAdmin(account) returns (bytes memory, bytes memory) {
30
- Execution memory exec = openInput(input, descriptor, 0);
30
+ Execution memory exec = openCommand(state, input, descriptor, 0);
31
31
 
32
32
  while (exec.more()) {
33
33
  (uint target, uint resources, bytes calldata data) = exec.unpackCall(Lanes.Input);
34
- rawCall(target, exec.useValue(resources), data);
34
+ rawCall(target, exec.useResourceValue(resources), data);
35
35
  }
36
36
 
37
37
  return close(exec, account);
@@ -27,10 +27,10 @@ abstract contract Unauthorize is AdminBase {
27
27
  /// @return Empty transaction stream.
28
28
  function unauthorize(
29
29
  bytes32 account,
30
- bytes calldata,
30
+ bytes calldata state,
31
31
  bytes calldata input
32
32
  ) external onlyAdmin(account) returns (bytes memory, bytes memory) {
33
- Execution memory exec = openInput(input, descriptor, 0);
33
+ Execution memory exec = openCommand(state, input, descriptor, 0);
34
34
 
35
35
  while (exec.more()) {
36
36
  uint node = exec.unpackNode(Lanes.Input);
package/core/Endpoint.sol CHANGED
@@ -43,19 +43,6 @@ abstract contract EndpointBase is EndpointEvent, Label, Schema {
43
43
  return descriptor;
44
44
  }
45
45
 
46
- /// @notice Open an endpoint input stream with an expected batch count.
47
- /// @param source Input block stream to open.
48
- /// @param descriptor Packed endpoint descriptor.
49
- /// @param batches Required batch count, or zero to accept the input count.
50
- /// @return exec Execution with its output buffer metadata initialized for the input batch count.
51
- function openInput(
52
- bytes calldata source,
53
- uint descriptor,
54
- uint batches
55
- ) internal view returns (Execution memory exec) {
56
- return Executions.openInput(source, descriptor, batches);
57
- }
58
-
59
46
  /// @notice Finalize an execution output and return its encoded block stream.
60
47
  /// @param exec Completed endpoint execution.
61
48
  /// @return Encoded output block stream.
@@ -63,3 +50,18 @@ abstract contract EndpointBase is EndpointEvent, Label, Schema {
63
50
  return Executions.finish(exec);
64
51
  }
65
52
  }
53
+
54
+ /// @title InputEndpointBase
55
+ /// @notice Shared input opening for endpoint families that have only an input lane.
56
+ /// Commands intentionally do not inherit this base because they must open state
57
+ /// and input together through `openCommand`.
58
+ abstract contract InputEndpointBase is EndpointBase {
59
+ /// @notice Open an endpoint input stream with an expected batch count.
60
+ function openInput(
61
+ bytes calldata input,
62
+ uint descriptor,
63
+ uint batches
64
+ ) internal view returns (Execution memory exec) {
65
+ return Executions.openInput(input, descriptor, batches);
66
+ }
67
+ }
package/core/Pipeline.sol CHANGED
@@ -2,7 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {Decoders, Cur, Readers, Reader} from "../Codec.sol";
5
- import {Settlement} from "./Settlement.sol";
5
+ import {PostHook} from "./Settlement.sol";
6
6
  import {Budget, Budgets} from "../execution/Budget.sol";
7
7
 
8
8
  using Decoders for Cur;
@@ -11,14 +11,14 @@ using Budgets for Budget;
11
11
 
12
12
  /// @title Pipeline
13
13
  /// @notice Core pipeline functionality shared by higher-level surfaces.
14
- abstract contract Pipeline is Settlement {
14
+ abstract contract Pipeline is PostHook {
15
15
  /// @dev Thrown when the pipeline finishes with non-empty threaded state.
16
16
  error UnexpectedState();
17
17
 
18
18
  /// @notice Override to dispatch one piped step.
19
19
  /// Called once per STEP block. The returned state becomes the state passed to
20
20
  /// the next step, and the final returned state must be empty. Returned
21
- /// transactions are decoded and passed individually to `settle` before the next step runs.
21
+ /// transactions are decoded and passed individually to `post` before the next step runs.
22
22
  /// @param cmd Command node ID to invoke or handle.
23
23
  /// @param account Account identifier for the piped context.
24
24
  /// @param state Current threaded state block stream.
@@ -42,16 +42,16 @@ abstract contract Pipeline is Settlement {
42
42
  /// @param steps STEP block stream to execute.
43
43
  /// @param budget Mutable native-value budget shared across all steps.
44
44
  function pipe(bytes32 account, bytes memory state, bytes calldata steps, Budget memory budget) internal {
45
- Cur memory cur = Decoders.open(steps, 1);
45
+ Cur memory cur = Decoders.open(steps);
46
46
 
47
47
  while (cur.more()) {
48
48
  (uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
49
49
  Reader memory txs;
50
- (state, txs.source) = dispatch(cmd, account, state, input, budget.use(resources));
50
+ (state, txs.source) = dispatch(cmd, account, state, input, budget.useResourceValue(resources));
51
51
 
52
52
  while (txs.more()) {
53
53
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = txs.unpackTransaction();
54
- settle(from, to, asset, amount);
54
+ post(from, to, asset, amount);
55
55
  }
56
56
  }
57
57
 
@@ -21,19 +21,48 @@ abstract contract CreditAccountHook {
21
21
  function creditAccount(bytes32 account, bytes32 asset, uint amount) internal virtual;
22
22
  }
23
23
 
24
+ /// @title PostHook
25
+ /// @notice Hook for posting one transaction between accounts.
26
+ abstract contract PostHook {
27
+ /// @notice Override to post one transaction.
28
+ function post(bytes32 from, bytes32 to, bytes32 asset, uint amount) internal virtual;
29
+ }
30
+
31
+ /// @title SettleHook
32
+ /// @notice Hook for settling one asset-liability position.
33
+ abstract contract SettleHook {
34
+ /// @notice Override to settle one position for `account`.
35
+ function settle(
36
+ bytes32 account,
37
+ bytes32 asset,
38
+ uint amount,
39
+ bytes32 liability,
40
+ uint debt
41
+ ) internal virtual;
42
+ }
43
+
24
44
  /// @title Settlement
25
- /// @notice Settles decoded transactions through debit and credit account hooks.
26
- abstract contract Settlement is DebitAccountHook, CreditAccountHook {
27
- /// @notice Settle one transaction by debiting its source and crediting its destination.
45
+ /// @notice Default account-hook implementation for transaction posting and position settlement.
46
+ abstract contract Settlement is PostHook, SettleHook, DebitAccountHook, CreditAccountHook {
47
+ /// @notice Post one transaction by debiting its source and crediting its destination.
28
48
  /// Returns without calling either hook when `amount` is zero and skips either
29
49
  /// operation when the corresponding account is zero.
30
- /// @param from Source account identifier.
31
- /// @param to Destination account identifier.
32
- /// @param asset Asset identifier.
33
- /// @param amount Token amount.
34
- function settle(bytes32 from, bytes32 to, bytes32 asset, uint amount) internal {
50
+ function post(bytes32 from, bytes32 to, bytes32 asset, uint amount) internal virtual override {
35
51
  if (amount == 0) return;
36
52
  if (from != 0) debitAccount(from, asset, amount);
37
53
  if (to != 0) creditAccount(to, asset, amount);
38
54
  }
55
+
56
+ /// @notice Settle one position by crediting its asset and debiting its liability.
57
+ /// Skips either operation when its corresponding amount is zero.
58
+ function settle(
59
+ bytes32 account,
60
+ bytes32 asset,
61
+ uint amount,
62
+ bytes32 liability,
63
+ uint debt
64
+ ) internal virtual override {
65
+ if (amount != 0) creditAccount(account, asset, amount);
66
+ if (debt != 0) debitAccount(account, liability, debt);
67
+ }
39
68
  }
package/core/Types.sol CHANGED
@@ -27,6 +27,14 @@ struct AccountAmount {
27
27
  uint amount;
28
28
  }
29
29
 
30
+ /// @notice Host-scoped asset shape.
31
+ struct HostAsset {
32
+ /// @dev Host node identifier.
33
+ uint host;
34
+ /// @dev Asset identifier.
35
+ bytes32 asset;
36
+ }
37
+
30
38
  /// @notice Host-scoped asset and amount shape.
31
39
  struct HostAmount {
32
40
  /// @dev Host node identifier.
@@ -59,7 +67,19 @@ struct HostAccountAmount {
59
67
  uint amount;
60
68
  }
61
69
 
62
- /// @notice Transfer payload used by transaction blocks and peer settlement.
70
+ /// @notice Asset and liability pair threaded as live pipeline state.
71
+ struct Position {
72
+ /// @dev Identifier for the asset side.
73
+ bytes32 asset;
74
+ /// @dev Quantity on the asset side.
75
+ uint amount;
76
+ /// @dev Identifier for the liability side.
77
+ bytes32 liability;
78
+ /// @dev Quantity owed on the liability side.
79
+ uint debt;
80
+ }
81
+
82
+ /// @notice Transfer payload used by transaction blocks and peer posting.
63
83
  struct Tx {
64
84
  /// @dev Sender account identifier.
65
85
  bytes32 from;
package/docs/Schema.md CHANGED
@@ -161,8 +161,58 @@ with no payload, use a zero-payload block such as `#unit`.
161
161
 
162
162
  Endpoint descriptors currently use a narrower convention than the full block
163
163
  grammar: each state, input, or output lane is a single run of blocks, without
164
- additional global items. Future protocol surfaces may use the more flexible
165
- top-level structure.
164
+ additional global items. Endpoint decoder opening requires that run to consume
165
+ the complete supplied lane; a trailing block with another key is invalid.
166
+ Lower-level cursor scanning may still intentionally open only a prefix run.
167
+ Those lower layers retain only the raw block count; descriptor strides are
168
+ applied and lane groups reconciled once when an endpoint execution opens.
169
+ Future protocol surfaces may use the more flexible top-level structure.
170
+
171
+ For commands, complete-lane validation is also a state-safety rule. State is a
172
+ linear value owned by the current pipeline step, not optional context that a
173
+ command may disregard. Every command must account for the complete supplied
174
+ state by consuming it, transforming and returning it, forwarding it intact, or
175
+ reverting. A command whose descriptor declares an empty state lane must reject
176
+ non-empty state. A command that accepts state must validate the complete stream
177
+ against its declared schema; accepting only a prefix and silently dropping the
178
+ remainder is invalid.
179
+
180
+ ## Live Pipeline State
181
+
182
+ `#balance`, `#custody`, and `#position` are live state carried between command
183
+ steps for the active account. A position atomically pairs an asset side with a
184
+ liability side:
185
+
186
+ ```txt
187
+ position { bytes32 asset, uint amount, bytes32 liability, uint debt }
188
+ ```
189
+
190
+ The pair is deliberately general. The asset side represents value acquired or
191
+ controlled, and the liability side represents value owed or required. Commands
192
+ may preserve or replace either side and return a new position. The terminal
193
+ `settle` command consumes the pair. Position state is transient protocol state;
194
+ rewriting it does not by itself create, discharge, or replace an obligation
195
+ persisted by a host or external protocol. The responsible command hook must
196
+ perform or verify those effects. A command must not ignore a supplied position:
197
+ it must explicitly consume, transform, forward, or reject it, so neither its
198
+ asset nor its debt can disappear accidentally.
199
+
200
+ This representation supports ordinary forward transformations as well as
201
+ backward composition. For example, an exact-output route can carry its desired
202
+ asset while successive hops replace the upstream liability:
203
+
204
+ ```txt
205
+ position(C, 100, C, 100)
206
+ → position(C, 100, B, 50)
207
+ → position(C, 100, A, 25)
208
+ → settle
209
+ ```
210
+
211
+ “Backward” describes how requirements are composed from the desired result
212
+ toward the source. Pipeline execution is not reversed: STEP blocks always run
213
+ forward in their encoded order. Exact-output routing is only an example;
214
+ borrowing, refinancing, collateral transformation, callback obligations,
215
+ cross-host claims, fees, and netting can use the same position state.
166
216
 
167
217
  ## Field Aliases
168
218
 
@@ -19,15 +19,23 @@ library Budgets {
19
19
  budget.remaining = msg.value;
20
20
  }
21
21
 
22
+ /// @notice Deduct an exact native value from `budget`.
23
+ /// @param budget Mutable budget to debit.
24
+ /// @param value Native value to consume in wei.
25
+ /// @return The consumed native value.
26
+ function useValue(Budget memory budget, uint value) internal pure returns (uint) {
27
+ if (value > budget.remaining) revert InsufficientValue();
28
+ budget.remaining -= value;
29
+ return value;
30
+ }
31
+
22
32
  /// @notice Deduct the EVM value lane of `resources` from `budget`.
23
33
  /// @dev EVM resources use the low 128 bits as native value/endowment.
24
34
  /// @param budget Mutable budget to debit.
25
35
  /// @param resources Packed resources whose low 128 bits contain native value.
26
36
  /// @return value Native value to forward in wei.
27
- function use(Budget memory budget, uint resources) internal pure returns (uint128 value) {
28
- value = uint128(resources);
29
- if (value > budget.remaining) revert InsufficientValue();
30
- budget.remaining -= value;
37
+ function useResourceValue(Budget memory budget, uint resources) internal pure returns (uint128) {
38
+ return uint128(useValue(budget, uint128(resources)));
31
39
  }
32
40
 
33
41
  /// @notice Remove and return all remaining value from `budget`.