@rootzero/contracts 1.19.0 → 1.21.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 (57) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/Codec.sol +1 -1
  3. package/Commands.sol +1 -0
  4. package/Endpoints.sol +5 -4
  5. package/Events.sol +3 -2
  6. package/README.md +18 -12
  7. package/Utils.sol +0 -1
  8. package/codec/Decoders.sol +9 -1
  9. package/codec/Descriptors.sol +28 -28
  10. package/codec/Schema.sol +5 -5
  11. package/codec/Specs.sol +9 -27
  12. package/commands/Allocate.sol +1 -1
  13. package/commands/Base.sol +4 -10
  14. package/commands/Burn.sol +1 -1
  15. package/commands/Credit.sol +3 -3
  16. package/commands/Debit.sol +3 -3
  17. package/commands/Deposit.sol +3 -10
  18. package/commands/Payout.sol +1 -1
  19. package/commands/Provision.sol +3 -3
  20. package/commands/Recover.sol +3 -3
  21. package/commands/Relay.sol +20 -15
  22. package/commands/Repay.sol +3 -3
  23. package/commands/Settle.sol +5 -5
  24. package/commands/Withdraw.sol +1 -1
  25. package/commands/admin/AllowAssets.sol +2 -2
  26. package/commands/admin/Allowance.sol +2 -2
  27. package/commands/admin/Annotate.sol +2 -2
  28. package/commands/admin/Appoint.sol +2 -2
  29. package/commands/admin/Authorize.sol +2 -2
  30. package/commands/admin/Base.sol +1 -1
  31. package/commands/admin/DenyAssets.sol +2 -8
  32. package/commands/admin/Dismiss.sol +2 -2
  33. package/commands/admin/Execute.sol +2 -2
  34. package/commands/admin/Unauthorize.sol +2 -2
  35. package/core/Portal.sol +11 -8
  36. package/docs/Schema.md +53 -29
  37. package/events/Relay.sol +20 -0
  38. package/events/Resolved.sol +17 -0
  39. package/events/Unresolved.sol +18 -0
  40. package/execution/Execution.sol +10 -1
  41. package/guards/Base.sol +1 -2
  42. package/package.json +1 -1
  43. package/ports/AllowAssets.sol +1 -1
  44. package/ports/Allowance.sol +1 -1
  45. package/ports/Base.sol +4 -5
  46. package/ports/Credit.sol +1 -1
  47. package/ports/Debit.sol +1 -1
  48. package/ports/DenyAssets.sol +1 -1
  49. package/ports/Dispatch.sol +19 -7
  50. package/ports/Pipe.sol +2 -1
  51. package/ports/Post.sol +1 -1
  52. package/ports/Redeem.sol +1 -1
  53. package/queries/Base.sol +1 -2
  54. package/utils/Nodes.sol +21 -16
  55. package/events/Recovered.sol +0 -17
  56. package/events/Undelivered.sol +0 -18
  57. package/utils/Selectors.sol +0 -49
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Execution, Executions, CommandBase, HostAmount, Lanes, Specs} from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, HostAmount, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @notice Shared provision hook used by `Provision`.
@@ -32,7 +32,7 @@ abstract contract Provision is CommandBase, ProvisionHook {
32
32
  uint private immutable descriptor;
33
33
 
34
34
  constructor() {
35
- (, descriptor) = command("provision", Specs.Empty, Specs.Allocation, Specs.Custody, 0, false, false);
35
+ (, descriptor) = command("provision", Specs.Empty, Specs.Allocation, Specs.Custody, 0, 0);
36
36
  }
37
37
 
38
38
  /// @notice Provision ALLOCATION input blocks and output matching CUSTODY state blocks.
@@ -64,7 +64,7 @@ abstract contract ProvisionPayable is CommandBase, ProvisionPayableHook {
64
64
  uint private immutable descriptor;
65
65
 
66
66
  constructor() {
67
- (, descriptor) = command("provisionPayable", Specs.Empty, Specs.Allocation, Specs.Custody, 0, true, false);
67
+ (, descriptor) = command("provisionPayable", Specs.Empty, Specs.Allocation, Specs.Custody, 0, Flags.Funded);
68
68
  }
69
69
 
70
70
  /// @notice Provision ALLOCATION input blocks with access to a mutable native-value budget.
@@ -1,11 +1,11 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.sol";
5
5
 
6
6
  using Executions for Execution;
7
7
 
8
- /// @notice Hook implemented by hosts that recover previously undelivered payloads.
8
+ /// @notice Hook implemented by hosts that recover previously unresolved payloads.
9
9
  abstract contract RecoverPayableHook {
10
10
  /// @notice Override to recover a witness through `handler`.
11
11
  /// @param handler Port that should attempt recovery.
@@ -31,7 +31,7 @@ abstract contract RecoverPayable is CommandBase, RecoverPayableHook {
31
31
  uint private immutable descriptor;
32
32
 
33
33
  constructor() {
34
- (, descriptor) = command("recoverPayable", Specs.Empty, Specs.Recover, Specs.Empty, 0, true, false);
34
+ (, descriptor) = command("recoverPayable", Specs.Empty, Specs.Recover, Specs.Empty, 0, Flags.Funded);
35
35
  }
36
36
 
37
37
  /// @notice Recover each recover block in the command input.
@@ -1,20 +1,29 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Blocks, Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.sol";
5
5
 
6
6
  using Executions for Execution;
7
7
 
8
- /// @notice Hook implemented by hosts that forward funded relay payloads.
8
+ /// @notice Hook implemented by hosts that relay command contexts.
9
9
  abstract contract RelayPayableHook {
10
- /// @notice Override to relay an encoded payload to `portal`.
10
+ /// @notice Override to relay a command context 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
- /// @param payload Encoded payload ready for the transport layer.
14
+ /// @param account Destination command account.
15
+ /// @param state State forwarded into the destination context.
16
+ /// @param input Input forwarded into the destination context.
15
17
  /// @param funds Execution used for source value available for transport fees
16
18
  /// and destination resource funding.
17
- function relayTo(uint portal, uint resources, bytes memory payload, Execution memory funds) internal virtual;
19
+ function relay(
20
+ uint portal,
21
+ uint resources,
22
+ bytes32 account,
23
+ bytes calldata state,
24
+ bytes calldata input,
25
+ Execution memory funds
26
+ ) internal virtual;
18
27
  }
19
28
 
20
29
  /// @title RelayPayable
@@ -23,7 +32,7 @@ abstract contract RelayPayable is CommandBase, RelayPayableHook {
23
32
  uint private immutable descriptor;
24
33
 
25
34
  constructor() {
26
- (, descriptor) = command("relayPayable", Specs.Empty, Specs.Relay, Specs.Empty, 0, true, false);
35
+ (, descriptor) = command("relayPayable", Specs.Empty, Specs.Relay, Specs.Empty, 0, Flags.Funded);
27
36
  }
28
37
 
29
38
  /// @notice Relay one RELAY input block with the command account and empty state.
@@ -33,10 +42,8 @@ abstract contract RelayPayable is CommandBase, RelayPayableHook {
33
42
  bytes calldata input
34
43
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
35
44
  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.contextCopy(account, state, payload);
38
-
39
- relayTo(portal, resources, context, exec);
45
+ (uint portal, uint resources, bytes calldata relayInput) = exec.unpackRelay(Lanes.Input);
46
+ relay(portal, resources, account, state, relayInput, exec);
40
47
 
41
48
  return close(exec, account);
42
49
  }
@@ -51,7 +58,7 @@ abstract contract RelayBalancePayable is CommandBase, RelayPayableHook {
51
58
  uint private immutable descriptor;
52
59
 
53
60
  constructor() {
54
- (, descriptor) = command("relayBalancePayable", Specs.Balance, Specs.Relay, Specs.Empty, 0, true, false);
61
+ (, descriptor) = command("relayBalancePayable", Specs.Balance, Specs.Relay, Specs.Empty, 0, Flags.Funded);
55
62
  }
56
63
 
57
64
  /// @notice Relay one RELAY input block with the command account and current state.
@@ -65,10 +72,8 @@ abstract contract RelayBalancePayable is CommandBase, RelayPayableHook {
65
72
  bytes calldata input
66
73
  ) external payable onlyCommand returns (bytes memory, bytes memory) {
67
74
  Execution memory exec = openCommand(state, input, descriptor, 1);
68
- (uint portal, uint resources, bytes calldata payload) = exec.unpackRelay(Lanes.Input);
69
- bytes memory context = Blocks.contextCopy(account, state, payload);
70
-
71
- relayTo(portal, resources, context, exec);
75
+ (uint portal, uint resources, bytes calldata relayInput) = exec.unpackRelay(Lanes.Input);
76
+ relay(portal, resources, account, state, relayInput, exec);
72
77
 
73
78
  return close(exec, account);
74
79
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.sol";
5
5
  import {RepayHook} from "../core/Settlement.sol";
6
6
  import {Action} from "../annotations/Action.sol";
7
7
  import {Actions} from "../utils/Actions.sol";
@@ -25,7 +25,7 @@ abstract contract Repay is CommandBase, RepayHook, Action {
25
25
 
26
26
  constructor() {
27
27
  uint id;
28
- (id, descriptor) = command("repay", Specs.Position, Specs.Empty, Specs.Balance, 0, false, false);
28
+ (id, descriptor) = command("repay", Specs.Position, Specs.Empty, Specs.Balance, 0, 0);
29
29
  action(id, Actions.Settle);
30
30
  }
31
31
 
@@ -57,7 +57,7 @@ abstract contract RepayPayable is CommandBase, RepayPayableHook, Action {
57
57
 
58
58
  constructor() {
59
59
  uint id;
60
- (id, descriptor) = command("repayPayable", Specs.Position, Specs.Empty, Specs.Balance, 0, true, false);
60
+ (id, descriptor) = command("repayPayable", Specs.Position, Specs.Empty, Specs.Balance, 0, Flags.Funded);
61
61
  action(id, Actions.Settle);
62
62
  }
63
63
 
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Execution, Executions, CommandBase, Lanes, Specs} from "./Base.sol";
4
+ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.sol";
5
5
  import {SettleHook} from "../core/Settlement.sol";
6
6
  import {Action} from "../annotations/Action.sol";
7
7
  import {Actions} from "../utils/Actions.sol";
@@ -37,7 +37,7 @@ abstract contract Settle is CommandBase, SettleHook, Action {
37
37
  uint private immutable id;
38
38
 
39
39
  constructor() {
40
- (id, descriptor) = command("settle", Specs.Position, Specs.Empty, Specs.Empty, 0, false, false);
40
+ (id, descriptor) = command("settle", Specs.Position, Specs.Empty, Specs.Empty, 0, 0);
41
41
  action(id, Actions.Settle);
42
42
  }
43
43
 
@@ -73,7 +73,7 @@ abstract contract SettlePayable is CommandBase, SettlePayableHook, Action {
73
73
 
74
74
  constructor() {
75
75
  uint id;
76
- (id, descriptor) = command("settlePayable", Specs.Position, Specs.Empty, Specs.Empty, 0, true, false);
76
+ (id, descriptor) = command("settlePayable", Specs.Position, Specs.Empty, Specs.Empty, 0, Flags.Funded);
77
77
  action(id, Actions.Settle);
78
78
  }
79
79
 
@@ -97,11 +97,11 @@ abstract contract SettlePayable is CommandBase, SettlePayableHook, Action {
97
97
  }
98
98
  }
99
99
 
100
- /// @title InternalSettle
100
+ /// @title SettleInternal
101
101
  /// @notice Extends the advertised settle command with memory-state pipeline dispatch.
102
102
  /// @dev This adapter is not a separate command. It uses the command ID and settlement hook
103
103
  /// inherited from `Settle` while accepting the state location used by `Pipeline`.
104
- abstract contract InternalSettle is Settle {
104
+ abstract contract SettleInternal is Settle {
105
105
  /// @notice Execute the inherited settle command from an internal pipeline.
106
106
  /// @param account Account for which each position is settled.
107
107
  /// @param state POSITION block stream held in pipeline memory.
@@ -25,7 +25,7 @@ abstract contract Withdraw is CommandBase, WithdrawHook, Action {
25
25
 
26
26
  constructor() {
27
27
  uint id;
28
- (id, descriptor) = command("withdraw", Specs.Balance, Specs.Empty, Specs.Empty, 0, false, false);
28
+ (id, descriptor) = command("withdraw", Specs.Balance, Specs.Empty, Specs.Empty, 0, 0);
29
29
  action(id, Actions.Withdraw);
30
30
  }
31
31
 
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Lanes, Specs } from "./Base.sol";
4
+ import { AdminBase, Execution, Executions, Flags, Lanes, Specs } from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @notice Hook implemented by hosts that allow assets.
@@ -19,7 +19,7 @@ abstract contract AllowAssets is AdminBase, AllowAssetsHook {
19
19
  uint private immutable descriptor;
20
20
 
21
21
  constructor() {
22
- (, descriptor) = command("allowAssets", Specs.Empty, Specs.Asset, Specs.Empty, 0, false, true);
22
+ (, descriptor) = command("allowAssets", Specs.Empty, Specs.Asset, Specs.Empty, 0, Flags.Admin);
23
23
  }
24
24
 
25
25
  /// @notice Allow each ASSET block in the admin input.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @notice Hook implemented by hosts that configure peer asset allowances.
@@ -23,7 +23,7 @@ abstract contract Allowance is AdminBase, AllowanceHook {
23
23
  uint private immutable descriptor;
24
24
 
25
25
  constructor() {
26
- (, descriptor) = command("allowance", Specs.Empty, Specs.Allowance, Specs.Empty, 0, false, true);
26
+ (, descriptor) = command("allowance", Specs.Empty, Specs.Allowance, Specs.Empty, 0, Flags.Admin);
27
27
  }
28
28
 
29
29
  /// @notice Apply each ALLOWANCE block in the admin input.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Annotate
@@ -12,7 +12,7 @@ abstract contract Annotate is AdminBase {
12
12
  uint private immutable descriptor;
13
13
 
14
14
  constructor() {
15
- (, descriptor) = command("annotate", Specs.Empty, Specs.Annotation, Specs.Empty, 0, false, true);
15
+ (, descriptor) = command("annotate", Specs.Empty, Specs.Annotation, Specs.Empty, 0, Flags.Admin);
16
16
  }
17
17
 
18
18
  /// @notice Publish each ANNOTATION block in the admin input.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Lanes, Specs } from "./Base.sol";
4
+ import { AdminBase, Execution, Executions, Flags, Lanes, Specs } from "./Base.sol";
5
5
  import { GuardianAccess } from "../../core/Access.sol";
6
6
  using Executions for Execution;
7
7
 
@@ -13,7 +13,7 @@ abstract contract Appoint is GuardianAccess, AdminBase {
13
13
  uint private immutable descriptor;
14
14
 
15
15
  constructor() {
16
- (, descriptor) = command("appoint", Specs.Empty, Specs.Account, Specs.Empty, 0, false, true);
16
+ (, descriptor) = command("appoint", Specs.Empty, Specs.Account, Specs.Empty, 0, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Appoint each user ACCOUNT block in the admin input as a guardian.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Authorize
@@ -13,7 +13,7 @@ abstract contract Authorize is AdminBase {
13
13
  uint private immutable id;
14
14
 
15
15
  constructor() {
16
- (id, descriptor) = command("authorize", Specs.Empty, Specs.Node, Specs.Empty, 0, false, true);
16
+ (id, descriptor) = command("authorize", Specs.Empty, Specs.Node, Specs.Empty, 0, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Return the registered AUTHORIZE command ID.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {CommandBase, Execution, Executions, Lanes, Specs} from "../Base.sol";
4
+ import {CommandBase, Execution, Executions, Flags, Lanes, Specs} from "../Base.sol";
5
5
  import {NodeAccess} from "../../core/Access.sol";
6
6
 
7
7
  /// @title AdminBase
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Lanes, Specs } from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @notice Hook implemented by hosts that deny assets.
@@ -19,7 +19,7 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
19
19
  uint private immutable descriptor;
20
20
 
21
21
  constructor() {
22
- (, descriptor) = command("denyAssets", Specs.Empty, Specs.Asset, Specs.Empty, 0, false, true);
22
+ (, descriptor) = command("denyAssets", Specs.Empty, Specs.Asset, Specs.Empty, 0, Flags.Admin);
23
23
  }
24
24
 
25
25
  /// @notice Deny each ASSET block in the admin input.
@@ -41,9 +41,3 @@ abstract contract DenyAssets is AdminBase, DenyAssetsHook {
41
41
  return close(exec, account);
42
42
  }
43
43
  }
44
-
45
-
46
-
47
-
48
-
49
-
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import { AdminBase, Execution, Executions, Lanes, Specs } from "./Base.sol";
4
+ import { AdminBase, Execution, Executions, Flags, Lanes, Specs } from "./Base.sol";
5
5
  import { GuardianAccess } from "../../core/Access.sol";
6
6
  using Executions for Execution;
7
7
 
@@ -13,7 +13,7 @@ abstract contract Dismiss is GuardianAccess, AdminBase {
13
13
  uint private immutable descriptor;
14
14
 
15
15
  constructor() {
16
- (, descriptor) = command("dismiss", Specs.Empty, Specs.Account, Specs.Empty, 0, false, true);
16
+ (, descriptor) = command("dismiss", Specs.Empty, Specs.Account, Specs.Empty, 0, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Dismiss each user ACCOUNT block in the admin input from guardian status.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  import {RawNodeCalls} from "../../core/Calls.sol";
6
6
 
7
7
  using Executions for Execution;
@@ -15,7 +15,7 @@ abstract contract ExecutePayable is RawNodeCalls, AdminBase {
15
15
  uint private immutable descriptor;
16
16
 
17
17
  constructor() {
18
- (, descriptor) = command("executePayable", Specs.Empty, Specs.Call, Specs.Empty, 0, true, true);
18
+ (, descriptor) = command("executePayable", Specs.Empty, Specs.Call, Specs.Empty, 0, Flags.AdminFunded);
19
19
  }
20
20
 
21
21
  /// @notice Execute each CALL block in the admin input.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {AdminBase, Execution, Executions, Lanes, Specs} from "./Base.sol";
4
+ import {AdminBase, Execution, Executions, Flags, Lanes, Specs} from "./Base.sol";
5
5
  using Executions for Execution;
6
6
 
7
7
  /// @title Unauthorize
@@ -13,7 +13,7 @@ abstract contract Unauthorize is AdminBase {
13
13
  uint private immutable id;
14
14
 
15
15
  constructor() {
16
- (id, descriptor) = command("unauthorize", Specs.Empty, Specs.Node, Specs.Empty, 0, false, true);
16
+ (id, descriptor) = command("unauthorize", Specs.Empty, Specs.Node, Specs.Empty, 0, Flags.Admin);
17
17
  }
18
18
 
19
19
  /// @notice Return the registered UNAUTHORIZE command ID.
package/core/Portal.sol CHANGED
@@ -2,13 +2,16 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {PortCalls} from "./Calls.sol";
5
+ import {NodeAccess} from "./Access.sol";
6
+ import {ResolvedEvent} from "../events/Resolved.sol";
7
+ import {UnresolvedEvent} from "../events/Unresolved.sol";
5
8
 
6
9
  /// @title Portal
7
10
  /// @notice Base contract for hosts that route payloads through portal adapters.
8
- abstract contract Portal is PortCalls {
11
+ abstract contract Portal is PortCalls, NodeAccess, UnresolvedEvent, ResolvedEvent {
9
12
  error BadWitness();
10
13
 
11
- mapping(bytes32 key => bytes32 digest) internal undelivered;
14
+ mapping(bytes32 key => bytes32 digest) internal unresolved;
12
15
 
13
16
  /// @notice Try to forward `message` to `port`.
14
17
  /// @dev Records and returns `keccak256(message)` under `key` only when forwarding fails.
@@ -21,19 +24,19 @@ abstract contract Portal is PortCalls {
21
24
  if (tryCallPort(port, value, message)) return bytes32(0);
22
25
 
23
26
  miss = keccak256(message);
24
- undelivered[key] = miss;
27
+ unresolved[key] = miss;
25
28
  }
26
29
 
27
- /// @notice Retry a previously undelivered witness through `port`.
30
+ /// @notice Resolve a previously unresolved witness through `port`.
28
31
  /// @dev The witness must hash to the digest stored under `key`.
29
32
  /// @param port Port that should attempt recovery.
30
33
  /// @param key Recovery lookup key.
31
34
  /// @param witness Witness payload used to prove and replay recovery.
32
- /// @param value Native EVM value assigned to the recovery attempt.
33
- function retry(uint port, bytes32 key, bytes calldata witness, uint128 value) internal virtual {
34
- if (undelivered[key] != keccak256(witness)) revert BadWitness();
35
+ /// @param value Native EVM value assigned to the resolution attempt.
36
+ function resolve(uint port, bytes32 key, bytes calldata witness, uint128 value) internal virtual {
37
+ if (unresolved[key] != keccak256(witness)) revert BadWitness();
35
38
 
36
- delete undelivered[key];
39
+ delete unresolved[key];
37
40
  callPort(port, value, witness);
38
41
  }
39
42
  }
package/docs/Schema.md CHANGED
@@ -50,8 +50,16 @@ overloaded in the relevant host/schema context.
50
50
 
51
51
  ## Block Syntax
52
52
 
53
- A block definition has an event alias and a schema body. Fixed fields are
54
- written in braces:
53
+ A block definition has an event alias and a schema body. A schema body is one
54
+ of three forms:
55
+
56
+ ```txt
57
+ "" empty or raw payload
58
+ { fields } structured payload
59
+ many #item top-level custom list payload
60
+ ```
61
+
62
+ Fixed fields are written in braces:
55
63
 
56
64
  ```txt
57
65
  alias: amount
@@ -66,9 +74,11 @@ A block body can reference another block alias as a child item with `#`:
66
74
 
67
75
  The empty schema string `""` means the block has no structured payload. This is
68
76
  used for zero-payload blocks such as `#unit` and raw dynamic blocks such as
69
- `#bytes`.
77
+ `#bytes`. A root `many #item` body is reserved for an emitted custom schema
78
+ whose own key identifies the outer list block.
70
79
 
71
- A schema body is a comma-separated list of items. Order is significant.
80
+ A structured schema body is a comma-separated list of items. Order is
81
+ significant.
72
82
 
73
83
  ```txt
74
84
  { #amount, maybe #account as recipient }
@@ -110,11 +120,30 @@ maybe many #balance
110
120
 
111
121
  - no prefix: one required item
112
122
  - `maybe`: optional item
113
- - `many`: one generic `#list` block whose payload contains repeated items
123
+ - `many`: a list whose payload contains repeated items
114
124
  - `maybe many`: optional `#list` block
115
125
 
116
- `maybe` emits no placeholder when absent. `many` wraps repeated items in one
117
- generic list block; it does not repeat the item in place.
126
+ `maybe` emits no placeholder when absent. Inside a structured schema body,
127
+ `many` wraps repeated items in one generic `#list` block; it does not repeat the
128
+ item in place:
129
+
130
+ ```txt
131
+ { uint id, many #asset as assets }
132
+ ```
133
+
134
+ When an emitted custom schema consists entirely of a top-level `many` item, the
135
+ custom schema key identifies the outer list block instead. Its payload contains
136
+ the repeated items directly:
137
+
138
+ ```txt
139
+ schema key: 0x00000001
140
+ schema body: many #asset
141
+ wire value: [0x00000001][length][ASSET][ASSET]...
142
+ ```
143
+
144
+ This convention gives a top-level list a discoverable, context-local type while
145
+ retaining the generic `#list` key for lists whose type is supplied by an
146
+ enclosing schema.
118
147
 
119
148
  ## Endpoint Lanes
120
149
 
@@ -128,33 +157,28 @@ The packed descriptor uses these lane layouts:
128
157
 
129
158
  ```txt
130
159
  state [key:4][group:1]
131
- input [key:4][item:4][group:1]
132
- output [key:4][min:4][max:4][hint:4][group:1]
160
+ input [key:4][group:1]
161
+ output [key:4][min:4][max:4][hint:3][group:1]
162
+ reserve [reserved:4]
163
+ tx [transactions:1]
164
+ flags [flags:1]
133
165
  ```
134
166
 
135
- Containers are exclusive to input. A plain input spec is compressed into
136
- `[spec.key][0]`. A spec with a nonzero container is compressed into
137
- `[spec.container][spec.key]`: the container is the top-level wire key and the
138
- item is its contained block key. The built-in `many(spec)` helper annotates the
139
- spec with `Specs.List` as its container, matching the DSL form `many #item`.
140
- Output lanes retain their size bounds and allocation hint so execution can
141
- reconstruct the output spec and initialize its writer directly. The Solidity
142
- output decoder returns this as a left-aligned, writer-ready spec that retains
143
- its encoded group. Its container and reserved fields are cleared. `Specs.group`
144
- returns the effective group, interpreting an encoded zero as one for a
145
- non-empty spec.
167
+ Each lane directly identifies its top-level block key. Output lanes retain their
168
+ size bounds and allocation hint so execution can reconstruct the output spec and
169
+ initialize its writer directly. Four descriptor-level bytes are reserved after
170
+ the lane metadata. The Solidity output decoder returns a left-aligned,
171
+ writer-ready spec that retains its encoded group and clears its reserved fields.
172
+ `Specs.group` returns the effective group, interpreting an encoded zero as one
173
+ for a non-empty spec.
146
174
 
147
175
  Any non-empty lane resolves its key to a block alias and schema body through the
148
- active schema context. If the item slot is nonzero, tooling also resolves that
149
- item key in the same context. A bare list lane, `[Keys.List][0]`, is incomplete
150
- discovery metadata because it does not say what the list contains; indexers
151
- should reject it for self-describing endpoints.
176
+ active schema context. A top-level list lane uses the key of its emitted custom
177
+ `many` schema; the descriptor treats it like every other direct lane spec.
152
178
 
153
179
  The lane key is the prime item. Prime items may repeat at the top level for
154
- batching. When the lane is `many #item`, the repeated prime item is the generic
155
- LIST block and each LIST payload contains repeated `item` blocks. Later
156
- top-level items are globals for the whole batch and are not counted as
157
- per-operation prime blocks.
180
+ batching. Later top-level items are globals for the whole batch and are not
181
+ counted as per-operation prime blocks.
158
182
 
159
183
  The prime item cannot be optional. If an endpoint needs a per-operation marker
160
184
  with no payload, use a zero-payload block such as `#unit`.
@@ -369,7 +393,7 @@ invalid in any path segment.
369
393
 
370
394
  - `#bytes`: raw dynamic bytes, written without a body
371
395
  - `#string`: UTF-8 string bytes, written without a body
372
- - `#list`: generic list wrapper emitted by `many`
396
+ - `#list`: generic list wrapper emitted by nested `many`
373
397
 
374
398
  Custom input shapes should define their own context-local block spec and publish
375
399
  it with a `#schema` annotation. Endpoint contracts can use `schema(...)` to
@@ -0,0 +1,20 @@
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 an account records an outbound relay reference.
7
+ abstract contract RelayEvent is EventEmitter {
8
+ string private constant ABI = "event Relay(bytes32 indexed account, uint portal, uint resources, bytes32 key, bytes32 digest)";
9
+
10
+ /// @param account Account that owns the relayed context.
11
+ /// @param portal Destination portal identifier, often the destination host ID.
12
+ /// @param resources Chain-specific resources assigned to the relay.
13
+ /// @param key Relay correlation or recovery lookup key.
14
+ /// @param digest Digest of the relayed payload or canonical envelope.
15
+ event Relay(bytes32 indexed account, uint portal, uint resources, bytes32 key, bytes32 digest);
16
+
17
+ constructor() {
18
+ emit EventAbi(ABI);
19
+ }
20
+ }
@@ -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 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
+ }
@@ -0,0 +1,18 @@
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 records a portal message awaiting resolution.
7
+ abstract contract UnresolvedEvent is EventEmitter {
8
+ string private constant ABI = "event Unresolved(uint indexed host, bytes32 key, bytes32 digest)";
9
+
10
+ /// @param host Host node ID that owns the unresolved message.
11
+ /// @param key Resolution lookup key.
12
+ /// @param digest Digest of the unresolved message.
13
+ event Unresolved(uint indexed host, bytes32 key, bytes32 digest);
14
+
15
+ constructor() {
16
+ emit EventAbi(ABI);
17
+ }
18
+ }
@@ -227,7 +227,16 @@ library Executions {
227
227
  /// @param lane Decoder lane containing the LIST block.
228
228
  /// @return items Cursor over the nested list items.
229
229
  function list(Execution memory exec, uint8 lane) internal pure returns (Cur memory items) {
230
- (uint abs, uint end) = consume(exec, lane, Specs.List);
230
+ return list(exec, Specs.List, lane);
231
+ }
232
+
233
+ /// @notice Consume a list block described by `spec` and return a cursor scoped to its payload.
234
+ /// @param exec Execution whose decoder is advanced.
235
+ /// @param spec Custom list block specification.
236
+ /// @param lane Decoder lane containing the list block.
237
+ /// @return items Cursor over the nested list items.
238
+ function list(Execution memory exec, uint spec, uint8 lane) internal pure returns (Cur memory items) {
239
+ (uint abs, uint end) = consume(exec, lane, spec);
231
240
  items.state = Cursors.create(abs, end - abs, 0, 0, 0);
232
241
  }
233
242