@rootzero/contracts 1.3.0 → 1.5.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 (62) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/Endpoints.sol +4 -3
  3. package/Events.sol +3 -3
  4. package/README.md +58 -31
  5. package/Utils.sol +4 -3
  6. package/blocks/Cursors.sol +83 -143
  7. package/blocks/Keys.sol +15 -15
  8. package/blocks/Schema.sol +27 -28
  9. package/blocks/Writers.sol +26 -33
  10. package/commands/Base.sol +2 -2
  11. package/commands/Burn.sol +3 -4
  12. package/commands/Credit.sol +3 -4
  13. package/commands/Debit.sol +4 -5
  14. package/commands/Deposit.sol +8 -10
  15. package/commands/Payout.sol +3 -6
  16. package/commands/Withdraw.sol +3 -4
  17. package/commands/admin/AllowAssets.sol +5 -6
  18. package/commands/admin/Allowance.sol +3 -4
  19. package/commands/admin/DenyAssets.sol +5 -6
  20. package/commands/admin/Execute.sol +2 -2
  21. package/core/Access.sol +2 -2
  22. package/core/Balances.sol +10 -11
  23. package/core/Calls.sol +7 -7
  24. package/core/Host.sol +2 -2
  25. package/core/Runtime.sol +3 -3
  26. package/core/Types.sol +0 -14
  27. package/docs/Schema.md +29 -10
  28. package/events/Asset.sol +17 -3
  29. package/events/Balance.sol +2 -3
  30. package/events/Commander.sol +19 -0
  31. package/events/Labeled.sol +6 -6
  32. package/events/Locked.sol +2 -3
  33. package/events/Position.sol +2 -3
  34. package/events/Received.sol +2 -3
  35. package/events/Route.sol +18 -0
  36. package/events/Spent.sol +2 -3
  37. package/events/Unlocked.sol +2 -3
  38. package/guards/Base.sol +4 -4
  39. package/package.json +1 -1
  40. package/peer/AllowAssets.sol +3 -3
  41. package/peer/Allowance.sol +2 -2
  42. package/peer/Base.sol +4 -4
  43. package/peer/Credit.sol +10 -10
  44. package/peer/Debit.sol +10 -10
  45. package/peer/DenyAssets.sol +3 -3
  46. package/peer/Recover.sol +51 -0
  47. package/peer/Redeem.sol +48 -0
  48. package/peer/Settle.sol +3 -3
  49. package/queries/Assets.sol +7 -8
  50. package/queries/Balances.sol +8 -9
  51. package/queries/Base.sol +4 -4
  52. package/queries/Positions.sol +4 -6
  53. package/utils/Accounts.sol +76 -58
  54. package/utils/Actions.sol +1 -0
  55. package/utils/Assets.sol +55 -115
  56. package/utils/Ids.sol +33 -233
  57. package/utils/Layout.sol +11 -17
  58. package/utils/Nodes.sol +263 -0
  59. package/utils/Utils.sol +9 -24
  60. package/events/Chain.sol +0 -19
  61. package/events/Transfer.sol +0 -22
  62. package/peer/BalancePull.sol +0 -49
package/utils/Assets.sol CHANGED
@@ -2,7 +2,8 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import {Layout} from "./Layout.sol";
5
- import {matchesBase, toLocalBase} from "./Utils.sol";
5
+ import {Ids} from "./Ids.sol";
6
+ import {ensureAddr, isFamily, matchesBase, toLocalBase} from "./Utils.sol";
6
7
 
7
8
  /// @title Assets
8
9
  /// @notice Encoding and decoding helpers for 256-bit asset identifiers.
@@ -10,8 +11,12 @@ import {matchesBase, toLocalBase} from "./Utils.sol";
10
11
  /// Asset IDs embed a 4-byte type tag in bits [255:224]:
11
12
  /// - `Native` - native chain coin/token; no address payload
12
13
  /// - `Erc20` - ERC-20 token; contract address in bits [191:32]
13
- /// - `Erc721` - ERC-721 collection; collection address in bits [191:32]
14
- /// - `Erc1155` - ERC-1155 collection; collection address in bits [191:32]
14
+ ///
15
+ /// If the first byte is zero, the asset is an opaque
16
+ /// `0x00 || bytes31(hash)` ID. The full asset metadata must be supplied by
17
+ /// lookup or witness data when native token handling needs it.
18
+ ///
19
+ /// The helpers in this library validate and deconstruct structured asset IDs.
15
20
  ///
16
21
  /// All asset IDs are chain-local (include `block.chainid` in bits [223:192]).
17
22
  library Assets {
@@ -20,28 +25,21 @@ library Assets {
20
25
  /// @dev Thrown when an asset is not authorized for the requested operation.
21
26
  error UnauthorizedAsset();
22
27
 
28
+ /// @dev 24-bit family tag shared by all EVM-backed asset types.
29
+ uint24 constant Family = (uint24(Layout.Evm) << 8) | uint24(Layout.Asset);
23
30
  /// @dev Full 4-byte type prefix for the native chain coin/token asset.
24
- uint32 constant Native = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Native);
31
+ uint32 constant Native = (uint32(Layout.Evm) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Native);
25
32
  /// @dev Full 4-byte type prefix for ERC-20 assets.
26
- uint32 constant Erc20 = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc20);
27
- /// @dev Full 4-byte type prefix for ERC-721 assets.
28
- uint32 constant Erc721 = (uint32(Layout.Evm64) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc721);
29
- /// @dev Full 4-byte type prefix for ERC-1155 assets.
30
- uint32 constant Erc1155 = (uint32(Layout.Evm64) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc1155);
31
-
32
- /// @notice Return true if `asset` uses the Asset category tag in the type field.
33
- function isAsset(bytes32 asset) internal pure returns (bool) {
34
- return uint8(uint(asset) >> 232) == Layout.Asset;
35
- }
33
+ uint32 constant Erc20 = (uint32(Layout.Evm) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc20);
36
34
 
37
- /// @notice Return true if `asset` uses a 32-byte asset layout with no metadata identity.
38
- function is32(bytes32 asset) internal pure returns (bool) {
39
- return isAsset(asset) && uint8(uint(asset) >> 240) == Layout.Width32;
35
+ /// @notice Return true if `asset` belongs to the EVM asset family.
36
+ function isEvm(bytes32 asset) internal pure returns (bool) {
37
+ return isFamily(uint(asset), Family);
40
38
  }
41
39
 
42
- /// @notice Return true if `asset` uses a 64-byte asset layout with metadata-backed identity.
43
- function is64(bytes32 asset) internal pure returns (bool) {
44
- return isAsset(asset) && uint8(uint(asset) >> 240) == Layout.Width64;
40
+ /// @notice Return true if `asset` is opaque.
41
+ function isOpaque(bytes32 asset) internal pure returns (bool) {
42
+ return Ids.isOpaque(asset);
45
43
  }
46
44
 
47
45
  /// @notice Return true if `asset` is the local native chain coin/token asset.
@@ -54,46 +52,36 @@ library Assets {
54
52
  return matchesBase(asset, toLocalBase(Erc20));
55
53
  }
56
54
 
57
- /// @notice Return true if `asset` is a local ERC-721 asset.
58
- function isErc721(bytes32 asset) internal view returns (bool) {
59
- return matchesBase(asset, toLocalBase(Erc721));
60
- }
61
-
62
- /// @notice Return true if `asset` is a local ERC-1155 asset.
63
- function isErc1155(bytes32 asset) internal view returns (bool) {
64
- return matchesBase(asset, toLocalBase(Erc1155));
55
+ /// @notice Assert that `value` belongs to the EVM asset family and return it unchanged.
56
+ /// @param value Asset identifier to validate.
57
+ /// @return asset The same `value` if it is an EVM asset.
58
+ function evm(bytes32 value) internal pure returns (bytes32 asset) {
59
+ if (!isEvm(value)) revert InvalidAsset();
60
+ return value;
65
61
  }
66
62
 
67
- /// @notice Assert that `input` is the local native chain coin/token asset and return it unchanged.
68
- /// @param input Asset identifier to validate.
69
- /// @return asset The same `input` if it is the local native asset.
70
- function native(bytes32 input) internal view returns (bytes32 asset) {
71
- if (!isNative(input)) revert InvalidAsset();
72
- return input;
63
+ /// @notice Assert that `value` is an opaque asset and return it unchanged.
64
+ /// @param value Asset identifier to validate.
65
+ /// @return asset The same `value` if it is opaque.
66
+ function opaque(bytes32 value) internal pure returns (bytes32 asset) {
67
+ if (!Ids.isOpaque(value)) revert InvalidAsset();
68
+ return value;
73
69
  }
74
70
 
75
- /// @notice Assert that `input` is a local ERC-20 asset and return it unchanged.
76
- /// @param input Asset identifier to validate.
77
- /// @return asset The same `input` if it is a local ERC-20 asset.
78
- function erc20(bytes32 input) internal view returns (bytes32 asset) {
79
- if (!isErc20(input)) revert InvalidAsset();
80
- return input;
71
+ /// @notice Assert that `value` is the local native chain coin/token asset and return it unchanged.
72
+ /// @param value Asset identifier to validate.
73
+ /// @return asset The same `value` if it is the local native asset.
74
+ function native(bytes32 value) internal view returns (bytes32 asset) {
75
+ if (!isNative(value)) revert InvalidAsset();
76
+ return value;
81
77
  }
82
78
 
83
- /// @notice Assert that `input` is a local ERC-721 asset and return it unchanged.
84
- /// @param input Asset identifier to validate.
85
- /// @return asset The same `input` if it is a local ERC-721 asset.
86
- function erc721(bytes32 input) internal view returns (bytes32 asset) {
87
- if (!isErc721(input)) revert InvalidAsset();
88
- return input;
89
- }
90
-
91
- /// @notice Assert that `input` is a local ERC-1155 asset and return it unchanged.
92
- /// @param input Asset identifier to validate.
93
- /// @return asset The same `input` if it is a local ERC-1155 asset.
94
- function erc1155(bytes32 input) internal view returns (bytes32 asset) {
95
- if (!isErc1155(input)) revert InvalidAsset();
96
- return input;
79
+ /// @notice Assert that `value` is a local ERC-20 asset and return it unchanged.
80
+ /// @param value Asset identifier to validate.
81
+ /// @return asset The same `value` if it is a local ERC-20 asset.
82
+ function erc20(bytes32 value) internal view returns (bytes32 asset) {
83
+ if (!isErc20(value)) revert InvalidAsset();
84
+ return value;
97
85
  }
98
86
 
99
87
  /// @notice Create a chain-local native coin/token asset ID.
@@ -109,18 +97,20 @@ library Assets {
109
97
  return bytes32(toLocalBase(Erc20) | (uint(uint160(addr)) << 32));
110
98
  }
111
99
 
112
- /// @notice Create a chain-local ERC-721 asset ID for `collection`.
113
- /// @param collection ERC-721 collection contract address.
114
- /// @return Asset ID with `collection` embedded in bits [191:32].
115
- function toErc721(address collection) internal view returns (bytes32) {
116
- return bytes32(toLocalBase(Erc721) | (uint(uint160(collection)) << 32));
100
+ /// @notice Derive an opaque asset ID from a keccak preimage.
101
+ /// @param preimage Preimage whose first byte is `0x01`.
102
+ /// @return asset `0x00 || bytes31(keccak256(preimage))`.
103
+ function toKeccak(bytes memory preimage) internal pure returns (bytes32 asset) {
104
+ return Ids.toKeccak(preimage);
117
105
  }
118
106
 
119
- /// @notice Create a chain-local ERC-1155 asset ID for `collection`.
120
- /// @param collection ERC-1155 collection contract address.
121
- /// @return Asset ID with `collection` embedded in bits [191:32].
122
- function toErc1155(address collection) internal view returns (bytes32) {
123
- return bytes32(toLocalBase(Erc1155) | (uint(uint160(collection)) << 32));
107
+ /// @notice Assert that `asset` matches the opaque keccak ID for `preimage`.
108
+ /// @param asset Opaque asset ID to validate.
109
+ /// @param preimage Preimage whose first byte is `0x01`.
110
+ /// @return The same `asset` value if it matches.
111
+ function matchKeccak(bytes32 asset, bytes memory preimage) internal pure returns (bytes32) {
112
+ if (asset != Ids.toKeccak(preimage)) revert InvalidAsset();
113
+ return asset;
124
114
  }
125
115
 
126
116
  /// @notice Extract the ERC-20 contract address from an asset ID.
@@ -128,7 +118,7 @@ library Assets {
128
118
  /// @param asset ERC-20 asset identifier.
129
119
  /// @return Token contract address embedded in bits [191:32].
130
120
  function erc20Addr(bytes32 asset) internal view returns (address) {
131
- return address(uint160(uint(erc20(asset)) >> 32));
121
+ return ensureAddr(address(uint160(uint(erc20(asset)) >> 32)));
132
122
  }
133
123
 
134
124
  /// @notice Assert that `asset` is a local ERC-20 for `token` and return it unchanged.
@@ -141,56 +131,6 @@ library Assets {
141
131
  return asset;
142
132
  }
143
133
 
144
- /// @notice Extract the ERC-721 collection address from an asset ID.
145
- /// Reverts if `asset` is not a local ERC-721 asset.
146
- /// @param asset ERC-721 asset identifier.
147
- /// @return Collection contract address embedded in bits [191:32].
148
- function erc721Collection(bytes32 asset) internal view returns (address) {
149
- return address(uint160(uint(erc721(asset)) >> 32));
150
- }
151
-
152
- /// @notice Assert that `asset` is a local ERC-721 for `collection` and return it unchanged.
153
- /// Reverts if `asset` is not a local ERC-721 asset or if its collection address differs.
154
- /// @param asset ERC-721 asset identifier.
155
- /// @param collection Expected ERC-721 collection address.
156
- /// @return The same `asset` value if valid.
157
- function matchErc721(bytes32 asset, address collection) internal view returns (bytes32) {
158
- if (erc721Collection(asset) != collection) revert InvalidAsset();
159
- return asset;
160
- }
161
-
162
- /// @notice Extract the ERC-1155 collection address from an asset ID.
163
- /// Reverts if `asset` is not a local ERC-1155 asset.
164
- /// @param asset ERC-1155 asset identifier.
165
- /// @return Collection contract address embedded in bits [191:32].
166
- function erc1155Collection(bytes32 asset) internal view returns (address) {
167
- return address(uint160(uint(erc1155(asset)) >> 32));
168
- }
169
-
170
- /// @notice Assert that `asset` is a local ERC-1155 for `collection` and return it unchanged.
171
- /// Reverts if `asset` is not a local ERC-1155 asset or if its collection address differs.
172
- /// @param asset ERC-1155 asset identifier.
173
- /// @param collection Expected ERC-1155 collection address.
174
- /// @return The same `asset` value if valid.
175
- function matchErc1155(bytes32 asset, address collection) internal view returns (bytes32) {
176
- if (erc1155Collection(asset) != collection) revert InvalidAsset();
177
- return asset;
178
- }
179
-
180
- /// @notice Derive a storage slot for an (asset, meta) pair.
181
- /// For 32-byte assets (no meta), the slot is the asset ID itself.
182
- /// For assets with metadata (e.g. ERC-721 or ERC-1155 token IDs), the slot is
183
- /// `keccak256(asset ++ meta)`.
184
- /// Reverts only if `asset` is zero.
185
- /// For 32-byte assets, `meta` is ignored and does not affect the derived slot.
186
- /// @param asset Asset identifier.
187
- /// @param meta Asset metadata slot (e.g. token ID context).
188
- /// @return Storage slot for the (asset, meta) combination.
189
- function slot(bytes32 asset, bytes32 meta) internal pure returns (bytes32) {
190
- if (is32(asset)) return asset;
191
- if (meta == 0 || !is64(asset)) revert InvalidAsset();
192
- return keccak256(bytes.concat(asset, meta));
193
- }
194
134
  }
195
135
 
196
136
  /// @title Amounts
package/utils/Ids.sol CHANGED
@@ -1,250 +1,50 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Layout} from "./Layout.sol";
5
- import {isLocalFamily, matchesBase, toLocalBase} from "./Utils.sol";
6
-
7
4
  /// @title Ids
8
- /// @notice Encoding and decoding helpers for 256-bit node identifiers.
5
+ /// @notice Shared helpers for the protocol-wide 256-bit ID convention.
6
+ ///
7
+ /// IDs whose first byte is zero are opaque:
8
+ /// `[0x00][bytes31 truncated hash]`
9
9
  ///
10
- /// Node IDs share a common layout:
11
- /// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `Peer`)
12
- /// - bits [223:192] — current `block.chainid` (makes IDs chain-local)
13
- /// - bits [191:160] — 4-byte ABI selector (commands and peers only)
14
- /// - bits [159:0] — 160-bit EVM contract address
10
+ /// Opaque keccak preimages start with `0x01`, and the ID is derived as:
11
+ /// `0x00 || bytes31(keccak256(preimage))`
15
12
  library Ids {
16
- /// @dev Thrown when an ID does not match the expected node type or chain.
13
+ /// @dev Thrown when an ID does not match the expected convention.
17
14
  error InvalidId();
15
+ /// @dev Thrown when an opaque ID preimage is missing or uses an unsupported format/hash tag.
16
+ error InvalidPreimage();
18
17
 
19
- /// @dev 24-bit family tag shared by all node types (Evm32 + Node category).
20
- uint24 constant Node = (uint24(Layout.Evm32) << 8) | uint24(Layout.Node);
21
- /// @dev Full 4-byte type prefix for chain/domain nodes.
22
- uint32 constant Chain = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Chain);
23
- /// @dev Full 4-byte type prefix for host nodes.
24
- uint32 constant Host = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
25
- /// @dev Full 4-byte type prefix for command nodes.
26
- uint32 constant Command = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
27
- /// @dev Full 4-byte type prefix for peer nodes.
28
- uint32 constant Peer = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Peer);
29
- /// @dev Full 4-byte type prefix for query nodes.
30
- uint32 constant Query = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Query);
31
- /// @dev Full 4-byte type prefix for guard action nodes.
32
- uint32 constant Guard = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Guard);
33
-
34
- /// @notice Return true if `id` is a host node ID.
35
- function isHost(uint id) internal pure returns (bool) {
36
- return uint32(id >> 224) == Host;
37
- }
38
-
39
- /// @notice Return true if `id` is a command node ID.
40
- function isCommand(uint id) internal pure returns (bool) {
41
- return uint32(id >> 224) == Command;
42
- }
43
-
44
- /// @notice Return true if `id` is a peer node ID.
45
- function isPeer(uint id) internal pure returns (bool) {
46
- return uint32(id >> 224) == Peer;
47
- }
48
-
49
- /// @notice Return true if `id` is a query node ID.
50
- function isQuery(uint id) internal pure returns (bool) {
51
- return uint32(id >> 224) == Query;
52
- }
53
-
54
- /// @notice Return true if `id` is a guard action node ID.
55
- function isGuard(uint id) internal pure returns (bool) {
56
- return uint32(id >> 224) == Guard;
57
- }
58
-
59
- /// @notice Return true if `id` is a local node ID for this contract.
60
- function isLocalNode(uint id) internal view returns (bool) {
61
- return uint32(id >> 224) != Chain && isLocalFamily(id, Node) && address(uint160(id)) == address(this);
62
- }
63
-
64
- /// @notice Assert that `id` is a command ID and return it unchanged.
65
- /// @param id Node ID to validate.
66
- /// @return cid The same `id` value if it is a command.
67
- function command(uint id) internal pure returns (uint cid) {
68
- if (!isCommand(id)) revert InvalidId();
69
- return id;
70
- }
71
-
72
- /// @notice Assert that `id` is a peer ID and return it unchanged.
73
- /// @param id Node ID to validate.
74
- /// @return pid The same `id` value if it is a peer.
75
- function peer(uint id) internal pure returns (uint pid) {
76
- if (!isPeer(id)) revert InvalidId();
77
- return id;
78
- }
79
-
80
- /// @notice Assert that `id` is a query ID and return it unchanged.
81
- /// @param id Node ID to validate.
82
- /// @return queryId The same `id` value if it is a query.
83
- function query(uint id) internal pure returns (uint queryId) {
84
- if (!isQuery(id)) revert InvalidId();
85
- return id;
86
- }
87
-
88
- /// @notice Assert that `id` is a guard action ID and return it unchanged.
89
- /// @param id Node ID to validate.
90
- /// @return guardId The same `id` value if it is a guard action.
91
- function guard(uint id) internal pure returns (uint guardId) {
92
- if (!isGuard(id)) revert InvalidId();
93
- return id;
94
- }
95
-
96
- /// @notice Assert that `id` is a command ID and return its embedded ABI selector.
97
- /// @param id Node ID to validate.
98
- /// @return selector 4-byte command selector stored in bits [191:160].
99
- function commandSelector(uint id) internal pure returns (bytes4 selector) {
100
- return bytes4(uint32(command(id) >> 160));
101
- }
102
-
103
- /// @notice Assert that `id` is a peer ID and return its embedded ABI selector.
104
- /// @param id Node ID to validate.
105
- /// @return selector 4-byte peer selector stored in bits [191:160].
106
- function peerSelector(uint id) internal pure returns (bytes4 selector) {
107
- return bytes4(uint32(peer(id) >> 160));
108
- }
109
-
110
- /// @notice Assert that `id` is a query ID and return its embedded ABI selector.
111
- /// @param id Node ID to validate.
112
- /// @return selector 4-byte query selector stored in bits [191:160].
113
- function querySelector(uint id) internal pure returns (bytes4 selector) {
114
- return bytes4(uint32(query(id) >> 160));
115
- }
116
-
117
- /// @notice Assert that `id` is a guard action ID and return its embedded ABI selector.
118
- /// @param id Node ID to validate.
119
- /// @return selector 4-byte guard selector stored in bits [191:160].
120
- function guardSelector(uint id) internal pure returns (bytes4 selector) {
121
- return bytes4(uint32(guard(id) >> 160));
122
- }
123
-
124
- /// @notice Assert that `id` is the host ID of `addr` on the current chain.
125
- /// @param id Node ID to validate.
126
- /// @param addr Expected host contract address.
127
- /// @return hid The same `id` value if it matches `addr`.
128
- function matchHost(uint id, address addr) internal view returns (uint hid) {
129
- if (id != toHost(addr)) revert InvalidId();
130
- return id;
131
- }
132
-
133
- /// @notice Build the chain node ID for the current chain.
134
- /// @return Chain node ID with the Chain prefix and `block.chainid` payload.
135
- function localChain() internal view returns (uint) {
136
- uint id = block.chainid;
137
- if (id >> 224 != 0) revert InvalidId();
138
- return (uint(Chain) << 224) | id;
139
- }
140
-
141
- /// @notice Build a chain-local host ID for `target`.
142
- /// @param target Host contract address.
143
- /// @return Host node ID on the current chain.
144
- function toHost(address target) internal view returns (uint) {
145
- return toLocalBase(Host) | uint(uint160(target));
146
- }
147
-
148
- /// @notice Build a chain-local command ID for the given selector and contract.
149
- /// @param selector 4-byte ABI selector of the command entry point.
150
- /// @param target Command contract address.
151
- /// @return Command node ID embedding both the selector and address.
152
- function toCommand(bytes4 selector, address target) internal view returns (uint) {
153
- uint id = toLocalBase(Command) | uint(uint160(target));
154
- id |= uint(uint32(selector)) << 160;
155
- return id;
156
- }
157
-
158
- /// @notice Build a chain-local peer ID for the given selector and contract.
159
- /// @param selector 4-byte ABI selector of the peer entry point.
160
- /// @param target Peer contract address.
161
- /// @return Peer node ID embedding both the selector and address.
162
- function toPeer(bytes4 selector, address target) internal view returns (uint) {
163
- uint id = toLocalBase(Peer) | uint(uint160(target));
164
- id |= uint(uint32(selector)) << 160;
165
- return id;
166
- }
167
-
168
- /// @notice Build a chain-local query ID for the given selector and contract.
169
- /// @param selector 4-byte ABI selector of the query entry point.
170
- /// @param target Query contract address.
171
- /// @return Query node ID embedding both the selector and address.
172
- function toQuery(bytes4 selector, address target) internal view returns (uint) {
173
- uint id = toLocalBase(Query) | uint(uint160(target));
174
- id |= uint(uint32(selector)) << 160;
175
- return id;
176
- }
177
-
178
- /// @notice Build a chain-local guard action ID for the given selector and contract.
179
- /// @param selector 4-byte ABI selector of the guard action entry point.
180
- /// @param target Guard action contract address.
181
- /// @return Guard action node ID embedding both the selector and address.
182
- function toGuard(bytes4 selector, address target) internal view returns (uint) {
183
- uint id = toLocalBase(Guard) | uint(uint160(target));
184
- id |= uint(uint32(selector)) << 160;
185
- return id;
186
- }
187
-
188
- /// @notice Extract the contract address from any local node ID.
189
- /// Reverts if `id` does not belong to the local node family.
190
- /// @param id Node ID (host, command, or peer).
191
- /// @return Contract address in the lower 160 bits of `id`.
192
- function nodeAddr(uint id) internal view returns (address) {
193
- if (uint32(id >> 224) == Chain || !isLocalFamily(id, Node)) revert InvalidId();
194
- return address(uint160(id));
195
- }
196
-
197
- /// @notice Extract the contract address from a local host ID.
198
- /// Reverts if `id` does not match the local host base.
199
- /// @param id Host node ID.
200
- /// @return Host contract address in the lower 160 bits of `id`.
201
- function hostAddr(uint id) internal view returns (address) {
202
- if (!matchesBase(bytes32(id), toLocalBase(Host))) revert InvalidId();
203
- return address(uint160(id));
204
- }
205
- }
206
-
207
- /// @title Selectors
208
- /// @notice ABI-selector derivation helpers for command, peer, and query dispatch.
209
- library Selectors {
210
- /// @dev ABI argument encoding for command entry points: `((bytes32,bytes,bytes))`.
211
- string constant CommandArgs = "((bytes32,bytes,bytes))";
212
- /// @dev ABI argument encoding for peer entry points: `(bytes)`.
213
- string constant PeerArgs = "(bytes)";
214
- /// @dev ABI argument encoding for query entry points: `(bytes)`.
215
- string constant QueryArgs = "(bytes)";
216
- /// @dev ABI argument encoding for guard action entry points: `(bytes)`.
217
- string constant GuardArgs = "(bytes)";
18
+ /// @dev Preimage format/hash tag for keccak256-derived opaque IDs.
19
+ uint8 constant Keccak = 0x01;
218
20
 
219
- /// @notice Derive the 4-byte ABI selector for a named command.
220
- /// The selector is `keccak256(name ++ CommandArgs)[0:4]`.
221
- /// @param name Command function name (without arguments).
222
- /// @return 4-byte selector.
223
- function command(string memory name) internal pure returns (bytes4) {
224
- return bytes4(keccak256(bytes.concat(bytes(name), bytes(CommandArgs))));
21
+ /// @notice Return true if `id` is opaque.
22
+ function isOpaque(bytes32 id) internal pure returns (bool) {
23
+ return uint8(uint(id) >> 248) == 0;
225
24
  }
226
25
 
227
- /// @notice Derive the 4-byte ABI selector for a named peer.
228
- /// The selector is `keccak256(name ++ PeerArgs)[0:4]`.
229
- /// @param name Peer function name (without arguments).
230
- /// @return 4-byte selector.
231
- function peer(string memory name) internal pure returns (bytes4) {
232
- return bytes4(keccak256(bytes.concat(bytes(name), bytes(PeerArgs))));
26
+ /// @notice Assert that `value` is opaque and return it unchanged.
27
+ /// @param value ID to validate.
28
+ /// @return id The same `value` if it is opaque.
29
+ function opaque(bytes32 value) internal pure returns (bytes32 id) {
30
+ if (!isOpaque(value)) revert InvalidId();
31
+ return value;
233
32
  }
234
33
 
235
- /// @notice Derive the 4-byte ABI selector for a named query.
236
- /// The selector is `keccak256(name ++ QueryArgs)[0:4]`.
237
- /// @param name Query function name (without arguments).
238
- /// @return 4-byte selector.
239
- function query(string memory name) internal pure returns (bytes4) {
240
- return bytes4(keccak256(bytes.concat(bytes(name), bytes(QueryArgs))));
34
+ /// @notice Derive an opaque ID from a keccak preimage.
35
+ /// @param preimage Preimage whose first byte is `0x01`.
36
+ /// @return id `0x00 || bytes31(keccak256(preimage))`.
37
+ function toKeccak(bytes memory preimage) internal pure returns (bytes32 id) {
38
+ if (preimage.length == 0 || uint8(preimage[0]) != Keccak) revert InvalidPreimage();
39
+ return bytes32(uint(keccak256(preimage)) >> 8);
241
40
  }
242
41
 
243
- /// @notice Derive the 4-byte ABI selector for a named guard action.
244
- /// The selector is `keccak256(name ++ GuardArgs)[0:4]`.
245
- /// @param name Guard action function name (without arguments).
246
- /// @return 4-byte selector.
247
- function guard(string memory name) internal pure returns (bytes4) {
248
- return bytes4(keccak256(bytes.concat(bytes(name), bytes(GuardArgs))));
42
+ /// @notice Assert that `value` matches the opaque keccak ID for `preimage`.
43
+ /// @param value Opaque ID to validate.
44
+ /// @param preimage Preimage whose first byte is `0x01`.
45
+ /// @return id The same `value` if it matches.
46
+ function matchKeccak(bytes32 value, bytes memory preimage) internal pure returns (bytes32 id) {
47
+ if (value != toKeccak(preimage)) revert InvalidId();
48
+ return value;
249
49
  }
250
50
  }
package/utils/Layout.sol CHANGED
@@ -7,23 +7,21 @@ pragma solidity ^0.8.33;
7
7
  ///
8
8
  /// IDs are structured as:
9
9
  /// `[uint32 type][uint32 chainid][192-bit payload]`
10
- /// where `type` is `[uint8 vm][uint8 width][uint8 category][uint8 subtype]`.
10
+ /// where `type` is `[uint16 representation][uint8 category][uint8 subtype]`.
11
+ ///
12
+ /// Values whose first byte is zero are opaque IDs:
13
+ /// `[0x00][bytes31 truncated hash]`
14
+ /// They require lookup or witness data when the native preimage is needed.
15
+ /// Opaque preimages start with a one-byte format/hash tag. `0x01` means the ID
16
+ /// is `0x00 || bytes31(keccak256(preimage))`; remaining bytes are host/domain-specific.
17
+ /// Values whose first byte is nonzero follow the structured layout above.
11
18
  library Layout {
12
19
  // -------------------------------------------------------------------------
13
- // VM and data-width tags (top 2 bytes of the ID type field)
20
+ // Representation tags (top 2 bytes of the ID type field)
14
21
  // -------------------------------------------------------------------------
15
22
 
16
- /// @dev EVM-compatible value; payloads are built around 20-byte addresses.
17
- uint8 constant Evm = 0x01;
18
- /// @dev 32-byte ID; no paired metadata word is required.
19
- uint8 constant Width32 = 0x20;
20
- /// @dev 64-byte ID; a paired metadata word completes the identity.
21
- uint8 constant Width64 = 0x40;
22
-
23
- /// @dev 32-byte EVM-compatible value; lower 20 bytes hold an address.
24
- uint16 constant Evm32 = (uint16(Evm) << 8) | uint16(Width32);
25
- /// @dev 64-byte EVM-compatible value; used when a paired metadata word completes the identity.
26
- uint16 constant Evm64 = (uint16(Evm) << 8) | uint16(Width64);
23
+ /// @dev EVM-compatible ID; lower 20 payload bytes hold an address when present.
24
+ uint16 constant Evm = 0x0120;
27
25
 
28
26
  // -------------------------------------------------------------------------
29
27
  // Category tags (uint8, third byte of the ID type field)
@@ -71,8 +69,4 @@ library Layout {
71
69
  uint8 constant Native = 0x01;
72
70
  /// @dev ERC-20 fungible token; lower 20 bytes of the ID hold the contract address.
73
71
  uint8 constant Erc20 = 0x02;
74
- /// @dev ERC-721 non-fungible token; lower 20 bytes of the ID hold the collection address.
75
- uint8 constant Erc721 = 0x03;
76
- /// @dev ERC-1155 multi-token asset; lower 20 bytes of the ID hold the collection address.
77
- uint8 constant Erc1155 = 0x04;
78
72
  }