@rootzero/contracts 1.9.0 → 1.10.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 (43) hide show
  1. package/CHANGELOG.md +21 -2
  2. package/Core.sol +2 -1
  3. package/Endpoints.sol +2 -3
  4. package/Events.sol +1 -1
  5. package/README.md +1 -1
  6. package/blocks/Cursors.sol +22 -22
  7. package/commands/Burn.sol +1 -1
  8. package/commands/Credit.sol +1 -1
  9. package/commands/Debit.sol +1 -1
  10. package/commands/Deposit.sol +2 -2
  11. package/commands/Payout.sol +2 -2
  12. package/commands/Provision.sol +2 -2
  13. package/commands/Recover.sol +10 -2
  14. package/commands/Relay.sol +12 -2
  15. package/commands/Withdraw.sol +1 -1
  16. package/commands/admin/AllowAssets.sol +1 -1
  17. package/commands/admin/Allowance.sol +1 -1
  18. package/commands/admin/Appoint.sol +1 -1
  19. package/commands/admin/Authorize.sol +1 -1
  20. package/commands/admin/DenyAssets.sol +1 -1
  21. package/commands/admin/Dismiss.sol +1 -1
  22. package/commands/admin/Execute.sol +1 -1
  23. package/commands/admin/Label.sol +1 -1
  24. package/commands/admin/Unauthorize.sol +1 -1
  25. package/core/Payable.sol +7 -0
  26. package/core/Pipeline.sol +1 -1
  27. package/core/Portal.sol +19 -48
  28. package/events/Recovered.sol +17 -0
  29. package/guards/Revoke.sol +1 -1
  30. package/package.json +1 -1
  31. package/ports/AllowAssets.sol +1 -1
  32. package/ports/Allowance.sol +1 -1
  33. package/ports/Credit.sol +1 -1
  34. package/ports/Debit.sol +1 -1
  35. package/ports/DenyAssets.sol +1 -1
  36. package/ports/Dispatch.sol +2 -2
  37. package/ports/Pipe.sol +1 -1
  38. package/ports/Redeem.sol +1 -1
  39. package/ports/Settle.sol +1 -1
  40. package/queries/Assets.sol +1 -1
  41. package/queries/Balances.sol +1 -1
  42. package/queries/Positions.sol +1 -1
  43. package/events/Resolved.sol +0 -17
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  Until the protocol reaches integration-stable status, minor versions may include
4
4
  breaking API changes. Breaking changes are called out explicitly.
5
5
 
6
+ ## Unreleased
7
+
8
+ ## 1.10.0
9
+
10
+ ### Breaking Changes
11
+
12
+ - Moved `RecoverHook` from `Portal.sol` to `commands/Recover.sol`.
13
+ - Moved `RoutePayableHook` from `Portal.sol` to `commands/Relay.sol`.
14
+ - Renamed Portal helpers to `forward` / `retry`; hosts now explicitly bridge
15
+ the `RecoverPayable` hook to Portal retry behavior.
16
+ - Renamed the generic `Resolved` event to `Recovered`.
17
+ - Portal no longer emits `Recovered` when retrying an undelivered witness.
18
+ - Renamed `Cursors.exit` to `ensureAt` and renamed its position argument to
19
+ `pos`.
20
+ - Removed the redundant `next` return value from both `Cursors.init` overloads;
21
+ callers should use the returned cursor's `len` as the run boundary.
22
+ - Changed `Cursors.list` to require the expected current cursor position as
23
+ `pos` before entering the LIST block.
24
+
6
25
  ## 1.9.0
7
26
 
8
27
  ### Breaking Changes
@@ -18,10 +37,10 @@ breaking API changes. Breaking changes are called out explicitly.
18
37
  so routes can be removed by emitting zero status.
19
38
  - Added `Undelivered(uint indexed host, bytes32 key, bytes32 digest)` for portal
20
39
  messages that could not be delivered to their handler port.
21
- - Added `Resolved(uint indexed host, bytes32 key)` as the
40
+ - Added `Recovered(uint indexed host, bytes32 key)` as the
22
41
  matching event for resolved undelivered digests.
23
42
  - Removed the generic `Commitments` core mixin and `Commitment` event in favor
24
- of domain-specific events such as `Undelivered` and `Resolved`.
43
+ of domain-specific events such as `Undelivered` and `Recovered`.
25
44
 
26
45
  ## 1.8.0
27
46
 
package/Core.sol CHANGED
@@ -12,7 +12,8 @@ import { Host, IHostIntroduction } from "./core/Host.sol";
12
12
  import { CommandCalls, FailedCall, NodeCalls, PortCalls } from "./core/Calls.sol";
13
13
  import { Payable } from "./core/Payable.sol";
14
14
  import { Pipeline } from "./core/Pipeline.sol";
15
- import { Portal, RecoverHook, RoutePayableHook } from "./core/Portal.sol";
15
+ import { Portal } from "./core/Portal.sol";
16
+ import { RecoverHook } from "./commands/Recover.sol";
16
17
  import { AssetAmount, AccountAsset, AccountAmount, HostAmount, HostAccountAsset, HostAccountAmount, Tx } from "./core/Types.sol";
17
18
  import { Validator } from "./core/Validator.sol";
18
19
 
package/Endpoints.sol CHANGED
@@ -16,9 +16,8 @@ import { DebitAccount, DebitAccountHook } from "./commands/Debit.sol";
16
16
  import { Deposit, DepositHook, DepositPayable, DepositPayableHook } from "./commands/Deposit.sol";
17
17
  import { Payout, PayoutHook } from "./commands/Payout.sol";
18
18
  import { Provision, ProvisionHook, ProvisionPayable, ProvisionPayableHook } from "./commands/Provision.sol";
19
- import { RecoverPayable } from "./commands/Recover.sol";
20
- import { RelayPayable } from "./commands/Relay.sol";
21
- import { RecoverHook, RoutePayableHook } from "./core/Portal.sol";
19
+ import { RecoverHook, RecoverPayable } from "./commands/Recover.sol";
20
+ import { RelayPayable, RoutePayableHook } from "./commands/Relay.sol";
22
21
  import { Withdraw, WithdrawHook } from "./commands/Withdraw.sol";
23
22
 
24
23
  // Admin commands
package/Events.sol CHANGED
@@ -13,7 +13,7 @@ import { CommandEvent } from "./events/Command.sol";
13
13
  import { DispatchEvent } from "./events/Dispatch.sol";
14
14
  import { PositionEvent } from "./events/Position.sol";
15
15
  import { ReceivedEvent } from "./events/Received.sol";
16
- import { ResolvedEvent } from "./events/Resolved.sol";
16
+ import { RecoveredEvent } from "./events/Recovered.sol";
17
17
  import { EventEmitter } from "./events/Emitter.sol";
18
18
  import { GuardEvent } from "./events/Guard.sol";
19
19
  import { GuardianEvent } from "./events/Guardian.sol";
package/README.md CHANGED
@@ -220,7 +220,7 @@ the batch, call the hook, write the output run:
220
220
 
221
221
  ```solidity
222
222
  function deposit(CommandContext calldata c) external onlyCommand returns (bytes memory) {
223
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
223
+ (Cur memory request, uint groups) = Cursors.init(c.request, 1);
224
224
  Writer memory writer = Writers.allocBalances(groups);
225
225
 
226
226
  while (request.i < request.len) {
@@ -76,16 +76,14 @@ library Cursors {
76
76
  /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
77
77
  /// @return cur Cursor with `len` truncated to the end of the first run in `source`.
78
78
  /// @return groups Number of block groups in the run (`block count / group`).
79
- /// @return next Byte offset immediately after the run, relative to `source`.
80
79
  function init(
81
80
  bytes calldata source,
82
81
  uint group
83
- ) internal pure returns (Cur memory cur, uint groups, uint next) {
82
+ ) internal pure returns (Cur memory cur, uint groups) {
84
83
  cur = open(source);
85
84
  if (cur.i == cur.len) revert ZeroCursor();
86
85
  (bytes4 key, ) = cur.peek(cur.i);
87
86
  groups = cur.run(key, group);
88
- next = cur.len;
89
87
  }
90
88
 
91
89
  /// @notice Create a cursor over `source`, restrict it to its first grouped run, and require an exact group count.
@@ -93,14 +91,13 @@ library Cursors {
93
91
  /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
94
92
  /// @param expectedGroups Required number of groups in the run.
95
93
  /// @return cur Cursor with `len` truncated to the end of the first run in `source`.
96
- /// @return next Byte offset immediately after the run, relative to `source`.
97
94
  function init(
98
95
  bytes calldata source,
99
96
  uint group,
100
97
  uint expectedGroups
101
- ) internal pure returns (Cur memory cur, uint next) {
98
+ ) internal pure returns (Cur memory cur) {
102
99
  uint groups;
103
- (cur, groups, next) = init(source, group);
100
+ (cur, groups) = init(source, group);
104
101
  if (groups != expectedGroups) revert BadRatio();
105
102
  }
106
103
 
@@ -333,13 +330,16 @@ library Cursors {
333
330
  return find(cur, cur.i, key);
334
331
  }
335
332
 
336
- /// @notice Enter a List block at the current position and return the next offset.
333
+ /// @notice Enter a LIST block at the expected current position and return its next offset.
334
+ /// Reverts with `IncompleteCursor` if `cur.i` is not exactly `pos`.
337
335
  /// Advances `cur.i` past the list header so the list members can be parsed
338
336
  /// directly from the same cursor. The returned `next` is the byte offset
339
337
  /// immediately after the list payload, relative to the current cursor region.
340
- /// @param cur Cursor positioned at a list block; advanced past the 8-byte header.
338
+ /// @param cur Cursor expected to be positioned at a list block; advanced past the 8-byte header.
339
+ /// @param pos Expected current cursor position, relative to the cursor region.
341
340
  /// @return next Byte offset immediately after the list payload.
342
- function list(Cur memory cur) internal pure returns (uint next) {
341
+ function list(Cur memory cur, uint pos) internal pure returns (uint next) {
342
+ cur.ensureAt(pos);
343
343
  next = enter(cur, Keys.List, 0, 0);
344
344
  }
345
345
 
@@ -387,13 +387,13 @@ library Cursors {
387
387
  return maybeTake(cur, Keys.Data);
388
388
  }
389
389
 
390
- /// @notice Exit a nested region at an exact boundary.
391
- /// Reverts with `IncompleteCursor` if `end` exceeds the cursor region length
392
- /// or `cur.i != end`.
390
+ /// @notice Ensure the cursor is at an exact position.
391
+ /// Reverts with `IncompleteCursor` if `pos` exceeds the cursor region length
392
+ /// or `cur.i != pos`.
393
393
  /// @param cur Cursor to check.
394
- /// @param end Relative end offset of the nested region.
395
- function exit(Cur memory cur, uint end) internal pure {
396
- if (end > cur.len || cur.i != end) revert IncompleteCursor();
394
+ /// @param pos Relative byte offset the cursor must be positioned at.
395
+ function ensureAt(Cur memory cur, uint pos) internal pure {
396
+ if (pos > cur.len || cur.i != pos) revert IncompleteCursor();
397
397
  }
398
398
 
399
399
  /// @notice Assert that the cursor has consumed its entire source region.
@@ -747,7 +747,7 @@ library Cursors {
747
747
  id = cur.readUint();
748
748
  namespace = cur.read32();
749
749
  name = cur.unpackString();
750
- cur.exit(end);
750
+ cur.ensureAt(end);
751
751
  }
752
752
 
753
753
  /// @notice Consume a dynamic block with a single bytes32 payload.
@@ -1107,7 +1107,7 @@ library Cursors {
1107
1107
  target = uint(cur.read32());
1108
1108
  resources = uint(cur.read32());
1109
1109
  req = cur.unpackBytes();
1110
- cur.exit(end);
1110
+ cur.ensureAt(end);
1111
1111
  }
1112
1112
 
1113
1113
  /// @notice Consume a CALL block and return its target invocation fields.
@@ -1121,7 +1121,7 @@ library Cursors {
1121
1121
  target = uint(cur.read32());
1122
1122
  resources = uint(cur.read32());
1123
1123
  data = cur.unpackBytes();
1124
- cur.exit(end);
1124
+ cur.ensureAt(end);
1125
1125
  }
1126
1126
 
1127
1127
  /// @notice Consume a CONTEXT block and return its command context fields.
@@ -1137,7 +1137,7 @@ library Cursors {
1137
1137
  account = cur.read32();
1138
1138
  state = cur.unpackBytes();
1139
1139
  request = cur.unpackBytes();
1140
- cur.exit(end);
1140
+ cur.ensureAt(end);
1141
1141
  }
1142
1142
 
1143
1143
  /// @notice Consume a RELAY block and return its destination portal, resources, and request stream.
@@ -1152,7 +1152,7 @@ library Cursors {
1152
1152
  portal = cur.readUint();
1153
1153
  resources = cur.readUint();
1154
1154
  request = cur.unpackBytes();
1155
- cur.exit(end);
1155
+ cur.ensureAt(end);
1156
1156
  }
1157
1157
 
1158
1158
  /// @notice Consume a DISPATCH block and return its destination portal, resources, and payload.
@@ -1167,7 +1167,7 @@ library Cursors {
1167
1167
  portal = cur.readUint();
1168
1168
  resources = cur.readUint();
1169
1169
  payload = cur.unpackBytes();
1170
- cur.exit(end);
1170
+ cur.ensureAt(end);
1171
1171
  }
1172
1172
 
1173
1173
  /// @notice Consume a RECOVER block and return its handler, resources, key, and witness bytes.
@@ -1184,7 +1184,7 @@ library Cursors {
1184
1184
  resources = cur.readUint();
1185
1185
  key = cur.read32();
1186
1186
  witness = cur.unpackBytes();
1187
- cur.exit(end);
1187
+ cur.ensureAt(end);
1188
1188
  }
1189
1189
 
1190
1190
  // Type-specific validators
package/commands/Burn.sol CHANGED
@@ -30,7 +30,7 @@ abstract contract Burn is CommandBase, BurnHook {
30
30
  /// @param c Command context; `c.state` must contain BALANCE blocks.
31
31
  /// @return Empty output state.
32
32
  function burn(CommandContext calldata c) external onlyCommand returns (bytes memory) {
33
- (Cur memory state, , ) = Cursors.init(c.state, 1);
33
+ (Cur memory state, ) = Cursors.init(c.state, 1);
34
34
 
35
35
  while (state.i < state.len) {
36
36
  (bytes32 asset, uint amount) = state.unpackBalance();
@@ -32,7 +32,7 @@ abstract contract CreditAccount is CommandBase, CreditAccountHook {
32
32
  function creditAccount(
33
33
  CommandContext calldata c
34
34
  ) external onlyCommand returns (bytes memory) {
35
- (Cur memory state, , ) = Cursors.init(c.state, 1);
35
+ (Cur memory state, ) = Cursors.init(c.state, 1);
36
36
 
37
37
  while (state.i < state.len) {
38
38
  (bytes32 asset, uint amount) = state.unpackBalance();
@@ -32,7 +32,7 @@ abstract contract DebitAccount is CommandBase, DebitAccountHook {
32
32
  /// The default implementation iterates AMOUNT blocks, calls
33
33
  /// `debitAccount`, and emits matching BALANCE blocks.
34
34
  function debitAccount(bytes32 account, bytes calldata request) internal virtual returns (bytes memory) {
35
- (Cur memory input, uint groups, ) = Cursors.init(request, 1);
35
+ (Cur memory input, uint groups) = Cursors.init(request, 1);
36
36
  Writer memory writer = Writers.allocBalances(groups);
37
37
 
38
38
  while (input.i < input.len) {
@@ -48,7 +48,7 @@ abstract contract Deposit is CommandBase, DepositHook {
48
48
  function deposit(
49
49
  CommandContext calldata c
50
50
  ) external onlyCommand returns (bytes memory) {
51
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
51
+ (Cur memory request, uint groups) = Cursors.init(c.request, 1);
52
52
  Writer memory writer = Writers.allocBalances(groups);
53
53
 
54
54
  while (request.i < request.len) {
@@ -79,7 +79,7 @@ abstract contract DepositPayable is CommandBase, Payable, DepositPayableHook {
79
79
  function depositPayable(
80
80
  CommandContext calldata c
81
81
  ) external payable onlyCommand returns (bytes memory) {
82
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
82
+ (Cur memory request, uint groups) = Cursors.init(c.request, 1);
83
83
  Writer memory writer = Writers.allocBalances(groups);
84
84
  Budget memory budget = openValue();
85
85
 
@@ -31,8 +31,8 @@ abstract contract Payout is CommandBase, PayoutHook {
31
31
  /// @param c Command context; `c.state` must contain BALANCE blocks and `c.request` matching ACCOUNT blocks.
32
32
  /// @return Empty output state.
33
33
  function payout(CommandContext calldata c) external onlyCommand returns (bytes memory) {
34
- (Cur memory state, uint groups, ) = Cursors.init(c.state, 1);
35
- (Cur memory request, ) = Cursors.init(c.request, 1, groups);
34
+ (Cur memory state, uint groups) = Cursors.init(c.state, 1);
35
+ Cur memory request = Cursors.init(c.request, 1, groups);
36
36
 
37
37
  while (state.i < state.len) {
38
38
  (bytes32 asset, uint amount) = state.unpackBalance();
@@ -44,7 +44,7 @@ abstract contract Provision is CommandBase, ProvisionHook {
44
44
  /// @param c Command context; `c.request` must contain ALLOCATION blocks.
45
45
  /// @return CUSTODY block stream matching the provisioned allocations.
46
46
  function provision(CommandContext calldata c) external onlyCommand returns (bytes memory) {
47
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
47
+ (Cur memory request, uint groups) = Cursors.init(c.request, 1);
48
48
  Writer memory writer = Writers.allocCustodies(groups);
49
49
 
50
50
  while (request.i < request.len) {
@@ -76,7 +76,7 @@ abstract contract ProvisionPayable is CommandBase, Payable, ProvisionPayableHook
76
76
  function provisionPayable(
77
77
  CommandContext calldata c
78
78
  ) external payable onlyCommand returns (bytes memory) {
79
- (Cur memory request, uint groups, ) = Cursors.init(c.request, 1);
79
+ (Cur memory request, uint groups) = Cursors.init(c.request, 1);
80
80
  Writer memory writer = Writers.allocCustodies(groups);
81
81
  Budget memory budget = openValue();
82
82
 
@@ -3,12 +3,20 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {CommandBase, CommandContext, Keys} from "./Base.sol";
5
5
  import {Payable} from "../core/Payable.sol";
6
- import {RecoverHook} from "../core/Portal.sol";
7
6
  import {Cursors, Cur, Schemas} from "../Cursors.sol";
8
7
  import {Budget} from "../utils/Value.sol";
9
8
 
10
9
  using Cursors for Cur;
11
10
 
11
+ abstract contract RecoverHook {
12
+ /// @notice Override to recover a witness through `handler`.
13
+ /// @param handler Port that should attempt recovery.
14
+ /// @param key Recovery lookup key.
15
+ /// @param witness Witness payload used to prove and replay recovery.
16
+ /// @param value Native EVM value assigned to the recovery attempt.
17
+ function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual;
18
+ }
19
+
12
20
  /// @title RecoverPayable
13
21
  /// @notice Command that forwards recover request blocks to a virtual hook.
14
22
  /// Recovery is witness-driven: the command account pays and receives leftover
@@ -26,7 +34,7 @@ abstract contract RecoverPayable is CommandBase, Payable, RecoverHook {
26
34
  /// @param c Command context; `c.request` must contain Recover blocks.
27
35
  /// @return Empty output state.
28
36
  function recoverPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory) {
29
- (Cur memory request, , ) = Cursors.init(c.request, 1);
37
+ (Cur memory request, ) = Cursors.init(c.request, 1);
30
38
  Budget memory budget = openValue();
31
39
 
32
40
  while (request.i < request.len) {
@@ -3,12 +3,22 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import {CommandBase, CommandContext, Keys} from "./Base.sol";
5
5
  import {Payable} from "../core/Payable.sol";
6
- import {RoutePayableHook} from "../core/Portal.sol";
7
6
  import {Cursors, Cur, Schemas} from "../Cursors.sol";
8
7
  import {Budget} from "../utils/Value.sol";
9
8
 
10
9
  using Cursors for Cur;
11
10
 
11
+ abstract contract RoutePayableHook {
12
+ /// @notice Override to route an encoded payload through `portal`.
13
+ /// @param portal Destination portal identifier, often the destination host ID.
14
+ /// @param resources Chain-specific destination resources. EVM adapters
15
+ /// may interpret this as packed execution gas and destination value.
16
+ /// @param payload Encoded payload ready for the transport layer.
17
+ /// @param budget Source native-value budget available for transport
18
+ /// fees and destination resource funding.
19
+ function route(uint portal, uint resources, bytes memory payload, Budget memory budget) internal virtual;
20
+ }
21
+
12
22
  /// @title RelayPayable
13
23
  /// @notice Command that forwards one RELAY block to a host-defined relay hook.
14
24
  /// Reverts unless the request contains exactly one RELAY block, preventing
@@ -26,7 +36,7 @@ abstract contract RelayPayable is CommandBase, Payable, RoutePayableHook {
26
36
  /// @param c Command context; `c.request` must contain exactly one RELAY block.
27
37
  /// @return output Empty output state.
28
38
  function relayPayable(CommandContext calldata c) external payable onlyCommand returns (bytes memory output) {
29
- (Cur memory request, ) = Cursors.init(c.request, 1, 1);
39
+ Cur memory request = Cursors.init(c.request, 1, 1);
30
40
  Budget memory budget = openValue();
31
41
 
32
42
  (uint portal, uint resources, bytes memory context) = request.relayToContext(c.account, c.state);
@@ -32,7 +32,7 @@ abstract contract Withdraw is CommandBase, WithdrawHook {
32
32
  function withdraw(
33
33
  CommandContext calldata c
34
34
  ) external onlyCommand returns (bytes memory) {
35
- (Cur memory state, , ) = Cursors.init(c.state, 1);
35
+ (Cur memory state, ) = Cursors.init(c.state, 1);
36
36
 
37
37
  while (state.i < state.len) {
38
38
  (bytes32 asset, uint amount) = state.unpackBalance();
@@ -29,7 +29,7 @@ abstract contract AllowAssets is AdminBase, AllowAssetsHook {
29
29
  function allowAssets(
30
30
  CommandContext calldata c
31
31
  ) external onlyAdmin(c.account) returns (bytes memory) {
32
- (Cur memory request, , ) = Cursors.init(c.request, 1);
32
+ (Cur memory request, ) = Cursors.init(c.request, 1);
33
33
 
34
34
  while (request.i < request.len) {
35
35
  bytes32 asset = request.unpackAsset();
@@ -31,7 +31,7 @@ abstract contract Allowance is AdminBase, AllowanceHook {
31
31
  /// @param c Admin command context; `c.request` must contain ALLOWANCE blocks.
32
32
  /// @return Empty output state.
33
33
  function allowance(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
34
- (Cur memory request, , ) = Cursors.init(c.request, 1);
34
+ (Cur memory request, ) = Cursors.init(c.request, 1);
35
35
 
36
36
  while (request.i < request.len) {
37
37
  (uint peer, bytes32 asset, uint amount) = request.unpackAllowance();
@@ -23,7 +23,7 @@ abstract contract Appoint is AdminBase {
23
23
  function appoint(
24
24
  CommandContext calldata c
25
25
  ) external onlyAdmin(c.account) returns (bytes memory) {
26
- (Cur memory request, , ) = Cursors.init(c.request, 1);
26
+ (Cur memory request, ) = Cursors.init(c.request, 1);
27
27
 
28
28
  while (request.i < request.len) {
29
29
  bytes32 account = request.unpackAccount();
@@ -23,7 +23,7 @@ abstract contract Authorize is AdminBase {
23
23
  function authorize(
24
24
  CommandContext calldata c
25
25
  ) external onlyAdmin(c.account) returns (bytes memory) {
26
- (Cur memory request, , ) = Cursors.init(c.request, 1);
26
+ (Cur memory request, ) = Cursors.init(c.request, 1);
27
27
 
28
28
  while (request.i < request.len) {
29
29
  uint node = request.unpackNode();
@@ -29,7 +29,7 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
29
29
  function denyAssets(
30
30
  CommandContext calldata c
31
31
  ) external onlyAdmin(c.account) returns (bytes memory) {
32
- (Cur memory request, , ) = Cursors.init(c.request, 1);
32
+ (Cur memory request, ) = Cursors.init(c.request, 1);
33
33
 
34
34
  while (request.i < request.len) {
35
35
  bytes32 asset = request.unpackAsset();
@@ -23,7 +23,7 @@ abstract contract Dismiss is AdminBase {
23
23
  function dismiss(
24
24
  CommandContext calldata c
25
25
  ) external onlyAdmin(c.account) returns (bytes memory) {
26
- (Cur memory request, , ) = Cursors.init(c.request, 1);
26
+ (Cur memory request, ) = Cursors.init(c.request, 1);
27
27
 
28
28
  while (request.i < request.len) {
29
29
  bytes32 account = request.unpackAccount();
@@ -25,7 +25,7 @@ abstract contract ExecutePayable is AdminBase, Payable {
25
25
  /// @param c Admin command context; `c.request` must contain CALL blocks.
26
26
  /// @return Empty output state.
27
27
  function executePayable(CommandContext calldata c) external payable onlyAdmin(c.account) returns (bytes memory) {
28
- (Cur memory request, , ) = Cursors.init(c.request, 1);
28
+ (Cur memory request, ) = Cursors.init(c.request, 1);
29
29
  Budget memory budget = openValue();
30
30
 
31
31
  while (request.i < request.len) {
@@ -21,7 +21,7 @@ abstract contract Label is AdminBase {
21
21
  /// @param c Admin command context; `c.request` must contain LABEL blocks.
22
22
  /// @return Empty output state.
23
23
  function label(CommandContext calldata c) external onlyAdmin(c.account) returns (bytes memory) {
24
- (Cur memory request, , ) = Cursors.init(c.request, 1);
24
+ (Cur memory request, ) = Cursors.init(c.request, 1);
25
25
 
26
26
  while (request.i < request.len) {
27
27
  (uint id, bytes32 namespace, string memory name) = request.unpackLabel();
@@ -23,7 +23,7 @@ abstract contract Unauthorize is AdminBase {
23
23
  function unauthorize(
24
24
  CommandContext calldata c
25
25
  ) external onlyAdmin(c.account) returns (bytes memory) {
26
- (Cur memory request, , ) = Cursors.init(c.request, 1);
26
+ (Cur memory request, ) = Cursors.init(c.request, 1);
27
27
 
28
28
  while (request.i < request.len) {
29
29
  uint node = request.unpackNode();
package/core/Payable.sol CHANGED
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {Budget, Values} from "../utils/Value.sol";
5
+ import {max128} from "../utils/Utils.sol";
5
6
 
6
7
  /// @title Payable
7
8
  /// @notice Abstract mixin for entrypoints that accept native value (`msg.value`).
@@ -16,6 +17,12 @@ abstract contract Payable {
16
17
  return Budget({remaining: msg.value});
17
18
  }
18
19
 
20
+ /// @notice Return the current call's native value as a checked uint128.
21
+ /// @return value Current `msg.value` in wei.
22
+ function msgValue() internal view returns (uint128 value) {
23
+ return uint128(max128(msg.value));
24
+ }
25
+
19
26
  /// @notice Deduct the EVM value lane from a packed resource word and return it.
20
27
  /// @dev EVM resources use the low 128 bits as native value/endowment.
21
28
  /// @param budget Mutable budget to deduct from.
package/core/Pipeline.sol CHANGED
@@ -43,7 +43,7 @@ abstract contract Pipeline is Payable {
43
43
  bytes calldata steps,
44
44
  Budget memory budget
45
45
  ) internal {
46
- (Cur memory input, , ) = Cursors.init(steps, 1);
46
+ (Cur memory input, ) = Cursors.init(steps, 1);
47
47
 
48
48
  while (input.i < input.len) {
49
49
  (uint target, uint resources, bytes calldata request) = input.unpackStep();
package/core/Portal.sol CHANGED
@@ -2,67 +2,38 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {PortCalls} from "./Calls.sol";
5
- import {ResolvedEvent} from "../events/Resolved.sol";
6
- import {UndeliveredEvent} from "../events/Undelivered.sol";
7
- import {Budget} from "../utils/Value.sol";
8
-
9
- abstract contract RoutePayableHook {
10
- /// @notice Override to route an encoded payload through `portal`.
11
- /// @param portal Destination portal identifier, often the destination host ID.
12
- /// @param resources Chain-specific destination resources. EVM adapters
13
- /// may interpret this as packed execution gas and destination value.
14
- /// @param payload Encoded payload ready for the transport layer.
15
- /// @param budget Source native-value budget available for transport
16
- /// fees and destination resource funding.
17
- function route(uint portal, uint resources, bytes memory payload, Budget memory budget) internal virtual;
18
- }
19
-
20
- abstract contract RecoverHook {
21
- /// @notice Override to recover a previously undelivered witness through `handler`.
22
- /// @param handler Port that should attempt recovery.
23
- /// @param key Recovery lookup key.
24
- /// @param witness Witness payload used to prove and replay recovery.
25
- /// @param value Native EVM value assigned to the recovery attempt.
26
- function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual;
27
- }
28
5
 
29
6
  /// @title Portal
30
7
  /// @notice Base contract for hosts that route payloads through portal adapters.
31
- abstract contract Portal is PortCalls, RecoverHook, ResolvedEvent, UndeliveredEvent {
8
+ abstract contract Portal is PortCalls {
32
9
  error BadWitness();
33
10
 
34
- /// @dev Remote port used to handle messages delivered through this portal.
35
- uint private immutable port;
36
-
37
11
  mapping(bytes32 key => bytes32 digest) internal undelivered;
38
12
 
39
- /// @param handler Remote port used to handle messages delivered through this portal.
40
- constructor(uint handler) {
41
- port = handler;
13
+ /// @notice Try to forward `message` to `port`.
14
+ /// @dev Records and returns `keccak256(message)` under `key` only when forwarding fails.
15
+ /// @param port Port that should handle the forwarded message.
16
+ /// @param key Forwarding/recovery lookup key.
17
+ /// @param message Encoded port input to forward.
18
+ /// @param value Native EVM value assigned to the forwarding attempt.
19
+ /// @return miss Message digest recorded for recovery when forwarding fails; zero on success.
20
+ function forward(uint port, bytes32 key, bytes calldata message, uint128 value) internal returns (bytes32 miss) {
21
+ if (tryCallPort(port, value, message)) return bytes32(0);
22
+
23
+ miss = keccak256(message);
24
+ undelivered[key] = miss;
42
25
  }
43
26
 
44
- /// @notice Try to deliver `message` to this portal's handler port.
45
- /// @dev Records `keccak256(message)` under `key` only when delivery fails.
46
- /// @param key Delivery/recovery lookup key.
47
- /// @param message Encoded port input to deliver.
48
- /// @param value Native EVM value assigned to the delivery attempt.
49
- function deliver(bytes32 key, bytes calldata message, uint128 value) internal {
50
- if (tryCallPort(port, value, message)) return;
51
- bytes32 digest = keccak256(message);
52
- undelivered[key] = digest;
53
- emit Undelivered(host, key, digest);
54
- }
55
-
56
- /// @notice Recover a previously undelivered witness through `handler`.
27
+ /// @notice Retry a previously undelivered witness through `port`.
57
28
  /// @dev The witness must hash to the digest stored under `key`.
58
- /// @param handler Port that should attempt recovery.
29
+ /// @param port Port that should attempt recovery.
59
30
  /// @param key Recovery lookup key.
60
31
  /// @param witness Witness payload used to prove and replay recovery.
61
32
  /// @param value Native EVM value assigned to the recovery attempt.
62
- function recover(uint handler, bytes32 key, bytes calldata witness, uint128 value) internal virtual override {
63
- if (keccak256(witness) != undelivered[key]) revert BadWitness();
33
+ function retry(uint port, bytes32 key, bytes calldata witness, uint128 value) internal virtual {
34
+ if (undelivered[key] != keccak256(witness)) revert BadWitness();
35
+
64
36
  delete undelivered[key];
65
- callPort(handler, value, witness);
66
- emit Resolved(host, key);
37
+ callPort(port, value, witness);
67
38
  }
68
39
  }
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {EventEmitter} from "./Emitter.sol";
5
+
6
+ /// @notice Emitted when a host recovers a previously recorded key.
7
+ abstract contract RecoveredEvent is EventEmitter {
8
+ string private constant ABI = "event Recovered(uint indexed host, bytes32 key)";
9
+
10
+ /// @param host Host node ID that owns the recovered key.
11
+ /// @param key Recovery lookup key.
12
+ event Recovered(uint indexed host, bytes32 key);
13
+
14
+ constructor() {
15
+ emit EventAbi(ABI);
16
+ }
17
+ }
package/guards/Revoke.sol CHANGED
@@ -18,7 +18,7 @@ abstract contract Revoke is GuardBase {
18
18
  }
19
19
 
20
20
  function revoke(bytes calldata request) external onlyGuardian {
21
- (Cur memory input, , ) = Cursors.init(request, 1);
21
+ (Cur memory input, ) = Cursors.init(request, 1);
22
22
 
23
23
  while (input.i < input.len) {
24
24
  uint node = input.unpackNode();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Solidity contracts and protocol building blocks for rootzero hosts and commands.",
5
5
  "private": false,
6
6
  "license": "GPL-3.0-only",
@@ -22,7 +22,7 @@ abstract contract PortAllowAssets is PortBase, AllowAssetsHook {
22
22
  /// @param data ASSET block stream supplied by the trusted peer.
23
23
  /// @return Empty response bytes.
24
24
  function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- (Cur memory assets, , ) = Cursors.init(data, 1);
25
+ (Cur memory assets, ) = Cursors.init(data, 1);
26
26
 
27
27
  while (assets.i < assets.len) {
28
28
  bytes32 asset = assets.unpackAsset();
@@ -23,7 +23,7 @@ abstract contract PortAllowance is PortBase, AllowanceHook {
23
23
  /// @param data AMOUNT block stream requested by the trusted peer.
24
24
  /// @return Empty response bytes.
25
25
  function portAllowance(bytes calldata data) external onlyPeer returns (bytes memory) {
26
- (Cur memory amounts, , ) = Cursors.init(data, 1);
26
+ (Cur memory amounts, ) = Cursors.init(data, 1);
27
27
  uint peer = caller();
28
28
 
29
29
  while (amounts.i < amounts.len) {
package/ports/Credit.sol CHANGED
@@ -22,7 +22,7 @@ abstract contract PortCreditAccount is PortBase, CreditAccountHook {
22
22
  /// @param data ACCOUNT_AMOUNT block stream supplied by the trusted peer.
23
23
  /// @return Empty response bytes.
24
24
  function portCreditAccount(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- (Cur memory amounts, , ) = Cursors.init(data, 1);
25
+ (Cur memory amounts, ) = Cursors.init(data, 1);
26
26
 
27
27
  while (amounts.i < amounts.len) {
28
28
  (bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
package/ports/Debit.sol CHANGED
@@ -22,7 +22,7 @@ abstract contract PortDebitAccount is PortBase, DebitAccountHook {
22
22
  /// @param data ACCOUNT_AMOUNT block stream supplied by the trusted peer.
23
23
  /// @return Empty response bytes.
24
24
  function portDebitAccount(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- (Cur memory amounts, , ) = Cursors.init(data, 1);
25
+ (Cur memory amounts, ) = Cursors.init(data, 1);
26
26
 
27
27
  while (amounts.i < amounts.len) {
28
28
  (bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
@@ -22,7 +22,7 @@ abstract contract PortDenyAssets is PortBase, DenyAssetsHook {
22
22
  /// @param data ASSET block stream supplied by the trusted peer.
23
23
  /// @return Empty response bytes.
24
24
  function portDenyAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
25
- (Cur memory assets, , ) = Cursors.init(data, 1);
25
+ (Cur memory assets, ) = Cursors.init(data, 1);
26
26
 
27
27
  while (assets.i < assets.len) {
28
28
  bytes32 asset = assets.unpackAsset();
@@ -3,7 +3,7 @@ pragma solidity ^0.8.33;
3
3
 
4
4
  import { PortBase } from "./Base.sol";
5
5
  import { Payable } from "../core/Payable.sol";
6
- import { RoutePayableHook } from "../core/Portal.sol";
6
+ import { RoutePayableHook } from "../commands/Relay.sol";
7
7
  import { Cursors, Cur, Schemas } from "../Cursors.sol";
8
8
  import { Budget } from "../utils/Value.sol";
9
9
 
@@ -25,7 +25,7 @@ abstract contract PortDispatchPayable is PortBase, Payable, RoutePayableHook {
25
25
  /// @param data DISPATCH block stream supplied by the trusted peer.
26
26
  /// @return output Empty response bytes.
27
27
  function portDispatchPayable(bytes calldata data) external payable onlyPeer returns (bytes memory output) {
28
- (Cur memory input, , ) = Cursors.init(data, 1);
28
+ (Cur memory input, ) = Cursors.init(data, 1);
29
29
  Budget memory budget = openValue();
30
30
 
31
31
  while (input.i < input.len) {
package/ports/Pipe.sol CHANGED
@@ -25,7 +25,7 @@ abstract contract PortPipePayable is PortBase, Pipeline {
25
25
  /// @param data CONTEXT block stream supplied by the trusted peer.
26
26
  /// @return Empty response bytes.
27
27
  function portPipePayable(bytes calldata data) external payable onlyPeer returns (bytes memory) {
28
- (Cur memory input, , ) = Cursors.init(data, 1);
28
+ (Cur memory input, ) = Cursors.init(data, 1);
29
29
  Budget memory budget = openValue();
30
30
 
31
31
  while (input.i < input.len) {
package/ports/Redeem.sol CHANGED
@@ -30,7 +30,7 @@ abstract contract PortRedeemBalance is PortBase, RedeemBalanceHook {
30
30
  /// @param data BALANCE block stream supplied by the trusted peer.
31
31
  /// @return Empty response bytes.
32
32
  function portRedeemBalance(bytes calldata data) external onlyPeer returns (bytes memory) {
33
- (Cur memory input, , ) = Cursors.init(data, 1);
33
+ (Cur memory input, ) = Cursors.init(data, 1);
34
34
  uint peer = caller();
35
35
 
36
36
  while (input.i < input.len) {
package/ports/Settle.sol CHANGED
@@ -23,7 +23,7 @@ abstract contract PortSettle is PortBase, DebitAccountHook, CreditAccountHook {
23
23
  /// @param data TRANSACTION block stream supplied by the trusted peer.
24
24
  /// @return Empty response bytes.
25
25
  function portSettle(bytes calldata data) external onlyPeer returns (bytes memory) {
26
- (Cur memory state, , ) = Cursors.init(data, 1);
26
+ (Cur memory state, ) = Cursors.init(data, 1);
27
27
 
28
28
  while (state.i < state.len) {
29
29
  (bytes32 from, bytes32 to, bytes32 asset, uint amount) = state.unpackTransaction();
@@ -32,7 +32,7 @@ abstract contract AssetStatus is QueryBase, AssetStatusHook {
32
32
  /// @param request Block-stream request consisting of `#asset { bytes32 asset }` blocks.
33
33
  /// @return Block-stream response containing one `#status { uint code }` per asset block.
34
34
  function assetStatus(bytes calldata request) external view returns (bytes memory) {
35
- (Cur memory query, uint groups, ) = Cursors.init(request, 1);
35
+ (Cur memory query, uint groups) = Cursors.init(request, 1);
36
36
  Writer memory response = Writers.allocStatuses(groups);
37
37
 
38
38
  while (query.i < query.len) {
@@ -32,7 +32,7 @@ abstract contract GetBalances is QueryBase, GetBalancesHook {
32
32
  /// @param request Block-stream request consisting of `accountAsset(account, asset)*`.
33
33
  /// @return Block-stream response containing one `accountAmount(account, asset, amount)` block per request block.
34
34
  function getBalances(bytes calldata request) external view returns (bytes memory) {
35
- (Cur memory query, uint groups, ) = Cursors.init(request, 1);
35
+ (Cur memory query, uint groups) = Cursors.init(request, 1);
36
36
  Writer memory response = Writers.allocAccountAmounts(groups);
37
37
 
38
38
  while (query.i < query.len) {
@@ -40,7 +40,7 @@ abstract contract GetPosition is QueryBase, GetPositionHook {
40
40
  /// @param request Block-stream request consisting of `accountAsset(account, asset)*`.
41
41
  /// @return Block-stream response containing one output-schema block per position block.
42
42
  function getPosition(bytes calldata request) external view returns (bytes memory) {
43
- (Cur memory query, uint groups, ) = Cursors.init(request, 1);
43
+ (Cur memory query, uint groups) = Cursors.init(request, 1);
44
44
  Writer memory response = Writers.allocAny(groups);
45
45
 
46
46
  while (query.i < query.len) {
@@ -1,17 +0,0 @@
1
- // SPDX-License-Identifier: GPL-3.0-only
2
- pragma solidity ^0.8.33;
3
-
4
- import {EventEmitter} from "./Emitter.sol";
5
-
6
- /// @notice Emitted when a host resolves a previously recorded key.
7
- abstract contract ResolvedEvent is EventEmitter {
8
- string private constant ABI = "event Resolved(uint indexed host, bytes32 key)";
9
-
10
- /// @param host Host node ID that owns the resolved key.
11
- /// @param key Resolution lookup key.
12
- event Resolved(uint indexed host, bytes32 key);
13
-
14
- constructor() {
15
- emit EventAbi(ABI);
16
- }
17
- }