@rootzero/contracts 0.8.0 → 0.9.1

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 (82) hide show
  1. package/Commands.sol +17 -23
  2. package/Core.sol +3 -4
  3. package/Cursors.sol +4 -6
  4. package/Events.sol +3 -2
  5. package/Queries.sol +1 -1
  6. package/README.md +18 -19
  7. package/Utils.sol +3 -3
  8. package/blocks/Cursors.sol +1398 -0
  9. package/blocks/Keys.sol +57 -34
  10. package/blocks/Schema.sol +108 -126
  11. package/blocks/Writers.sol +476 -301
  12. package/commands/Base.sol +32 -22
  13. package/commands/Burn.sol +5 -5
  14. package/commands/Credit.sol +8 -8
  15. package/commands/Debit.sol +5 -5
  16. package/commands/Deposit.sol +13 -12
  17. package/commands/Pipe.sol +5 -5
  18. package/commands/Provision.sol +25 -54
  19. package/commands/Transfer.sol +11 -21
  20. package/commands/Withdraw.sol +7 -8
  21. package/commands/{admin → control}/AllowAssets.sol +9 -8
  22. package/commands/control/Allowance.sol +43 -0
  23. package/commands/{admin → control}/Authorize.sol +9 -8
  24. package/commands/{admin → control}/DenyAssets.sol +9 -8
  25. package/commands/{admin → control}/Destroy.sol +8 -7
  26. package/commands/control/Execute.sol +39 -0
  27. package/commands/{admin → control}/Init.sol +8 -7
  28. package/commands/{admin → control}/Unauthorize.sol +9 -8
  29. package/core/Access.sol +40 -36
  30. package/core/Balances.sol +17 -18
  31. package/core/{Operation.sol → Calls.sol} +5 -8
  32. package/core/{CursorBase.sol → Context.sol} +11 -5
  33. package/core/Host.sol +9 -9
  34. package/core/Types.sol +86 -0
  35. package/docs/GETTING_STARTED.md +37 -29
  36. package/events/Asset.sol +1 -1
  37. package/events/Command.sol +10 -10
  38. package/events/Control.sol +31 -0
  39. package/events/Deposit.sol +3 -4
  40. package/events/Listing.sol +1 -1
  41. package/events/Position.sol +21 -0
  42. package/events/Query.sol +5 -5
  43. package/events/Remote.sol +24 -0
  44. package/events/Withdraw.sol +2 -3
  45. package/package.json +1 -1
  46. package/queries/Assets.sol +8 -8
  47. package/queries/Balances.sol +11 -11
  48. package/queries/Base.sol +2 -3
  49. package/queries/Positions.sol +25 -19
  50. package/remote/AllowAssets.sol +39 -0
  51. package/remote/Allowance.sol +36 -0
  52. package/remote/AssetPull.sol +44 -0
  53. package/remote/Base.sol +40 -0
  54. package/remote/DenyAssets.sol +39 -0
  55. package/remote/Settle.sol +33 -0
  56. package/utils/Accounts.sol +14 -13
  57. package/utils/Assets.sol +77 -57
  58. package/utils/Ids.sol +34 -34
  59. package/utils/Layout.sol +4 -4
  60. package/utils/Utils.sol +10 -0
  61. package/blocks/cursors/Core.sol +0 -1121
  62. package/blocks/cursors/Erc1155.sol +0 -149
  63. package/blocks/cursors/Erc20.sol +0 -130
  64. package/blocks/cursors/Erc721.sol +0 -66
  65. package/commands/Create.sol +0 -44
  66. package/commands/Remove.sol +0 -44
  67. package/commands/Settle.sol +0 -38
  68. package/commands/Stake.sol +0 -49
  69. package/commands/Supply.sol +0 -43
  70. package/commands/admin/Allocate.sol +0 -43
  71. package/commands/admin/Relocate.sol +0 -43
  72. package/core/HostBound.sol +0 -14
  73. package/events/Erc721.sol +0 -20
  74. package/events/Peer.sol +0 -24
  75. package/peer/AllowAssets.sol +0 -39
  76. package/peer/AssetPull.sol +0 -44
  77. package/peer/Base.sol +0 -36
  78. package/peer/DenyAssets.sol +0 -39
  79. package/peer/Pull.sol +0 -41
  80. package/peer/Push.sol +0 -47
  81. package/peer/Settle.sol +0 -34
  82. package/utils/State.sol +0 -22
@@ -0,0 +1,36 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {RemoteBase} from "./Base.sol";
5
+ import {AllowanceHook} from "../commands/control/Allowance.sol";
6
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
+
8
+ using Cursors for Cur;
9
+
10
+ string constant NAME = "remoteAllowance";
11
+
12
+ /// @title RemoteAllowance
13
+ /// @notice Remote that lets a trusted remote host request or refresh its own allowance.
14
+ /// Each AMOUNT block in the request is scoped to the remote host and passed to the
15
+ /// shared allowance hook as a host-scoped allowance. Restricted to trusted remotes.
16
+ abstract contract RemoteAllowance is RemoteBase, AllowanceHook {
17
+ uint internal immutable remoteAllowanceId = remoteId(NAME);
18
+
19
+ constructor() {
20
+ emit Remote(host, remoteAllowanceId, NAME, Schemas.Amount, false);
21
+ }
22
+
23
+ /// @notice Execute the allowance remote call.
24
+ function remoteAllowance(bytes calldata request) external onlyRemote returns (bytes memory) {
25
+ (Cur memory amounts, , ) = cursor(request, 1);
26
+ uint remote = caller();
27
+
28
+ while (amounts.i < amounts.bound) {
29
+ (bytes32 asset, bytes32 meta, uint amount) = amounts.unpackAmount();
30
+ allowance(remote, asset, meta, amount);
31
+ }
32
+
33
+ amounts.complete();
34
+ return "";
35
+ }
36
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {RemoteBase} from "./Base.sol";
5
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
6
+
7
+ string constant NAME = "remoteAssetPull";
8
+
9
+ using Cursors for Cur;
10
+
11
+ abstract contract AssetPullHook {
12
+ /// @notice Override to process one incoming amount-based asset pull request from a remote host.
13
+ /// @param remote Remote host node ID for this request.
14
+ /// @param asset Requested asset identifier.
15
+ /// @param meta Requested asset metadata slot.
16
+ /// @param amount Requested amount in the asset's native units.
17
+ function assetPull(uint remote, bytes32 asset, bytes32 meta, uint amount) internal virtual;
18
+ }
19
+
20
+ /// @title RemoteAssetPull
21
+ /// @notice Remote that pulls requested asset amounts from a remote host into this one.
22
+ /// Each AMOUNT block in the request calls `assetPull(remote, asset, meta, amount)`.
23
+ /// Restricted to trusted remotes.
24
+ abstract contract RemoteAssetPull is RemoteBase, AssetPullHook {
25
+ uint internal immutable remoteAssetPullId = remoteId(NAME);
26
+
27
+ constructor() {
28
+ emit Remote(host, remoteAssetPullId, NAME, Schemas.Amount, false);
29
+ }
30
+
31
+ /// @notice Execute the asset-pull remote call.
32
+ function remoteAssetPull(bytes calldata request) external onlyRemote returns (bytes memory) {
33
+ (Cur memory assets, , ) = cursor(request, 1);
34
+ uint remote = caller();
35
+
36
+ while (assets.i < assets.bound) {
37
+ (bytes32 asset, bytes32 meta, uint amount) = assets.unpackAmount();
38
+ assetPull(remote, asset, meta, amount);
39
+ }
40
+
41
+ assets.complete();
42
+ return "";
43
+ }
44
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { NodeCalls } from "../core/Calls.sol";
5
+ import { RemoteEvent } from "../events/Remote.sol";
6
+ import { Ids, Selectors } from "../utils/Ids.sol";
7
+
8
+ /// @notice ABI-encode a remote call from a target remote ID and request block stream.
9
+ /// @dev Derives the function selector from `target` via `Ids.remoteSelector(target)`.
10
+ /// Reverts if `target` is not a valid remote ID.
11
+ /// @param target Destination remote node ID embedding the target selector.
12
+ /// @param request Input block stream for the remote invocation.
13
+ /// @return ABI-encoded calldata for the remote entry point.
14
+ function encodeRemoteCall(uint target, bytes calldata request) pure returns (bytes memory) {
15
+ bytes4 selector = Ids.remoteSelector(target);
16
+ return abi.encodeWithSelector(selector, request);
17
+ }
18
+
19
+ /// @title RemoteBase
20
+ /// @notice Abstract base for all rootzero remote contracts.
21
+ /// Remotes handle inter-host operations and asset allow/deny management
22
+ /// between cooperating hosts. Access is restricted to trusted callers via `onlyRemote`.
23
+ abstract contract RemoteBase is NodeCalls, RemoteEvent {
24
+ /// @dev Thrown when the commander attempts to call a remote entrypoint directly.
25
+ error CommanderNotAllowed();
26
+
27
+ /// @dev Restrict execution to trusted callers, excluding the commander.
28
+ modifier onlyRemote() {
29
+ if (msg.sender == commander) revert CommanderNotAllowed();
30
+ enforceCaller(msg.sender);
31
+ _;
32
+ }
33
+
34
+ /// @notice Derive the deterministic node ID for a named remote on this contract.
35
+ /// @param name Remote function name (without argument list).
36
+ /// @return Remote node ID.
37
+ function remoteId(string memory name) internal view returns (uint) {
38
+ return Ids.toRemote(Selectors.remote(name), address(this));
39
+ }
40
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {RemoteBase} from "./Base.sol";
5
+ import {DenyAssetsHook} from "../commands/control/DenyAssets.sol";
6
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
7
+
8
+ using Cursors for Cur;
9
+
10
+ string constant NAME = "remoteDenyAssets";
11
+
12
+ /// @title RemoteDenyAssets
13
+ /// @notice Remote that blocks a list of (asset, meta) pairs on behalf of a remote host.
14
+ /// Each ASSET block in the request calls `denyAsset`. Restricted to trusted remotes.
15
+ abstract contract RemoteDenyAssets is RemoteBase, DenyAssetsHook {
16
+ uint internal immutable remoteDenyAssetsId = remoteId(NAME);
17
+
18
+ constructor() {
19
+ emit Remote(host, remoteDenyAssetsId, NAME, Schemas.Asset, false);
20
+ }
21
+
22
+ /// @notice Execute the deny-assets remote call.
23
+ function remoteDenyAssets(bytes calldata request) external onlyRemote returns (bytes memory) {
24
+ (Cur memory assets, , ) = cursor(request, 1);
25
+
26
+ while (assets.i < assets.bound) {
27
+ (bytes32 asset, bytes32 meta) = assets.unpackAsset();
28
+ denyAsset(asset, meta);
29
+ }
30
+
31
+ assets.complete();
32
+ return "";
33
+ }
34
+ }
35
+
36
+
37
+
38
+
39
+
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { RemoteBase } from "./Base.sol";
5
+ import { TransferHook } from "../commands/Transfer.sol";
6
+ import { Cursors, Cur, Schemas } from "../Cursors.sol";
7
+
8
+ using Cursors for Cur;
9
+
10
+ string constant NAME = "remoteSettle";
11
+
12
+ /// @title RemoteSettle
13
+ /// @notice Remote that consumes remote-supplied TRANSACTION blocks through the shared transfer hook.
14
+ /// Each TRANSACTION block in the request calls `transfer(value)`. Restricted to trusted remotes.
15
+ abstract contract RemoteSettle is RemoteBase, TransferHook {
16
+ uint internal immutable remoteSettleId = remoteId(NAME);
17
+
18
+ constructor() {
19
+ emit Remote(host, remoteSettleId, NAME, Schemas.Transaction, false);
20
+ }
21
+
22
+ /// @notice Execute the remote-settle call.
23
+ function remoteSettle(bytes calldata request) external onlyRemote returns (bytes memory) {
24
+ (Cur memory state, , ) = cursor(request, 1);
25
+
26
+ while (state.i < state.bound) {
27
+ transfer(state.unpackTxValue());
28
+ }
29
+
30
+ state.complete();
31
+ return "";
32
+ }
33
+ }
@@ -10,7 +10,6 @@ import {isFamily, toLocalBase, toUnspecifiedBase} from "./Utils.sol";
10
10
  /// Account IDs embed a 4-byte type tag in bits [255:224]:
11
11
  /// - `Admin` — chain-local EVM address in bits [191:32]
12
12
  /// - `User` — chain-agnostic EVM address in bits [191:32]
13
- /// - `Keccak` — 28-byte keccak hash of an arbitrary key
14
13
  library Accounts {
15
14
  /// @dev Thrown when an account ID does not belong to the EVM family.
16
15
  error InvalidAccount();
@@ -31,12 +30,21 @@ library Accounts {
31
30
  return uint32(uint(account) >> 224);
32
31
  }
33
32
 
33
+ /// @notice Return true if `account` uses the Account category tag in the type field.
34
+ function isAccount(bytes32 account) internal pure returns (bool) {
35
+ return uint8(uint(account) >> 232) == Layout.Account;
36
+ }
37
+
34
38
  /// @notice Return true if `account` is an admin account.
35
39
  function isAdmin(bytes32 account) internal pure returns (bool) {
36
40
  return prefix(account) == Admin;
37
41
  }
38
42
 
39
- /// @notice Return true if `account` is a keccak account.
43
+ /// @notice Return true if `account` is a user account.
44
+ function isUser(bytes32 account) internal pure returns (bool) {
45
+ return prefix(account) == User;
46
+ }
47
+
40
48
  function isKeccak(bytes32 account) internal pure returns (bool) {
41
49
  return prefix(account) == Keccak;
42
50
  }
@@ -55,19 +63,12 @@ library Accounts {
55
63
  return bytes32(toUnspecifiedBase(User) | (uint(uint160(addr)) << 32));
56
64
  }
57
65
 
58
- /// @notice Encode arbitrary calldata as a keccak account ID.
59
- /// The lower 28 bytes of the ID hold the lower 28 bytes of `keccak256(raw)`.
60
- /// @param raw Arbitrary key bytes to hash.
61
- /// @return Keccak account ID.
62
- function toKeccak(bytes calldata raw) internal pure returns (bytes32) {
63
- return bytes32(toUnspecifiedBase(Keccak) | uint224(uint256(keccak256(raw))));
66
+ function toKeccak(bytes32 head, bytes32 meta) internal pure returns (bytes32) {
67
+ return bytes32(toUnspecifiedBase(Keccak) | uint224(uint256(keccak256(bytes.concat(head, meta)))));
64
68
  }
65
69
 
66
- /// @notice Return true if `account` is the keccak account ID of `raw`.
67
- /// @param account Account ID to compare.
68
- /// @param raw Raw key bytes to hash and compare against.
69
- function matchesKeccak(bytes32 account, bytes calldata raw) internal pure returns (bool) {
70
- return account == toKeccak(raw);
70
+ function matchesKeccak(bytes32 account, bytes32 head, bytes32 meta) internal pure returns (bool) {
71
+ return account == toKeccak(head, meta);
71
72
  }
72
73
 
73
74
  /// @notice Assert that `account` uses the Account layout tag and return it unchanged.
package/utils/Assets.sol CHANGED
@@ -27,14 +27,71 @@ library Assets {
27
27
  /// @dev Full 4-byte type prefix for ERC-1155 assets.
28
28
  uint32 constant Erc1155 = (uint32(Layout.Evm64) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc1155);
29
29
 
30
+ /// @notice Return true if `asset` uses the Asset category tag in the type field.
31
+ function isAsset(bytes32 asset) internal pure returns (bool) {
32
+ return uint8(uint(asset) >> 232) == Layout.Asset;
33
+ }
34
+
30
35
  /// @notice Return true if `asset` uses the 32-byte asset layout with no metadata identity (top byte is `0x20`).
31
36
  function is32(bytes32 asset) internal pure returns (bool) {
32
- return bytes1(asset) == 0x20;
37
+ return isAsset(asset) && bytes1(asset) == 0x20;
33
38
  }
34
39
 
35
40
  /// @notice Return true if `asset` uses the 64-byte asset layout with metadata-backed identity (top byte is `0x40`).
36
41
  function is64(bytes32 asset) internal pure returns (bool) {
37
- return bytes1(asset) == 0x40;
42
+ return isAsset(asset) && bytes1(asset) == 0x40;
43
+ }
44
+
45
+ /// @notice Return true if `asset` is the local native value asset.
46
+ function isValue(bytes32 asset) internal view returns (bool) {
47
+ return asset == toValue();
48
+ }
49
+
50
+ /// @notice Return true if `asset` is a local ERC-20 asset.
51
+ function isErc20(bytes32 asset) internal view returns (bool) {
52
+ return matchesBase(asset, toLocalBase(Erc20));
53
+ }
54
+
55
+ /// @notice Return true if `asset` is a local ERC-721 asset.
56
+ function isErc721(bytes32 asset) internal view returns (bool) {
57
+ return matchesBase(asset, toLocalBase(Erc721));
58
+ }
59
+
60
+ /// @notice Return true if `asset` is a local ERC-1155 asset.
61
+ function isErc1155(bytes32 asset) internal view returns (bool) {
62
+ return matchesBase(asset, toLocalBase(Erc1155));
63
+ }
64
+
65
+ /// @notice Assert that `input` is the local native value asset and return it unchanged.
66
+ /// @param input Asset identifier to validate.
67
+ /// @return asset The same `input` if it is the local native value asset.
68
+ function value(bytes32 input) internal view returns (bytes32 asset) {
69
+ if (!isValue(input)) revert InvalidAsset();
70
+ return input;
71
+ }
72
+
73
+ /// @notice Assert that `input` is a local ERC-20 asset and return it unchanged.
74
+ /// @param input Asset identifier to validate.
75
+ /// @return asset The same `input` if it is a local ERC-20 asset.
76
+ function erc20(bytes32 input) internal view returns (bytes32 asset) {
77
+ if (!isErc20(input)) revert InvalidAsset();
78
+ return input;
79
+ }
80
+
81
+ /// @notice Assert that `input` is a local ERC-721 asset and return it unchanged.
82
+ /// @param input Asset identifier to validate.
83
+ /// @return asset The same `input` if it is a local ERC-721 asset.
84
+ function erc721(bytes32 input) internal view returns (bytes32 asset) {
85
+ if (!isErc721(input)) revert InvalidAsset();
86
+ return input;
87
+ }
88
+
89
+ /// @notice Assert that `input` is a local ERC-1155 asset and return it unchanged.
90
+ /// @param input Asset identifier to validate.
91
+ /// @return asset The same `input` if it is a local ERC-1155 asset.
92
+ function erc1155(bytes32 input) internal view returns (bytes32 asset) {
93
+ if (!isErc1155(input)) revert InvalidAsset();
94
+ return input;
38
95
  }
39
96
 
40
97
  /// @notice Create a chain-local native value asset ID.
@@ -64,52 +121,12 @@ library Assets {
64
121
  return bytes32(toLocalBase(Erc1155) | (uint(uint160(collection)) << 32));
65
122
  }
66
123
 
67
- /// @notice Derive a storage key for an (asset, meta) pair.
68
- /// For 32-byte EVM assets (no meta), the key is the asset ID itself.
69
- /// For assets with metadata (e.g. ERC-721 or ERC-1155 token IDs), the key is
70
- /// `keccak256(asset ++ meta)`.
71
- /// Reverts only if `asset` is zero.
72
- /// For 32-byte assets, `meta` is ignored and does not affect the derived key.
73
- /// @param asset Asset identifier.
74
- /// @param meta Asset metadata slot (e.g. token ID context).
75
- /// @return Storage key for the (asset, meta) combination.
76
- function key(bytes32 asset, bytes32 meta) internal pure returns (bytes32) {
77
- if (asset == 0) revert InvalidAsset();
78
- return bytes1(asset) == 0x20 ? asset : keccak256(bytes.concat(asset, meta));
79
- }
80
-
81
- /// @notice Return true when two local ERC-20 assets are already in canonical token-address order.
82
- /// Useful for pair-style integrations that require a stable token ordering
83
- /// regardless of the caller's input order.
84
- /// Reverts if either asset is not a local ERC-20 asset.
85
- /// @param a First ERC-20 asset identifier.
86
- /// @param b Second ERC-20 asset identifier.
87
- /// @return ordered Whether `a`'s token address is lower than `b`'s token address.
88
- function isSortedErc20(bytes32 a, bytes32 b) internal view returns (bool ordered) {
89
- return erc20Addr(a) < erc20Addr(b);
90
- }
91
-
92
- /// @notice Extract the token addresses for two local ERC-20 assets and report whether they are already ordered.
93
- /// The returned addresses preserve the original input order.
94
- /// Reverts if either asset is not a local ERC-20 asset.
95
- /// @param a First ERC-20 asset identifier.
96
- /// @param b Second ERC-20 asset identifier.
97
- /// @return addrA Token address extracted from `a`.
98
- /// @return addrB Token address extracted from `b`.
99
- /// @return ordered Whether `addrA` is lower than `addrB`.
100
- function erc20Addrs(bytes32 a, bytes32 b) internal view returns (address addrA, address addrB, bool ordered) {
101
- addrA = erc20Addr(a);
102
- addrB = erc20Addr(b);
103
- ordered = addrA < addrB;
104
- }
105
-
106
124
  /// @notice Extract the ERC-20 contract address from an asset ID.
107
125
  /// Reverts if `asset` is not a local ERC-20 asset.
108
126
  /// @param asset ERC-20 asset identifier.
109
127
  /// @return Token contract address embedded in bits [191:32].
110
128
  function erc20Addr(bytes32 asset) internal view returns (address) {
111
- if (!matchesBase(asset, toLocalBase(Erc20))) revert InvalidAsset();
112
- return address(uint160(uint(asset) >> 32));
129
+ return address(uint160(uint(erc20(asset)) >> 32));
113
130
  }
114
131
 
115
132
  /// @notice Assert that `asset` is a local ERC-20 for `token` and return it unchanged.
@@ -127,8 +144,7 @@ library Assets {
127
144
  /// @param asset ERC-721 asset identifier.
128
145
  /// @return Collection contract address embedded in bits [191:32].
129
146
  function erc721Collection(bytes32 asset) internal view returns (address) {
130
- if (!matchesBase(asset, toLocalBase(Erc721))) revert InvalidAsset();
131
- return address(uint160(uint(asset) >> 32));
147
+ return address(uint160(uint(erc721(asset)) >> 32));
132
148
  }
133
149
 
134
150
  /// @notice Assert that `asset` is a local ERC-721 for `collection` and return it unchanged.
@@ -146,8 +162,7 @@ library Assets {
146
162
  /// @param asset ERC-1155 asset identifier.
147
163
  /// @return Collection contract address embedded in bits [191:32].
148
164
  function erc1155Collection(bytes32 asset) internal view returns (address) {
149
- if (!matchesBase(asset, toLocalBase(Erc1155))) revert InvalidAsset();
150
- return address(uint160(uint(asset) >> 32));
165
+ return address(uint160(uint(erc1155(asset)) >> 32));
151
166
  }
152
167
 
153
168
  /// @notice Assert that `asset` is a local ERC-1155 for `collection` and return it unchanged.
@@ -159,6 +174,21 @@ library Assets {
159
174
  if (erc1155Collection(asset) != collection) revert InvalidAsset();
160
175
  return asset;
161
176
  }
177
+
178
+ /// @notice Derive a storage slot for an (asset, meta) pair.
179
+ /// For 32-byte EVM assets (no meta), the slot is the asset ID itself.
180
+ /// For assets with metadata (e.g. ERC-721 or ERC-1155 token IDs), the slot is
181
+ /// `keccak256(asset ++ meta)`.
182
+ /// Reverts only if `asset` is zero.
183
+ /// For 32-byte assets, `meta` is ignored and does not affect the derived slot.
184
+ /// @param asset Asset identifier.
185
+ /// @param meta Asset metadata slot (e.g. token ID context).
186
+ /// @return Storage slot for the (asset, meta) combination.
187
+ function slot(bytes32 asset, bytes32 meta) internal pure returns (bytes32) {
188
+ if (is32(asset)) return asset;
189
+ if (meta == 0 || !is64(asset)) revert InvalidAsset();
190
+ return keccak256(bytes.concat(asset, meta));
191
+ }
162
192
  }
163
193
 
164
194
  /// @title Amounts
@@ -191,16 +221,6 @@ library Amounts {
191
221
  return amount;
192
222
  }
193
223
 
194
- /// @notice Assert non-zero amount and derive the storage key for the (asset, meta) pair.
195
- /// @param asset Asset identifier.
196
- /// @param meta Asset metadata slot.
197
- /// @param amount Amount to validate (must be non-zero).
198
- /// @return key_ Storage key from `Assets.key(asset, meta)`.
199
- function ensureKey(bytes32 asset, bytes32 meta, uint amount) internal pure returns (bytes32 key_) {
200
- ensure(amount);
201
- return Assets.key(asset, meta);
202
- }
203
-
204
224
  /// @notice Clamp `available` to `[min, max]`.
205
225
  /// Uses all of `available` if it does not exceed `max`; reverts if the result
206
226
  /// would fall below `min`.
package/utils/Ids.sol CHANGED
@@ -8,9 +8,9 @@ import {isLocalFamily, matchesBase, toLocalBase} from "./Utils.sol";
8
8
  /// @notice Encoding and decoding helpers for 256-bit node identifiers.
9
9
  ///
10
10
  /// Node IDs share a common layout:
11
- /// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `Peer`)
11
+ /// - bits [255:224] — 4-byte type prefix (`Host`, `Command`, or `Remote`)
12
12
  /// - bits [223:192] — current `block.chainid` (makes IDs chain-local)
13
- /// - bits [191:160] — 4-byte ABI selector (commands and peers only)
13
+ /// - bits [191:160] — 4-byte ABI selector (commands and remotes only)
14
14
  /// - bits [159:0] — 160-bit EVM contract address
15
15
  library Ids {
16
16
  /// @dev Thrown when an ID does not match the expected node type or chain.
@@ -22,8 +22,8 @@ library Ids {
22
22
  uint32 constant Host = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Host);
23
23
  /// @dev Full 4-byte type prefix for command nodes.
24
24
  uint32 constant Command = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Command);
25
- /// @dev Full 4-byte type prefix for peer nodes.
26
- uint32 constant Peer = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Peer);
25
+ /// @dev Full 4-byte type prefix for remote nodes.
26
+ uint32 constant Remote = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Remote);
27
27
  /// @dev Full 4-byte type prefix for query nodes.
28
28
  uint32 constant Query = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Node) << 8) | uint32(Layout.Query);
29
29
 
@@ -37,9 +37,9 @@ library Ids {
37
37
  return uint32(id >> 224) == Command;
38
38
  }
39
39
 
40
- /// @notice Return true if `id` is a peer node ID.
41
- function isPeer(uint id) internal pure returns (bool) {
42
- return uint32(id >> 224) == Peer;
40
+ /// @notice Return true if `id` is a remote node ID.
41
+ function isRemote(uint id) internal pure returns (bool) {
42
+ return uint32(id >> 224) == Remote;
43
43
  }
44
44
 
45
45
  /// @notice Return true if `id` is a query node ID.
@@ -63,26 +63,26 @@ library Ids {
63
63
  return bytes4(uint32(id >> 160));
64
64
  }
65
65
 
66
- /// @notice Assert that `id` is a peer ID and return it unchanged.
66
+ /// @notice Assert that `id` is a remote ID and return it unchanged.
67
67
  /// @param id Node ID to validate.
68
- /// @return pid The same `id` value if it is a peer.
69
- function peer(uint id) internal pure returns (uint pid) {
70
- if (!isPeer(id)) revert InvalidId();
68
+ /// @return pid The same `id` value if it is a remote.
69
+ function remote(uint id) internal pure returns (uint pid) {
70
+ if (!isRemote(id)) revert InvalidId();
71
71
  return id;
72
72
  }
73
73
 
74
- /// @notice Assert that `id` is a peer ID and return its embedded ABI selector.
74
+ /// @notice Assert that `id` is a remote ID and return its embedded ABI selector.
75
75
  /// @param id Node ID to validate.
76
- /// @return selector 4-byte peer selector stored in bits [191:160].
77
- function peerSelector(uint id) internal pure returns (bytes4 selector) {
78
- if (!isPeer(id)) revert InvalidId();
76
+ /// @return selector 4-byte remote selector stored in bits [191:160].
77
+ function remoteSelector(uint id) internal pure returns (bytes4 selector) {
78
+ if (!isRemote(id)) revert InvalidId();
79
79
  return bytes4(uint32(id >> 160));
80
80
  }
81
81
 
82
82
  /// @notice Assert that `id` is a query ID and return it unchanged.
83
83
  /// @param id Node ID to validate.
84
- /// @return qid The same `id` value if it is a query.
85
- function query(uint id) internal pure returns (uint qid) {
84
+ /// @return queryId The same `id` value if it is a query.
85
+ function query(uint id) internal pure returns (uint queryId) {
86
86
  if (!isQuery(id)) revert InvalidId();
87
87
  return id;
88
88
  }
@@ -121,12 +121,12 @@ library Ids {
121
121
  return id;
122
122
  }
123
123
 
124
- /// @notice Build a chain-local peer ID for the given selector and contract.
125
- /// @param selector 4-byte ABI selector of the peer entry point.
126
- /// @param target Peer contract address.
127
- /// @return Peer node ID embedding both the selector and address.
128
- function toPeer(bytes4 selector, address target) internal view returns (uint) {
129
- uint id = toLocalBase(Peer) | uint(uint160(target));
124
+ /// @notice Build a chain-local remote ID for the given selector and contract.
125
+ /// @param selector 4-byte ABI selector of the remote entry point.
126
+ /// @param target Remote contract address.
127
+ /// @return Remote node ID embedding both the selector and address.
128
+ function toRemote(bytes4 selector, address target) internal view returns (uint) {
129
+ uint id = toLocalBase(Remote) | uint(uint160(target));
130
130
  id |= uint(uint32(selector)) << 160;
131
131
  return id;
132
132
  }
@@ -143,7 +143,7 @@ library Ids {
143
143
 
144
144
  /// @notice Extract the contract address from any local node ID.
145
145
  /// Reverts if `id` does not belong to the local node family.
146
- /// @param id Node ID (host, command, or peer).
146
+ /// @param id Node ID (host, command, or remote).
147
147
  /// @return Contract address in the lower 160 bits of `id`.
148
148
  function nodeAddr(uint id) internal view returns (address) {
149
149
  if (!isLocalFamily(id, Node)) revert InvalidId();
@@ -161,12 +161,12 @@ library Ids {
161
161
  }
162
162
 
163
163
  /// @title Selectors
164
- /// @notice ABI-selector derivation helpers for command, peer, and query dispatch.
164
+ /// @notice ABI-selector derivation helpers for command, remote, and query dispatch.
165
165
  library Selectors {
166
- /// @dev ABI argument encoding for command entry points: `((uint256,bytes32,bytes,bytes))`.
167
- string constant CommandArgs = "((uint256,bytes32,bytes,bytes))";
168
- /// @dev ABI argument encoding for peer entry points: `(bytes)`.
169
- string constant PeerArgs = "(bytes)";
166
+ /// @dev ABI argument encoding for command entry points: `((bytes32,bytes,bytes))`.
167
+ string constant CommandArgs = "((bytes32,bytes,bytes))";
168
+ /// @dev ABI argument encoding for remote entry points: `(bytes)`.
169
+ string constant RemoteArgs = "(bytes)";
170
170
  /// @dev ABI argument encoding for query entry points: `(bytes)`.
171
171
  string constant QueryArgs = "(bytes)";
172
172
 
@@ -178,12 +178,12 @@ library Selectors {
178
178
  return bytes4(keccak256(bytes.concat(bytes(name), bytes(CommandArgs))));
179
179
  }
180
180
 
181
- /// @notice Derive the 4-byte ABI selector for a named peer.
182
- /// The selector is `keccak256(name ++ PeerArgs)[0:4]`.
183
- /// @param name Peer function name (without arguments).
181
+ /// @notice Derive the 4-byte ABI selector for a named remote.
182
+ /// The selector is `keccak256(name ++ RemoteArgs)[0:4]`.
183
+ /// @param name Remote function name (without arguments).
184
184
  /// @return 4-byte selector.
185
- function peer(string memory name) internal pure returns (bytes4) {
186
- return bytes4(keccak256(bytes.concat(bytes(name), bytes(PeerArgs))));
185
+ function remote(string memory name) internal pure returns (bytes4) {
186
+ return bytes4(keccak256(bytes.concat(bytes(name), bytes(RemoteArgs))));
187
187
  }
188
188
 
189
189
  /// @notice Derive the 4-byte ABI selector for a named query.
package/utils/Layout.sol CHANGED
@@ -26,7 +26,7 @@ library Layout {
26
26
 
27
27
  /// @dev ID encodes an account.
28
28
  uint8 constant Account = 0x01;
29
- /// @dev ID encodes a network node (host, command, or peer).
29
+ /// @dev ID encodes a network node (host, command, or remote).
30
30
  uint8 constant Node = 0x02;
31
31
  /// @dev ID encodes an asset.
32
32
  uint8 constant Asset = 0x03;
@@ -39,7 +39,7 @@ library Layout {
39
39
  uint8 constant Admin = 0x01;
40
40
  /// @dev User account — chain-agnostic, backed by an EVM address.
41
41
  uint8 constant User = 0x02;
42
- /// @dev Keccak account — opaque 28-byte hash of an arbitrary key.
42
+ /// @dev Keccak account — opaque 28-byte keccak commitment.
43
43
  uint8 constant Keccak = 0x03;
44
44
 
45
45
  // -------------------------------------------------------------------------
@@ -50,8 +50,8 @@ library Layout {
50
50
  uint8 constant Host = 0x01;
51
51
  /// @dev Node is a command contract.
52
52
  uint8 constant Command = 0x02;
53
- /// @dev Node is a peer contract.
54
- uint8 constant Peer = 0x03;
53
+ /// @dev Node is a remote contract.
54
+ uint8 constant Remote = 0x03;
55
55
  /// @dev Node is a query contract.
56
56
  uint8 constant Query = 0x04;
57
57
 
package/utils/Utils.sol CHANGED
@@ -92,6 +92,16 @@ function addrOr(address addr, address or) pure returns (address) {
92
92
  return addr == address(0) ? or : addr;
93
93
  }
94
94
 
95
+ /// @notice Convert a signed integer to its 32-byte two's-complement representation.
96
+ function intToBytes32(int value) pure returns (bytes32) {
97
+ return bytes32(uint(value));
98
+ }
99
+
100
+ /// @notice Convert a 32-byte two's-complement representation to a signed integer.
101
+ function bytes32ToInt(bytes32 value) pure returns (int) {
102
+ return int(uint(value));
103
+ }
104
+
95
105
  /// @notice Convert a null-terminated `bytes32` value to a Solidity string.
96
106
  /// Stops at the first zero byte and returns only the meaningful prefix.
97
107
  function bytes32ToString(bytes32 value) pure returns (string memory result) {