@rootzero/contracts 0.9.8 → 1.0.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 (56) hide show
  1. package/Core.sol +1 -1
  2. package/Endpoints.sol +3 -1
  3. package/Events.sol +2 -0
  4. package/README.md +19 -18
  5. package/blocks/Cursors.sol +152 -162
  6. package/blocks/Keys.sol +10 -6
  7. package/blocks/Schema.sol +17 -9
  8. package/blocks/Writers.sol +4 -4
  9. package/commands/Burn.sol +7 -4
  10. package/commands/Credit.sol +9 -9
  11. package/commands/Debit.sol +7 -4
  12. package/commands/Deposit.sol +14 -8
  13. package/commands/Payout.sol +49 -0
  14. package/commands/Provision.sol +14 -8
  15. package/commands/Relay.sol +58 -0
  16. package/commands/Withdraw.sol +10 -9
  17. package/commands/admin/AllowAssets.sol +9 -4
  18. package/commands/admin/Allowance.sol +7 -4
  19. package/commands/admin/Appoint.sol +7 -4
  20. package/commands/admin/Authorize.sol +7 -4
  21. package/commands/admin/DenyAssets.sol +9 -4
  22. package/commands/admin/Destroy.sol +5 -3
  23. package/commands/admin/Dismiss.sol +7 -4
  24. package/commands/admin/Execute.sol +8 -5
  25. package/commands/admin/Init.sol +5 -3
  26. package/commands/admin/Unauthorize.sol +7 -4
  27. package/core/Host.sol +10 -1
  28. package/core/Pipeline.sol +4 -3
  29. package/core/Runtime.sol +3 -36
  30. package/core/Types.sol +1 -1
  31. package/docs/Schema.md +0 -1
  32. package/events/Admin.sol +11 -8
  33. package/events/Chain.sol +20 -0
  34. package/events/Command.sol +11 -8
  35. package/events/Peer.sol +5 -5
  36. package/events/Query.sol +2 -4
  37. package/events/Transfer.sol +22 -0
  38. package/guards/Revoke.sol +3 -3
  39. package/package.json +1 -1
  40. package/peer/AllowAssets.sol +5 -3
  41. package/peer/Allowance.sol +5 -3
  42. package/peer/BalancePull.sol +5 -3
  43. package/peer/DenyAssets.sol +5 -3
  44. package/peer/Dispatch.sol +51 -0
  45. package/peer/Pipe.sol +7 -5
  46. package/peer/Settle.sol +13 -8
  47. package/queries/Assets.sol +3 -3
  48. package/queries/Balances.sol +3 -3
  49. package/queries/Positions.sol +3 -3
  50. package/utils/Accounts.sol +6 -31
  51. package/utils/Actions.sol +11 -9
  52. package/utils/Assets.sol +21 -21
  53. package/utils/Ids.sol +12 -2
  54. package/utils/Layout.sol +21 -17
  55. package/utils/Utils.sol +2 -2
  56. package/commands/Transfer.sol +0 -54
package/blocks/Schema.sol CHANGED
@@ -11,8 +11,11 @@ pragma solidity ^0.8.33;
11
11
  // - a block without braces has no payload, e.g. `#unit`
12
12
  // - commas separate siblings at every level
13
13
  // - braces define parent-child boundaries
14
- // - top-level item 0 is the prime item; later top-level items are globals
15
- // - prime items may repeat at top level for batching
14
+ // - command requests start with the input run when the request schema is non-empty
15
+ // - postcheck command requests include a constraint run after the input run; if the
16
+ // request schema is empty, the constraint run starts the request
17
+ // - command state starts with the active state run; trailing state globals may follow
18
+ // - run items may repeat at top level for batching
16
19
  // - `maybe #x { ... }` marks an optional block item
17
20
  // - `many #x { ... }` emits one generic list block containing repeated `#x` items
18
21
  // - fixed fields are packed in declaration order
@@ -30,8 +33,8 @@ pragma solidity ^0.8.33;
30
33
  // - while a balance or custody is in-flight as pipeline state, it is not simultaneously persisted
31
34
  // in another ledger/store by this protocol
32
35
  // - commands must preserve, transform, settle, or intentionally consume pipeline state
33
- // - request blocks such as `amount(...)`, `allocation(...)`, `allowance(...)`, `payout(...)`,
34
- // `minimum(...)`, and `maximum(...)` express intent, constraints, or references
36
+ // - request blocks such as `amount(...)`, `allocation(...)`, and `allowance(...)`
37
+ // express intent, constraints, or references
35
38
  // - request and value/response blocks are not live state
36
39
  //
37
40
  // Signed blocks:
@@ -50,19 +53,20 @@ library Schemas {
50
53
  string constant Node = "#node { uint id }";
51
54
  string constant Account = "#account { bytes32 account }";
52
55
  string constant Asset = "#asset { bytes32 asset, bytes32 meta }";
53
- string constant Balance = "#balance { bytes32 asset, bytes32 meta, uint amount }";
54
56
  string constant Amount = "#amount { bytes32 asset, bytes32 meta, uint amount }";
55
- string constant Minimum = "#minimum { bytes32 asset, bytes32 meta, uint amount }";
56
- string constant Maximum = "#maximum { bytes32 asset, bytes32 meta, uint amount }";
57
+ string constant Balance = "#balance { bytes32 asset, bytes32 meta, uint amount }";
58
+ string constant BalanceLimit = "#balanceLimit { bytes32 asset, bytes32 meta, uint min, uint max }";
57
59
  string constant Custody = "#custody { uint host, bytes32 asset, bytes32 meta, uint amount }";
58
- string constant Payout = "#payout { bytes32 account, bytes32 asset, bytes32 meta, uint amount }";
60
+ string constant CustodyLimit = "#custodyLimit { uint host, bytes32 asset, bytes32 meta, uint min, uint max }";
59
61
  string constant Allocation = "#allocation { uint host, bytes32 asset, bytes32 meta, uint amount }";
60
62
  string constant Allowance = "#allowance { uint host, bytes32 asset, bytes32 meta, uint amount }";
61
63
  string constant Transaction = "#transaction { bytes32 from, bytes32 to, bytes32 asset, bytes32 meta, uint amount }";
62
64
  string constant Context = "#context { bytes32 account, #bytes as state, #bytes as request }";
63
- string constant Pipe = "#pipe { uint value, #context { bytes32 account, #bytes as state, #bytes as request } }";
65
+ string constant Pipe = "#pipe { uint value, #context { bytes32 account, #bytes as state, #bytes as steps } }";
64
66
  string constant Call = "#call { uint target, uint value, #bytes as payload }";
65
67
  string constant Step = "#step { uint target, uint value, #bytes as request }";
68
+ string constant Relay = "#relay { uint chain, uint endowment, #bytes as steps }";
69
+ string constant Dispatch = "#dispatch { uint chain, uint endowment, #bytes as payload }";
66
70
  string constant Bounty = "#bounty { uint amount, bytes32 relayer }";
67
71
  string constant Fee = "#fee { uint amount }";
68
72
  string constant Auth = "#auth { uint cid, uint deadline, #bytes as proof }";
@@ -113,12 +117,16 @@ library Sizes {
113
117
  uint constant Amount = B96;
114
118
  /// @dev BALANCE block: 8 header + 32 asset + 32 meta + 32 amount = 104 bytes
115
119
  uint constant Balance = B96;
120
+ /// @dev BALANCE_LIMIT block: 8 header + 32 asset + 32 meta + 32 min + 32 max = 136 bytes
121
+ uint constant BalanceLimit = B128;
116
122
  /// @dev FEE block: 8 header + 32 amount = 40 bytes
117
123
  uint constant Fee = B32;
118
124
  /// @dev BOUNTY block: 8 header + 32 amount + 32 relayer = 72 bytes
119
125
  uint constant Bounty = B64;
120
126
  /// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 meta + 32 amount = 136 bytes
121
127
  uint constant HostAmount = B128;
128
+ /// @dev CUSTODY_LIMIT block: 8 header + 32 host + 32 asset + 32 meta + 32 min + 32 max = 168 bytes
129
+ uint constant CustodyLimit = B160;
122
130
  /// @dev TRANSACTION block: 8 header + 32 from + 32 to + 32 asset + 32 meta + 32 amount = 168 bytes
123
131
  uint constant Transaction = B160;
124
132
  }
@@ -894,16 +894,16 @@ library Writers {
894
894
  /// @param value Native value assigned to the pipe.
895
895
  /// @param account Command account identifier.
896
896
  /// @param state Raw nested state payload.
897
- /// @param request Raw nested request payload.
897
+ /// @param steps Raw nested step payload.
898
898
  function appendPipe(
899
899
  Writer memory writer,
900
900
  uint value,
901
901
  bytes32 account,
902
902
  bytes memory state,
903
- bytes memory request
903
+ bytes memory steps
904
904
  ) internal pure {
905
905
  uint i = writer.i;
906
- uint len = 64 + 3 * Sizes.Header + state.length + request.length;
906
+ uint len = 64 + 3 * Sizes.Header + state.length + steps.length;
907
907
  uint next = i + Sizes.Header + len;
908
908
  i = reserve(writer, next, next);
909
909
 
@@ -912,7 +912,7 @@ library Writers {
912
912
  mstore(add(p, 0x08), value)
913
913
  }
914
914
 
915
- writeBlock32BytesBytes(writer.dst, i + Sizes.Header + 32, Keys.Context, account, state, request);
915
+ writeBlock32BytesBytes(writer.dst, i + Sizes.Header + 32, Keys.Context, account, state, steps);
916
916
  }
917
917
 
918
918
  /// @notice Append a STATUS form block.
package/commands/Burn.sol CHANGED
@@ -25,18 +25,21 @@ abstract contract Burn is CommandBase, BurnHook {
25
25
  uint internal immutable burnId = commandId(NAME);
26
26
 
27
27
  constructor() {
28
- emit Command(host, burnId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false);
28
+ emit Command(host, burnId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
29
29
  }
30
30
 
31
+ /// @notice Burn each BALANCE block from the command state.
32
+ /// @param c Command context; `c.state` must contain BALANCE blocks.
33
+ /// @return Empty output state.
31
34
  function burn(CommandContext calldata c) external onlyCommand returns (bytes memory) {
32
- (Cur memory state, ) = cursor(c.state, 1);
35
+ (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
33
36
 
34
- while (state.i < state.bound) {
37
+ while (state.i < state.len) {
35
38
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
36
39
  burn(c.account, asset, meta, amount);
37
40
  }
38
41
 
39
- state.close();
42
+ state.complete();
40
43
  return "";
41
44
  }
42
45
  }
@@ -2,7 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandBase, CommandContext, Keys } from "./Base.sol";
5
- import { Cursors, Cur, Schemas } from "../Cursors.sol";
5
+ import { Cursors, Cur } from "../Cursors.sol";
6
6
 
7
7
  using Cursors for Cur;
8
8
 
@@ -19,29 +19,29 @@ abstract contract CreditAccountHook {
19
19
  /// @title CreditAccount
20
20
  /// @notice Command that delivers BALANCE state blocks to an account via a virtual hook.
21
21
  /// Use for internally recording credits that have already been settled externally.
22
- /// An optional ACCOUNT block in the request overrides the default `c.account` destination.
23
22
  abstract contract CreditAccount is CommandBase, CreditAccountHook {
24
23
  string private constant NAME = "creditAccount";
25
- string private constant REQUEST = string.concat(Schemas.Unit, ", maybe ", Schemas.Account);
26
24
 
27
25
  uint internal immutable creditAccountId = commandId(NAME);
28
26
 
29
27
  constructor() {
30
- emit Command(host, creditAccountId, NAME, "0:1:0", REQUEST, Keys.Balance, Keys.Empty, false);
28
+ emit Command(host, creditAccountId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
31
29
  }
32
30
 
31
+ /// @notice Credit each BALANCE block from the command state to the command account.
32
+ /// @param c Command context; `c.state` must contain BALANCE blocks.
33
+ /// @return Empty output state.
33
34
  function creditAccount(
34
35
  CommandContext calldata c
35
36
  ) external onlyCommand returns (bytes memory) {
36
- (Cur memory state, ) = cursor(c.state, 1);
37
- bytes32 to = Cursors.resolveAccount(c.request, c.account);
37
+ (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
38
38
 
39
- while (state.i < state.bound) {
39
+ while (state.i < state.len) {
40
40
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
41
- creditAccount(to, asset, meta, amount);
41
+ creditAccount(c.account, asset, meta, amount);
42
42
  }
43
43
 
44
- state.close();
44
+ state.complete();
45
45
  return "";
46
46
  }
47
47
  }
@@ -27,26 +27,29 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
27
27
  uint internal immutable debitAccountId = commandId(NAME);
28
28
 
29
29
  constructor() {
30
- emit Command(host, debitAccountId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
30
+ emit Command(host, debitAccountId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, false);
31
31
  }
32
32
 
33
33
  /// @notice Override to customize request parsing or batching for debits.
34
34
  /// The default implementation iterates AMOUNT blocks, calls
35
35
  /// `debitAccount`, and emits matching BALANCE blocks.
36
36
  function debitAccount(bytes32 account, bytes calldata request) internal virtual returns (bytes memory) {
37
- (Cur memory input, uint groups) = cursor(request, 1);
37
+ (Cur memory input, uint groups, ) = Cursors.init(request, 0, 1);
38
38
  Writer memory writer = Writers.allocBalances(groups);
39
39
 
40
- while (input.i < input.bound) {
40
+ while (input.i < input.len) {
41
41
  (bytes32 asset, bytes32 meta, uint amount) = input.unpackAmount();
42
42
  debitAccount(account, asset, meta, amount);
43
43
  writer.appendBalance(asset, meta, amount);
44
44
  }
45
45
 
46
- input.close();
46
+ input.complete();
47
47
  return writer.finish();
48
48
  }
49
49
 
50
+ /// @notice Debit AMOUNT request blocks from the command account and output matching BALANCE blocks.
51
+ /// @param c Command context; `c.request` must contain AMOUNT blocks.
52
+ /// @return BALANCE block stream matching the debited amounts.
50
53
  function debitAccount(
51
54
  CommandContext calldata c
52
55
  ) external onlyCommand returns (bytes memory) {
@@ -42,22 +42,25 @@ abstract contract Deposit is CommandBase, DepositHook {
42
42
  uint internal immutable depositId = commandId(NAME);
43
43
 
44
44
  constructor() {
45
- emit Command(host, depositId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false);
45
+ emit Command(host, depositId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, false);
46
46
  }
47
47
 
48
+ /// @notice Deposit AMOUNT request blocks into the command account and output matching BALANCE blocks.
49
+ /// @param c Command context; `c.request` must contain AMOUNT blocks.
50
+ /// @return BALANCE block stream matching the deposited amounts.
48
51
  function deposit(
49
52
  CommandContext calldata c
50
53
  ) external onlyCommand returns (bytes memory) {
51
- (Cur memory request, uint groups) = cursor(c.request, 1);
54
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
52
55
  Writer memory writer = Writers.allocBalances(groups);
53
56
 
54
- while (request.i < request.bound) {
57
+ while (request.i < request.len) {
55
58
  (bytes32 asset, bytes32 meta, uint amount) = request.unpackAmount();
56
59
  deposit(c.account, asset, meta, amount);
57
60
  writer.appendBalance(asset, meta, amount);
58
61
  }
59
62
 
60
- request.close();
63
+ request.complete();
61
64
  return writer.finish();
62
65
  }
63
66
  }
@@ -71,24 +74,27 @@ abstract contract DepositPayable is CommandBase, Payable, DepositPayableHook {
71
74
  uint internal immutable depositPayableId = commandId(NAME);
72
75
 
73
76
  constructor() {
74
- emit Command(host, depositPayableId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, true);
77
+ emit Command(host, depositPayableId, NAME, "1:0:1", Schemas.Amount, Keys.Empty, Keys.Balance, false, true);
75
78
  }
76
79
 
80
+ /// @notice Deposit AMOUNT request blocks with access to a mutable native-value budget.
81
+ /// @param c Command context; `c.request` must contain AMOUNT blocks.
82
+ /// @return BALANCE block stream matching the deposited amounts.
77
83
  function depositPayable(
78
84
  CommandContext calldata c
79
85
  ) external payable onlyCommand returns (bytes memory) {
80
- (Cur memory request, uint groups) = cursor(c.request, 1);
86
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
81
87
  Writer memory writer = Writers.allocBalances(groups);
82
88
  Budget memory budget = valueBudget();
83
89
 
84
- while (request.i < request.bound) {
90
+ while (request.i < request.len) {
85
91
  (bytes32 asset, bytes32 meta, uint amount) = request.unpackAmount();
86
92
  deposit(c.account, asset, meta, amount, budget);
87
93
  writer.appendBalance(asset, meta, amount);
88
94
  }
89
95
 
90
96
  settleValue(c.account, budget);
91
- request.close();
97
+ request.complete();
92
98
  return writer.finish();
93
99
  }
94
100
  }
@@ -0,0 +1,49 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {CommandContext, CommandBase, Keys} from "./Base.sol";
5
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
6
+ import {Accounts} from "../utils/Accounts.sol";
7
+
8
+ using Cursors for Cur;
9
+
10
+ abstract contract PayoutHook {
11
+ /// @notice Override to pay `amount` from `account` to `to`.
12
+ /// Called once per paired BALANCE state block and ACCOUNT request block.
13
+ /// @param account Source account identifier.
14
+ /// @param to Destination account identifier.
15
+ /// @param asset Asset identifier.
16
+ /// @param meta Asset metadata slot.
17
+ /// @param amount Amount to pay out.
18
+ function payout(bytes32 account, bytes32 to, bytes32 asset, bytes32 meta, uint amount) internal virtual;
19
+ }
20
+
21
+ /// @title Payout
22
+ /// @notice Command that sinks BALANCE state blocks to matching ACCOUNT request blocks.
23
+ /// Each BALANCE block is paired with one ACCOUNT block at the same position.
24
+ abstract contract Payout is CommandBase, PayoutHook {
25
+ string private constant NAME = "payout";
26
+
27
+ uint internal immutable payoutId = commandId(NAME);
28
+
29
+ constructor() {
30
+ emit Command(host, payoutId, NAME, "1:1:0", Schemas.Account, Keys.Balance, Keys.Empty, false, false);
31
+ }
32
+
33
+ /// @notice Pay out BALANCE state blocks to matching ACCOUNT request blocks.
34
+ /// @param c Command context; `c.state` must contain BALANCE blocks and `c.request` matching ACCOUNT blocks.
35
+ /// @return Empty output state.
36
+ function payout(CommandContext calldata c) external onlyCommand returns (bytes memory) {
37
+ (Cur memory state, uint groups, ) = Cursors.init(c.state, 0, 1);
38
+ (Cur memory request, ) = Cursors.init(c.request, 0, 1, groups);
39
+
40
+ while (state.i < state.len) {
41
+ (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
42
+ bytes32 to = Accounts.ensure(request.unpackAccount());
43
+ payout(c.account, to, asset, meta, amount);
44
+ }
45
+
46
+ state.complete();
47
+ return "";
48
+ }
49
+ }
@@ -38,20 +38,23 @@ abstract contract Provision is CommandBase, ProvisionHook {
38
38
  uint internal immutable provisionId = commandId(NAME);
39
39
 
40
40
  constructor() {
41
- emit Command(host, provisionId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false);
41
+ emit Command(host, provisionId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false, false);
42
42
  }
43
43
 
44
+ /// @notice Provision ALLOCATION request blocks and output matching CUSTODY state blocks.
45
+ /// @param c Command context; `c.request` must contain ALLOCATION blocks.
46
+ /// @return CUSTODY block stream matching the provisioned allocations.
44
47
  function provision(CommandContext calldata c) external onlyCommand returns (bytes memory) {
45
- (Cur memory request, uint groups) = cursor(c.request, 1);
48
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
46
49
  Writer memory writer = Writers.allocCustodies(groups);
47
50
 
48
- while (request.i < request.bound) {
51
+ while (request.i < request.len) {
49
52
  HostAmount memory allocation = request.unpackAllocationValue();
50
53
  provision(c.account, allocation);
51
54
  writer.appendCustody(allocation);
52
55
  }
53
56
 
54
- request.close();
57
+ request.complete();
55
58
  return writer.finish();
56
59
  }
57
60
  }
@@ -66,24 +69,27 @@ abstract contract ProvisionPayable is CommandBase, Payable, ProvisionPayableHook
66
69
  uint internal immutable provisionPayableId = commandId(NAME);
67
70
 
68
71
  constructor() {
69
- emit Command(host, provisionPayableId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, true);
72
+ emit Command(host, provisionPayableId, NAME, "1:0:1", Schemas.Allocation, Keys.Empty, Keys.Custody, false, true);
70
73
  }
71
74
 
75
+ /// @notice Provision ALLOCATION request blocks with access to a mutable native-value budget.
76
+ /// @param c Command context; `c.request` must contain ALLOCATION blocks.
77
+ /// @return CUSTODY block stream matching the provisioned allocations.
72
78
  function provisionPayable(
73
79
  CommandContext calldata c
74
80
  ) external payable onlyCommand returns (bytes memory) {
75
- (Cur memory request, uint groups) = cursor(c.request, 1);
81
+ (Cur memory request, uint groups, ) = Cursors.init(c.request, 0, 1);
76
82
  Writer memory writer = Writers.allocCustodies(groups);
77
83
  Budget memory budget = valueBudget();
78
84
 
79
- while (request.i < request.bound) {
85
+ while (request.i < request.len) {
80
86
  HostAmount memory allocation = request.unpackAllocationValue();
81
87
  provision(c.account, allocation, budget);
82
88
  writer.appendCustody(allocation);
83
89
  }
84
90
 
85
91
  settleValue(c.account, budget);
86
- request.close();
92
+ request.complete();
87
93
  return writer.finish();
88
94
  }
89
95
  }
@@ -0,0 +1,58 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {CommandBase, CommandContext, Keys} from "./Base.sol";
5
+ import {Payable} from "../core/Payable.sol";
6
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
+ import {Budget} from "../utils/Value.sol";
8
+
9
+ using Cursors for Cur;
10
+
11
+ abstract contract RelayPayableHook {
12
+ /// @notice Override to relay `steps` to `chain` with the current account and state.
13
+ /// @param chain Destination chain node ID.
14
+ /// @param endowment Native value requested for the destination pipe. The hook
15
+ /// decides how much source-chain budget must be spent to fund this value on
16
+ /// the destination chain.
17
+ /// @param account Command account identifier.
18
+ /// @param state Current command state block stream.
19
+ /// @param steps Embedded destination step block stream.
20
+ /// @param budget Source-chain native-value budget available for transport
21
+ /// fees and destination endowment funding.
22
+ function relay(
23
+ uint chain,
24
+ uint endowment,
25
+ bytes32 account,
26
+ bytes calldata state,
27
+ bytes calldata steps,
28
+ Budget memory budget
29
+ ) internal virtual;
30
+ }
31
+
32
+ /// @title RelayPayable
33
+ /// @notice Command that forwards one RELAY block to a host-defined relay hook.
34
+ /// Reverts unless the request contains exactly one RELAY block, preventing
35
+ /// the same state from being duplicated across multiple relays.
36
+ /// Produces no output state.
37
+ abstract contract RelayPayable is CommandBase, Payable, RelayPayableHook {
38
+ string private constant NAME = "relayPayable";
39
+
40
+ uint internal immutable relayPayableId = commandId(NAME);
41
+
42
+ constructor() {
43
+ emit Command(host, relayPayableId, NAME, "1:0:0", Schemas.Relay, Keys.Any, Keys.Empty, false, true);
44
+ }
45
+
46
+ /// @notice Relay one RELAY request block with the command account and current state.
47
+ /// @param c Command context; `c.request` must contain exactly one RELAY block.
48
+ /// @return output Empty output state.
49
+ function relayPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory output) {
50
+ (Cur memory request, ) = Cursors.init(c.request, 0, 1, 1);
51
+ Budget memory budget = valueBudget();
52
+
53
+ (uint chain, uint endowment, bytes calldata steps) = request.unpackRelay();
54
+ relay(chain, endowment, c.account, c.state, steps, budget);
55
+ request.complete();
56
+ return "";
57
+ }
58
+ }
@@ -2,13 +2,13 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandContext, CommandBase, Keys } from "./Base.sol";
5
- import { Cursors, Cur, Schemas } from "../Cursors.sol";
5
+ import { Cursors, Cur } from "../Cursors.sol";
6
6
  using Cursors for Cur;
7
7
 
8
8
  abstract contract WithdrawHook {
9
9
  /// @notice Override to send funds to `account`.
10
10
  /// Called once per BALANCE block in state.
11
- /// @param account Destination account identifier (resolved from ACCOUNT block or caller).
11
+ /// @param account Destination account identifier.
12
12
  /// @param asset Asset identifier.
13
13
  /// @param meta Asset metadata slot.
14
14
  /// @param amount Amount to deliver.
@@ -21,26 +21,27 @@ abstract contract WithdrawHook {
21
21
  /// For internal balance credits, use `creditAccount` instead.
22
22
  abstract contract Withdraw is CommandBase, WithdrawHook {
23
23
  string private constant NAME = "withdraw";
24
- string private constant REQUEST = string.concat(Schemas.Unit, ", maybe ", Schemas.Account);
25
24
 
26
25
  uint internal immutable withdrawId = commandId(NAME);
27
26
 
28
27
  constructor() {
29
- emit Command(host, withdrawId, NAME, "0:1:0", REQUEST, Keys.Balance, Keys.Empty, false);
28
+ emit Command(host, withdrawId, NAME, "0:1:0", "", Keys.Balance, Keys.Empty, false, false);
30
29
  }
31
30
 
31
+ /// @notice Withdraw each BALANCE block from the command state to the command account.
32
+ /// @param c Command context; `c.state` must contain BALANCE blocks.
33
+ /// @return Empty output state.
32
34
  function withdraw(
33
35
  CommandContext calldata c
34
36
  ) external onlyCommand returns (bytes memory) {
35
- (Cur memory state, ) = cursor(c.state, 1);
36
- bytes32 to = Cursors.resolveAccount(c.request, c.account);
37
+ (Cur memory state, , ) = Cursors.init(c.state, 0, 1);
37
38
 
38
- while (state.i < state.bound) {
39
+ while (state.i < state.len) {
39
40
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
40
- withdraw(to, asset, meta, amount);
41
+ withdraw(c.account, asset, meta, amount);
41
42
  }
42
43
 
43
- state.close();
44
+ state.complete();
44
45
  return "";
45
46
  }
46
47
  }
@@ -9,6 +9,8 @@ using Cursors for Cur;
9
9
  abstract contract AllowAssetsHook {
10
10
  /// @dev Override to allow a single asset/meta pair.
11
11
  /// Called once per ASSET block in the request.
12
+ /// @param asset Asset identifier.
13
+ /// @param meta Asset metadata slot.
12
14
  function allowAsset(bytes32 asset, bytes32 meta) internal virtual;
13
15
  }
14
16
 
@@ -21,20 +23,23 @@ abstract contract AllowAssets is CommandBase, AdminEvent, AllowAssetsHook {
21
23
  uint internal immutable allowAssetsId = commandId(NAME);
22
24
 
23
25
  constructor() {
24
- emit Admin(host, allowAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
26
+ emit Admin(host, allowAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false, false);
25
27
  }
26
28
 
29
+ /// @notice Allow each ASSET block in the admin request.
30
+ /// @param c Admin command context; `c.request` must contain ASSET blocks.
31
+ /// @return Empty output state.
27
32
  function allowAssets(
28
33
  CommandContext calldata c
29
34
  ) external onlyAdmin(c.account) returns (bytes memory) {
30
- (Cur memory request, ) = cursor(c.request, 1);
35
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
31
36
 
32
- while (request.i < request.bound) {
37
+ while (request.i < request.len) {
33
38
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
34
39
  allowAsset(asset, meta);
35
40
  }
36
41
 
37
- request.close();
42
+ request.complete();
38
43
  return "";
39
44
  }
40
45
  }
@@ -26,18 +26,21 @@ abstract contract Allowance is CommandBase, AdminEvent, AllowanceHook {
26
26
  uint internal immutable allowanceId = commandId(NAME);
27
27
 
28
28
  constructor() {
29
- emit Admin(host, allowanceId, NAME, "1:0:0", Schemas.Allowance, Keys.Empty, Keys.Empty, false);
29
+ emit Admin(host, allowanceId, NAME, "1:0:0", Schemas.Allowance, Keys.Empty, Keys.Empty, false, false);
30
30
  }
31
31
 
32
+ /// @notice Apply each ALLOWANCE block in the admin request.
33
+ /// @param c Admin command context; `c.request` must contain ALLOWANCE blocks.
34
+ /// @return Empty output state.
32
35
  function allowance(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
33
- (Cur memory request, ) = cursor(c.request, 1);
36
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
34
37
 
35
- while (request.i < request.bound) {
38
+ while (request.i < request.len) {
36
39
  (uint peer, bytes32 asset, bytes32 meta, uint amount) = request.unpackAllowance();
37
40
  allowance(peer, asset, meta, amount);
38
41
  }
39
42
 
40
- request.close();
43
+ request.complete();
41
44
  return "";
42
45
  }
43
46
  }
@@ -16,20 +16,23 @@ abstract contract Appoint is CommandBase, AdminEvent {
16
16
  uint internal immutable appointId = commandId(NAME);
17
17
 
18
18
  constructor() {
19
- emit Admin(host, appointId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, appointId, NAME, "1:0:0", Schemas.Account, Keys.Empty, Keys.Empty, false, false);
20
20
  }
21
21
 
22
+ /// @notice Appoint each ACCOUNT block in the admin request as a guardian.
23
+ /// @param c Admin command context; `c.request` must contain ACCOUNT blocks.
24
+ /// @return Empty output state.
22
25
  function appoint(
23
26
  CommandContext calldata c
24
27
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, ) = cursor(c.request, 1);
28
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
26
29
 
27
- while (request.i < request.bound) {
30
+ while (request.i < request.len) {
28
31
  bytes32 account = request.unpackAccount();
29
32
  setGuardian(account, true);
30
33
  }
31
34
 
32
- request.close();
35
+ request.complete();
33
36
  return "";
34
37
  }
35
38
  }
@@ -16,20 +16,23 @@ abstract contract Authorize is CommandBase, AdminEvent {
16
16
  uint internal immutable authorizeId = commandId(NAME);
17
17
 
18
18
  constructor() {
19
- emit Admin(host, authorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false);
19
+ emit Admin(host, authorizeId, NAME, "1:0:0", Schemas.Node, Keys.Empty, Keys.Empty, false, false);
20
20
  }
21
21
 
22
+ /// @notice Authorize each NODE block in the admin request.
23
+ /// @param c Admin command context; `c.request` must contain NODE blocks.
24
+ /// @return Empty output state.
22
25
  function authorize(
23
26
  CommandContext calldata c
24
27
  ) external onlyAdmin(c.account) returns (bytes memory) {
25
- (Cur memory request, ) = cursor(c.request, 1);
28
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
26
29
 
27
- while (request.i < request.bound) {
30
+ while (request.i < request.len) {
28
31
  uint node = request.unpackNode();
29
32
  setNode(node, true);
30
33
  }
31
34
 
32
- request.close();
35
+ request.complete();
33
36
  return "";
34
37
  }
35
38
  }
@@ -9,6 +9,8 @@ using Cursors for Cur;
9
9
  abstract contract DenyAssetsHook {
10
10
  /// @dev Override to deny a single asset/meta pair.
11
11
  /// Called once per ASSET block in the request.
12
+ /// @param asset Asset identifier.
13
+ /// @param meta Asset metadata slot.
12
14
  function denyAsset(bytes32 asset, bytes32 meta) internal virtual;
13
15
  }
14
16
 
@@ -21,20 +23,23 @@ abstract contract DenyAssets is CommandBase, AdminEvent, DenyAssetsHook {
21
23
  uint internal immutable denyAssetsId = commandId(NAME);
22
24
 
23
25
  constructor() {
24
- emit Admin(host, denyAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false);
26
+ emit Admin(host, denyAssetsId, NAME, "1:0:0", Schemas.Asset, Keys.Empty, Keys.Empty, false, false);
25
27
  }
26
28
 
29
+ /// @notice Deny each ASSET block in the admin request.
30
+ /// @param c Admin command context; `c.request` must contain ASSET blocks.
31
+ /// @return Empty output state.
27
32
  function denyAssets(
28
33
  CommandContext calldata c
29
34
  ) external onlyAdmin(c.account) returns (bytes memory) {
30
- (Cur memory request, ) = cursor(c.request, 1);
35
+ (Cur memory request, , ) = Cursors.init(c.request, 0, 1);
31
36
 
32
- while (request.i < request.bound) {
37
+ while (request.i < request.len) {
33
38
  (bytes32 asset, bytes32 meta) = request.unpackAsset();
34
39
  denyAsset(asset, meta);
35
40
  }
36
41
 
37
- request.close();
42
+ request.complete();
38
43
  return "";
39
44
  }
40
45
  }