@rootzero/contracts 1.14.0 → 1.16.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 (68) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -1
  4. package/Core.sol +8 -5
  5. package/Endpoints.sol +6 -6
  6. package/Events.sol +1 -3
  7. package/README.md +118 -35
  8. package/annotations/Action.sol +17 -0
  9. package/annotations/Label.sol +23 -0
  10. package/annotations/Schema.sol +53 -0
  11. package/codec/Blocks.sol +127 -32
  12. package/codec/Buffers.sol +3 -3
  13. package/codec/Decoders.sol +46 -13
  14. package/codec/Descriptors.sol +0 -1
  15. package/codec/Keys.sol +8 -4
  16. package/codec/Readers.sol +13 -0
  17. package/codec/Schema.sol +12 -3
  18. package/codec/Specs.sol +28 -4
  19. package/codec/Writers.sol +21 -6
  20. package/commands/Base.sol +39 -44
  21. package/commands/Burn.sol +8 -4
  22. package/commands/Credit.sol +35 -3
  23. package/commands/Debit.sol +46 -13
  24. package/commands/Deposit.sol +14 -8
  25. package/commands/Payout.sol +6 -2
  26. package/commands/Provision.sol +4 -4
  27. package/commands/Recover.sol +15 -8
  28. package/commands/Relay.sol +37 -12
  29. package/commands/Settle.sol +77 -0
  30. package/commands/Withdraw.sol +8 -4
  31. package/commands/admin/AllowAssets.sol +2 -2
  32. package/commands/admin/Allowance.sol +2 -2
  33. package/commands/admin/Annotate.sol +36 -0
  34. package/commands/admin/Appoint.sol +6 -5
  35. package/commands/admin/Authorize.sol +2 -2
  36. package/commands/admin/Base.sol +8 -1
  37. package/commands/admin/DenyAssets.sol +2 -2
  38. package/commands/admin/Dismiss.sol +6 -5
  39. package/commands/admin/Execute.sol +5 -4
  40. package/commands/admin/Unauthorize.sol +2 -2
  41. package/core/Access.sol +91 -49
  42. package/core/Calls.sol +27 -22
  43. package/core/Endpoint.sol +36 -46
  44. package/core/Host.sol +63 -21
  45. package/core/Pipeline.sol +6 -6
  46. package/core/Settlement.sol +37 -8
  47. package/core/Types.sol +13 -1
  48. package/docs/Schema.md +68 -7
  49. package/events/Annotation.sol +24 -0
  50. package/events/Guardian.sol +2 -2
  51. package/events/Introduction.sol +3 -2
  52. package/execution/Budget.sol +12 -4
  53. package/execution/Execution.sol +113 -18
  54. package/guards/Base.sol +4 -4
  55. package/package.json +1 -1
  56. package/ports/Base.sol +19 -7
  57. package/ports/Dispatch.sol +6 -6
  58. package/ports/{Settle.sol → Post.sol} +13 -9
  59. package/queries/Base.sol +18 -3
  60. package/utils/Accounts.sol +0 -23
  61. package/utils/Actions.sol +1 -0
  62. package/utils/Cursors.sol +79 -34
  63. package/utils/Layout.sol +0 -2
  64. package/commands/admin/Label.sol +0 -36
  65. package/commands/admin/Schemas.sol +0 -36
  66. package/events/Labeled.sol +0 -21
  67. package/events/Position.sol +0 -22
  68. package/events/Schema.sol +0 -23
package/commands/Base.sol CHANGED
@@ -1,11 +1,11 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {NodeCalls} from "../core/Calls.sol";
4
+ 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";
@@ -19,17 +19,10 @@ using Executions for Execution;
19
19
  /// @title CommandBase
20
20
  /// @notice Abstract base for all rootzero command contracts.
21
21
  /// Provides access control modifiers and command endpoint metadata helpers.
22
- abstract contract CommandBase is NodeCalls, EndpointBase, ReceivedEvent {
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
25
 
26
- /// @dev Restrict execution to the commander using the host's admin account.
27
- modifier onlyAdmin(bytes32 account) {
28
- if (account != admin) revert AccessDenied();
29
- enforceCommander(msg.sender);
30
- _;
31
- }
32
-
33
26
  /// @dev Restrict execution to trusted callers.
34
27
  modifier onlyCommand() {
35
28
  enforceCaller(msg.sender);
@@ -43,26 +36,6 @@ abstract contract CommandBase is NodeCalls, EndpointBase, ReceivedEvent {
43
36
  _;
44
37
  }
45
38
 
46
- /// @notice Close a command execution and refund unspent value to `account`.
47
- /// @param exec Command execution to close.
48
- /// @param account Account that should receive any unspent value.
49
- /// @return output Final encoded output block stream.
50
- /// @return transactions Final encoded transaction block stream.
51
- function close(
52
- Execution memory exec,
53
- bytes32 account
54
- ) internal returns (bytes memory output, bytes memory transactions) {
55
- if (exec.budget == 0 && Cursors.initial(exec.writers)) return ("", "");
56
-
57
- output = close(exec);
58
- uint amount = exec.refundValue(account, nativeAsset);
59
- if (amount != 0) {
60
- emit Received(account, nativeAsset, amount, Actions.Refund, 0);
61
- }
62
-
63
- transactions = exec.finishTransactions();
64
- }
65
-
66
39
  /// @notice Publish command metadata and a default label.
67
40
  /// @param name Command entrypoint name and default label. It must exactly
68
41
  /// match the Solidity command function name used by the canonical ABI.
@@ -83,27 +56,29 @@ abstract contract CommandBase is NodeCalls, EndpointBase, ReceivedEvent {
83
56
  bool funded,
84
57
  bool admin
85
58
  ) internal returns (uint id, uint descriptor) {
86
- id = Nodes.toCommand(Selectors.command(name), address(this));
87
- uint8 flags;
59
+ uint8 flags = 0;
88
60
  if (funded) flags |= Descriptors.Funded;
89
61
  if (admin) flags |= Descriptors.Admin;
90
- descriptor = endpoint(id, name, state, input, output, transactions, flags);
62
+ descriptor = Descriptors.create(state, input, output, transactions, flags);
63
+ return command(name, descriptor);
91
64
  }
92
65
 
93
- /// @notice Open a command state stream and return the expected output block count.
94
- /// @param source State block stream to open.
66
+ /// @notice Publish an already constructed command descriptor and default label.
67
+ /// @param name Command entrypoint name and default label. It must exactly
68
+ /// match the Solidity command function name used by the canonical ABI.
95
69
  /// @param descriptor Packed command endpoint descriptor.
96
- /// @param batches Required batch count, or zero to accept the state count.
97
- /// @return exec Execution with its output buffer metadata initialized for the state batch count.
98
- function openState(
99
- bytes calldata source,
100
- uint descriptor,
101
- uint batches
102
- ) internal view returns (Execution memory exec) {
103
- return Executions.openState(source, descriptor, batches);
70
+ /// @return id Command node ID.
71
+ /// @return published Published endpoint descriptor.
72
+ function command(string memory name, uint descriptor) internal returns (uint id, uint published) {
73
+ id = Nodes.toCommand(Selectors.command(name), address(this));
74
+ published = endpoint(id, name, descriptor);
104
75
  }
105
76
 
106
- /// @notice Open a command execution with batches derived from its input and state lanes.
77
+ /// @notice Open and validate both command lanes, including lanes declared EMPTY.
78
+ /// Batches are derived from the input and state lanes. A non-empty stream for
79
+ /// a lane whose descriptor has zero stride reverts instead of being ignored.
80
+ /// Commands must account for the complete validated state by consuming it,
81
+ /// transforming and returning it, forwarding it intact, or reverting.
107
82
  /// @param state Current command state block stream.
108
83
  /// @param input Command input block stream.
109
84
  /// @param descriptor Packed command endpoint descriptor.
@@ -117,4 +92,24 @@ abstract contract CommandBase is NodeCalls, EndpointBase, ReceivedEvent {
117
92
  ) internal view returns (Execution memory exec) {
118
93
  return Executions.open(state, input, descriptor, batches);
119
94
  }
95
+
96
+ /// @notice Close a command execution and refund unspent value to `account`.
97
+ /// @param exec Command execution to close.
98
+ /// @param account Account that should receive any unspent value.
99
+ /// @return output Final encoded output block stream.
100
+ /// @return transactions Final encoded transaction block stream.
101
+ function close(
102
+ Execution memory exec,
103
+ bytes32 account
104
+ ) internal returns (bytes memory output, bytes memory transactions) {
105
+ if (exec.budget == 0 && Cursors.initial(exec.writers)) return ("", "");
106
+
107
+ output = close(exec);
108
+ uint amount = exec.refundValue(account, nativeAsset);
109
+ if (amount != 0) {
110
+ emit Received(account, nativeAsset, amount, Actions.Refund, 0);
111
+ }
112
+
113
+ transactions = exec.finishTransactions();
114
+ }
120
115
  }
package/commands/Burn.sol CHANGED
@@ -2,6 +2,8 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { Execution, Executions, CommandBase, Lanes, Specs } from "./Base.sol";
5
+ import {Action} from "../annotations/Action.sol";
6
+ import {Actions} from "../utils/Actions.sol";
5
7
  using Executions for Execution;
6
8
 
7
9
  /// @notice Hook implemented by hosts that burn account assets.
@@ -18,11 +20,13 @@ abstract contract BurnHook {
18
20
  /// @title Burn
19
21
  /// @notice Command that irreversibly destroys each BALANCE state block via a virtual hook.
20
22
  /// Produces no output state.
21
- abstract contract Burn is CommandBase, BurnHook {
23
+ abstract contract Burn is CommandBase, BurnHook, Action {
22
24
  uint private immutable descriptor;
23
25
 
24
26
  constructor() {
25
- (, descriptor) = command("burn", Specs.Balance, Specs.Empty, Specs.Empty, 0, false, false);
27
+ uint id;
28
+ (id, descriptor) = command("burn", Specs.Balance, Specs.Empty, Specs.Empty, 0, false, false);
29
+ action(id, Actions.Burn);
26
30
  }
27
31
 
28
32
  /// @notice Burn each BALANCE block from the command state.
@@ -32,9 +36,9 @@ abstract contract Burn is CommandBase, BurnHook {
32
36
  function burn(
33
37
  bytes32 account,
34
38
  bytes calldata state,
35
- bytes calldata
39
+ bytes calldata input
36
40
  ) external onlyCommand returns (bytes memory, bytes memory) {
37
- Execution memory exec = openState(state, descriptor, 0);
41
+ Execution memory exec = openCommand(state, input, descriptor, 0);
38
42
 
39
43
  while (exec.more()) {
40
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,32 @@ 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
+ /// @return output Empty output state.
58
+ /// @return transactions Empty transaction stream.
59
+ function executeCreditAccount(
60
+ bytes32 account,
61
+ bytes memory state,
62
+ bytes calldata input
63
+ ) internal returns (bytes memory, bytes memory) {
64
+ if (input.length != 0) revert Executions.ZeroStride();
65
+ if (state.length == 0) revert Blocks.EmptyRun();
66
+
67
+ Reader memory reader = Readers.open(state);
68
+ while (reader.more()) {
69
+ (bytes32 asset, uint amount) = reader.unpackBalance();
70
+ creditAccount(account, asset, amount);
71
+ }
72
+
73
+ return ("", "");
74
+ }
75
+ }
@@ -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,39 @@ 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
+ /// @return output BALANCE block stream matching the debited amounts.
62
+ /// @return transactions Empty transaction stream.
63
+ function executeDebitAccount(
46
64
  bytes32 account,
47
- bytes calldata,
65
+ bytes memory state,
48
66
  bytes calldata input
49
- ) external onlyCommand returns (bytes memory, bytes memory) {
50
- return debitAccount(account, input);
67
+ ) internal returns (bytes memory, bytes memory) {
68
+ if (state.length != 0) revert Executions.ZeroStride();
69
+ if (input.length == 0) revert Blocks.EmptyRun();
70
+
71
+ Cur memory cur = Decoders.wrap(input);
72
+ uint count = input.length / Sizes.Amount;
73
+ bytes memory output = new bytes(count * Sizes.Balance);
74
+ uint i;
75
+
76
+ while (cur.more()) {
77
+ (bytes32 asset, uint amount) = cur.unpackAmount();
78
+ debitAccount(account, asset, amount);
79
+ Blocks.writeBalance(output, i, asset, amount);
80
+ i += Sizes.Balance;
81
+ }
82
+
83
+ return (output, "");
51
84
  }
52
85
  }
@@ -2,6 +2,8 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { Execution, Executions, CommandBase, Lanes, Specs } from "./Base.sol";
5
+ import {Action} from "../annotations/Action.sol";
6
+ import {Actions} from "../utils/Actions.sol";
5
7
 
6
8
  using Executions for Execution;
7
9
 
@@ -32,11 +34,13 @@ abstract contract DepositPayableHook {
32
34
  /// @notice Command that receives externally sourced assets and records them as BALANCE state.
33
35
  /// Use `deposit` for assets arriving from outside the protocol (e.g. ERC-20 transfers, ETH).
34
36
  /// For internal balance deductions, use `debitAccount` instead.
35
- abstract contract Deposit is CommandBase, DepositHook {
37
+ abstract contract Deposit is CommandBase, DepositHook, Action {
36
38
  uint private immutable descriptor;
37
39
 
38
40
  constructor() {
39
- (, descriptor) = command("deposit", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
41
+ uint id;
42
+ (id, descriptor) = command("deposit", Specs.Empty, Specs.Amount, Specs.Balance, 0, false, false);
43
+ action(id, Actions.Deposit);
40
44
  }
41
45
 
42
46
  /// @notice Deposit AMOUNT input blocks into the command account and output matching BALANCE blocks.
@@ -45,10 +49,10 @@ abstract contract Deposit is CommandBase, DepositHook {
45
49
  /// @return Empty transaction stream.
46
50
  function deposit(
47
51
  bytes32 account,
48
- bytes calldata,
52
+ bytes calldata state,
49
53
  bytes calldata input
50
54
  ) external onlyCommand returns (bytes memory, bytes memory) {
51
- Execution memory exec = openInput(input, descriptor, 0);
55
+ Execution memory exec = openCommand(state, input, descriptor, 0);
52
56
 
53
57
  while (exec.more()) {
54
58
  (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
@@ -63,11 +67,13 @@ abstract contract Deposit is CommandBase, DepositHook {
63
67
  /// @title DepositPayable
64
68
  /// @notice Command that receives externally sourced assets and records them as BALANCE state.
65
69
  /// Use `depositPayable` when the hook needs tracked access to `msg.value` via a mutable budget.
66
- abstract contract DepositPayable is CommandBase, DepositPayableHook {
70
+ abstract contract DepositPayable is CommandBase, DepositPayableHook, Action {
67
71
  uint private immutable descriptor;
68
72
 
69
73
  constructor() {
70
- (, descriptor) = command("depositPayable", Specs.Empty, Specs.Amount, Specs.Balance, 0, true, false);
74
+ uint id;
75
+ (id, descriptor) = command("depositPayable", Specs.Empty, Specs.Amount, Specs.Balance, 0, true, false);
76
+ action(id, Actions.Deposit);
71
77
  }
72
78
 
73
79
  /// @notice Deposit AMOUNT input blocks with access to a mutable native-value budget.
@@ -76,10 +82,10 @@ abstract contract DepositPayable is CommandBase, DepositPayableHook {
76
82
  /// @return Remaining native value as a refund transaction stream.
77
83
  function depositPayable(
78
84
  bytes32 account,
79
- bytes calldata,
85
+ bytes calldata state,
80
86
  bytes calldata input
81
87
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
82
- Execution memory exec = openInput(input, descriptor, 0);
88
+ Execution memory exec = openCommand(state, input, descriptor, 0);
83
89
 
84
90
  while (exec.more()) {
85
91
  (bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
@@ -2,6 +2,8 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
5
+ import {Action} from "../annotations/Action.sol";
6
+ import {Actions} from "../utils/Actions.sol";
5
7
 
6
8
  using Executions for Execution;
7
9
 
@@ -19,11 +21,13 @@ abstract contract PayoutHook {
19
21
  /// @title Payout
20
22
  /// @notice Command that sinks BALANCE state blocks to matching ACCOUNT input blocks.
21
23
  /// Each BALANCE block is paired with one ACCOUNT block at the same position.
22
- abstract contract Payout is CommandBase, PayoutHook {
24
+ abstract contract Payout is CommandBase, PayoutHook, Action {
23
25
  uint private immutable descriptor;
24
26
 
25
27
  constructor() {
26
- (, descriptor) = command("payout", Specs.Balance, Specs.Account, Specs.Empty, 0, false, false);
28
+ uint id;
29
+ (id, descriptor) = command("payout", Specs.Balance, Specs.Account, Specs.Empty, 0, false, false);
30
+ action(id, Actions.Payout);
27
31
  }
28
32
 
29
33
  /// @notice Pay out BALANCE state blocks to matching ACCOUNT input blocks.
@@ -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);
@@ -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,77 @@
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
+ /// @title Settle
15
+ /// @notice Command that consumes POSITION state blocks through a virtual hook.
16
+ abstract contract Settle is CommandBase, SettleHook, Action {
17
+ uint private immutable descriptor;
18
+ uint private immutable id;
19
+
20
+ constructor() {
21
+ (id, descriptor) = command("settle", Specs.Position, Specs.Empty, Specs.Empty, 0, false, false);
22
+ action(id, Actions.Settle);
23
+ }
24
+
25
+ /// @notice Return the registered SETTLE command ID.
26
+ function settleId() internal view returns (uint) {
27
+ return id;
28
+ }
29
+
30
+ /// @notice Settle each POSITION block from the command state.
31
+ /// @param state POSITION block stream.
32
+ /// @return Empty output state.
33
+ /// @return Empty transaction stream.
34
+ function settle(
35
+ bytes32 account,
36
+ bytes calldata state,
37
+ bytes calldata input
38
+ ) external onlyCommand returns (bytes memory, bytes memory) {
39
+ Execution memory exec = openCommand(state, input, descriptor, 0);
40
+
41
+ while (exec.more()) {
42
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = exec.unpackPosition(Lanes.State);
43
+ settle(account, asset, amount, liability, debt);
44
+ }
45
+
46
+ return close(exec, account);
47
+ }
48
+ }
49
+
50
+ /// @title InternalSettle
51
+ /// @notice Extends the advertised settle command with memory-state pipeline dispatch.
52
+ /// @dev This adapter is not a separate command. It uses the command ID and settlement hook
53
+ /// inherited from `Settle` while accepting the state location used by `Pipeline`.
54
+ abstract contract InternalSettle is Settle {
55
+ /// @notice Execute the inherited settle command from an internal pipeline.
56
+ /// @param account Account for which each position is settled.
57
+ /// @param state POSITION block stream held in pipeline memory.
58
+ /// @param input Empty input required by the command schema.
59
+ /// @return output Empty output state.
60
+ /// @return transactions Empty transaction stream.
61
+ function executeSettle(
62
+ bytes32 account,
63
+ bytes memory state,
64
+ bytes calldata input
65
+ ) internal returns (bytes memory, bytes memory) {
66
+ if (input.length != 0) revert Executions.ZeroStride();
67
+ if (state.length == 0) revert Blocks.EmptyRun();
68
+
69
+ Reader memory reader = Readers.open(state);
70
+ while (reader.more()) {
71
+ (bytes32 asset, uint amount, bytes32 liability, uint debt) = reader.unpackPosition();
72
+ settle(account, asset, amount, liability, debt);
73
+ }
74
+
75
+ return ("", "");
76
+ }
77
+ }