@rootzero/contracts 0.7.2 → 0.8.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.
@@ -10,24 +10,26 @@ using Cursors for Cur;
10
10
  string constant NAME = "isAllowedAsset";
11
11
  string constant OUTPUT = "response(uint allowed)";
12
12
 
13
+ abstract contract IsAllowedAssetHook {
14
+ /// @notice Resolve whether one asset tuple is allowed.
15
+ /// Concrete implementations define the allowlist policy.
16
+ /// @param asset Requested asset identifier.
17
+ /// @param meta Requested asset metadata slot.
18
+ /// @return allowed Whether the asset tuple is allowed.
19
+ function isAllowedAsset(bytes32 asset, bytes32 meta) internal view virtual returns (bool allowed);
20
+ }
21
+
13
22
  /// @title IsAllowedAsset
14
23
  /// @notice Rootzero query that checks whether one or more `(asset, meta)` tuples are allowed.
15
24
  /// The request is a run of `ASSET` blocks.
16
25
  /// The response returns one `RESPONSE` block per query entry, preserving request order.
17
- abstract contract IsAllowedAsset is QueryBase {
26
+ abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
18
27
  uint public immutable isAllowedAssetId = queryId(NAME);
19
28
 
20
29
  constructor() {
21
30
  emit Query(host, NAME, Schemas.Asset, OUTPUT, isAllowedAssetId);
22
31
  }
23
32
 
24
- /// @notice Resolve whether one asset tuple is allowed.
25
- /// Concrete implementations define the allowlist policy.
26
- /// @param asset Requested asset identifier.
27
- /// @param meta Requested asset metadata slot.
28
- /// @return allowed Whether the asset tuple is allowed.
29
- function isAllowedAsset(bytes32 asset, bytes32 meta) internal view virtual returns (bool allowed);
30
-
31
33
  /// @notice Resolve allowlist status for a run of requested `(asset, meta)` tuples.
32
34
  /// @param request Block-stream request consisting of `asset(asset, meta)*`.
33
35
  /// @return Block-stream response containing one `response(allowed)` per asset block.
@@ -9,25 +9,27 @@ using Cursors for Cur;
9
9
  string constant NAME = "getBalances";
10
10
  string constant INPUT = "query(bytes32 account, bytes32 asset, bytes32 meta)";
11
11
 
12
+ abstract contract GetBalancesHook {
13
+ /// @notice Resolve one account's balance for one supported asset.
14
+ /// Concrete implementations define how assets are resolved.
15
+ /// @param account Account identifier carried by the query payload.
16
+ /// @param asset Requested asset identifier.
17
+ /// @param meta Requested asset metadata slot.
18
+ /// @return amount Current balance in the asset's native units.
19
+ function getBalance(bytes32 account, bytes32 asset, bytes32 meta) internal view virtual returns (uint amount);
20
+ }
21
+
12
22
  /// @title GetBalances
13
23
  /// @notice Rootzero query that resolves balances for one or more `(account, asset, meta)` tuples.
14
24
  /// The request is a run of `QUERY` blocks, each encoding `(bytes32 account, bytes32 asset, bytes32 meta)`.
15
25
  /// The response returns one `BALANCE` block per query entry, preserving request order.
16
- abstract contract GetBalances is QueryBase {
26
+ abstract contract GetBalances is QueryBase, GetBalancesHook {
17
27
  uint public immutable getBalancesId = queryId(NAME);
18
28
 
19
29
  constructor() {
20
30
  emit Query(host, NAME, INPUT, Schemas.Balance, getBalancesId);
21
31
  }
22
32
 
23
- /// @notice Resolve one account's balance for one supported asset.
24
- /// Concrete implementations define how assets are resolved.
25
- /// @param account Account identifier carried by the query payload.
26
- /// @param asset Requested asset identifier.
27
- /// @param meta Requested asset metadata slot.
28
- /// @return amount Current balance in the asset's native units.
29
- function getBalance(bytes32 account, bytes32 asset, bytes32 meta) internal view virtual returns (uint amount);
30
-
31
33
  /// @notice Resolve balances for a run of requested `(account, asset, meta)` tuples.
32
34
  /// @param request Block-stream request consisting of `query(account, asset, meta)*`.
33
35
  /// @return Block-stream response containing one `balance(asset, meta, amount)` per query block.
@@ -9,11 +9,21 @@ using Cursors for Cur;
9
9
 
10
10
  string constant NAME = "getAssetPosition";
11
11
 
12
+ abstract contract AssetPositionHook {
13
+ /// @notice Resolve the position payload for one requested asset tuple.
14
+ /// Concrete implementations must append exactly one `RESPONSE` block whose payload
15
+ /// length matches `positionResponseSize`.
16
+ /// @param asset Requested asset identifier.
17
+ /// @param meta Requested asset metadata slot.
18
+ /// @param response Destination writer for the response stream.
19
+ function appendAssetPosition(bytes32 asset, bytes32 meta, Writer memory response) internal view virtual;
20
+ }
21
+
12
22
  /// @title PositionsQuery
13
23
  /// @notice Rootzero query that resolves one dynamic position response for each requested asset tuple.
14
24
  /// The request is a run of `ASSET` blocks.
15
25
  /// The response returns one dynamic `RESPONSE` block per asset entry, preserving request order.
16
- abstract contract AssetPosition is QueryBase {
26
+ abstract contract AssetPosition is QueryBase, AssetPositionHook {
17
27
  uint public immutable getAssetPositionId = queryId(NAME);
18
28
  uint internal immutable positionResponseSize;
19
29
 
@@ -22,14 +32,6 @@ abstract contract AssetPosition is QueryBase {
22
32
  emit Query(host, NAME, Schemas.Asset, output, getAssetPositionId);
23
33
  }
24
34
 
25
- /// @notice Resolve the position payload for one requested asset tuple.
26
- /// Concrete implementations must append exactly one `RESPONSE` block whose payload
27
- /// length matches `positionResponseSize`.
28
- /// @param asset Requested asset identifier.
29
- /// @param meta Requested asset metadata slot.
30
- /// @param response Destination writer for the response stream.
31
- function appendAssetPosition(bytes32 asset, bytes32 meta, Writer memory response) internal view virtual;
32
-
33
35
  /// @notice Resolve positions for a run of requested `(asset, meta)` tuples.
34
36
  /// @dev Allocates from the configured fixed response payload length so each hook call
35
37
  /// can append one `RESPONSE` block directly into the output stream.
package/utils/Assets.sol CHANGED
@@ -10,7 +10,8 @@ import {matchesBase, toLocalBase} from "./Utils.sol";
10
10
  /// Asset IDs embed a 4-byte type tag in bits [255:224]:
11
11
  /// - `Value` — native chain value (ETH); no address payload
12
12
  /// - `Erc20` — ERC-20 token; contract address in bits [191:32]
13
- /// - `Erc721` — ERC-721 collection; issuer 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
  ///
15
16
  /// All asset IDs are chain-local (include `block.chainid` in bits [223:192]).
16
17
  library Assets {
@@ -22,13 +23,20 @@ library Assets {
22
23
  /// @dev Full 4-byte type prefix for ERC-20 assets.
23
24
  uint32 constant Erc20 = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc20);
24
25
  /// @dev Full 4-byte type prefix for ERC-721 assets.
25
- uint32 constant Erc721 = (uint32(Layout.Evm32) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc721);
26
+ uint32 constant Erc721 = (uint32(Layout.Evm64) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc721);
27
+ /// @dev Full 4-byte type prefix for ERC-1155 assets.
28
+ uint32 constant Erc1155 = (uint32(Layout.Evm64) << 16) | (uint32(Layout.Asset) << 8) | uint32(Layout.Erc1155);
26
29
 
27
- /// @notice Return true if `asset` uses the 32-byte EVM layout (top byte is `0x20`).
30
+ /// @notice Return true if `asset` uses the 32-byte asset layout with no metadata identity (top byte is `0x20`).
28
31
  function is32(bytes32 asset) internal pure returns (bool) {
29
32
  return bytes1(asset) == 0x20;
30
33
  }
31
34
 
35
+ /// @notice Return true if `asset` uses the 64-byte asset layout with metadata-backed identity (top byte is `0x40`).
36
+ function is64(bytes32 asset) internal pure returns (bool) {
37
+ return bytes1(asset) == 0x40;
38
+ }
39
+
32
40
  /// @notice Create a chain-local native value asset ID.
33
41
  /// @return Asset ID for the native token on the current chain.
34
42
  function toValue() internal view returns (bytes32) {
@@ -42,23 +50,31 @@ library Assets {
42
50
  return bytes32(toLocalBase(Erc20) | (uint(uint160(addr)) << 32));
43
51
  }
44
52
 
45
- /// @notice Create a chain-local ERC-721 asset ID for `issuer`.
46
- /// @param issuer ERC-721 collection contract address.
47
- /// @return Asset ID with `issuer` embedded in bits [191:32].
48
- function toErc721(address issuer) internal view returns (bytes32) {
49
- return bytes32(toLocalBase(Erc721) | (uint(uint160(issuer)) << 32));
53
+ /// @notice Create a chain-local ERC-721 asset ID for `collection`.
54
+ /// @param collection ERC-721 collection contract address.
55
+ /// @return Asset ID with `collection` embedded in bits [191:32].
56
+ function toErc721(address collection) internal view returns (bytes32) {
57
+ return bytes32(toLocalBase(Erc721) | (uint(uint160(collection)) << 32));
58
+ }
59
+
60
+ /// @notice Create a chain-local ERC-1155 asset ID for `collection`.
61
+ /// @param collection ERC-1155 collection contract address.
62
+ /// @return Asset ID with `collection` embedded in bits [191:32].
63
+ function toErc1155(address collection) internal view returns (bytes32) {
64
+ return bytes32(toLocalBase(Erc1155) | (uint(uint160(collection)) << 32));
50
65
  }
51
66
 
52
67
  /// @notice Derive a storage key for an (asset, meta) pair.
53
68
  /// For 32-byte EVM assets (no meta), the key is the asset ID itself.
54
- /// For assets with metadata (e.g. ERC-721 token IDs), the key is
69
+ /// For assets with metadata (e.g. ERC-721 or ERC-1155 token IDs), the key is
55
70
  /// `keccak256(asset ++ meta)`.
56
- /// Reverts if `asset` is zero, or if it is a 32-byte asset but `meta` is non-zero.
71
+ /// Reverts only if `asset` is zero.
72
+ /// For 32-byte assets, `meta` is ignored and does not affect the derived key.
57
73
  /// @param asset Asset identifier.
58
74
  /// @param meta Asset metadata slot (e.g. token ID context).
59
75
  /// @return Storage key for the (asset, meta) combination.
60
76
  function key(bytes32 asset, bytes32 meta) internal pure returns (bytes32) {
61
- if (asset == 0 || (bytes1(asset) == 0x20 && meta != 0)) revert InvalidAsset();
77
+ if (asset == 0) revert InvalidAsset();
62
78
  return bytes1(asset) == 0x20 ? asset : keccak256(bytes.concat(asset, meta));
63
79
  }
64
80
 
@@ -96,14 +112,53 @@ library Assets {
96
112
  return address(uint160(uint(asset) >> 32));
97
113
  }
98
114
 
99
- /// @notice Extract the ERC-721 issuer address from an asset ID.
115
+ /// @notice Assert that `asset` is a local ERC-20 for `token` and return it unchanged.
116
+ /// Reverts if `asset` is not a local ERC-20 asset or if its token address differs.
117
+ /// @param asset ERC-20 asset identifier.
118
+ /// @param token Expected token contract address.
119
+ /// @return The same `asset` value if valid.
120
+ function matchErc20(bytes32 asset, address token) internal view returns (bytes32) {
121
+ if (erc20Addr(asset) != token) revert InvalidAsset();
122
+ return asset;
123
+ }
124
+
125
+ /// @notice Extract the ERC-721 collection address from an asset ID.
100
126
  /// Reverts if `asset` is not a local ERC-721 asset.
101
127
  /// @param asset ERC-721 asset identifier.
102
- /// @return Issuer contract address embedded in bits [191:32].
103
- function erc721Issuer(bytes32 asset) internal view returns (address) {
128
+ /// @return Collection contract address embedded in bits [191:32].
129
+ function erc721Collection(bytes32 asset) internal view returns (address) {
104
130
  if (!matchesBase(asset, toLocalBase(Erc721))) revert InvalidAsset();
105
131
  return address(uint160(uint(asset) >> 32));
106
132
  }
133
+
134
+ /// @notice Assert that `asset` is a local ERC-721 for `collection` and return it unchanged.
135
+ /// Reverts if `asset` is not a local ERC-721 asset or if its collection address differs.
136
+ /// @param asset ERC-721 asset identifier.
137
+ /// @param collection Expected ERC-721 collection address.
138
+ /// @return The same `asset` value if valid.
139
+ function matchErc721(bytes32 asset, address collection) internal view returns (bytes32) {
140
+ if (erc721Collection(asset) != collection) revert InvalidAsset();
141
+ return asset;
142
+ }
143
+
144
+ /// @notice Extract the ERC-1155 collection address from an asset ID.
145
+ /// Reverts if `asset` is not a local ERC-1155 asset.
146
+ /// @param asset ERC-1155 asset identifier.
147
+ /// @return Collection contract address embedded in bits [191:32].
148
+ function erc1155Collection(bytes32 asset) internal view returns (address) {
149
+ if (!matchesBase(asset, toLocalBase(Erc1155))) revert InvalidAsset();
150
+ return address(uint160(uint(asset) >> 32));
151
+ }
152
+
153
+ /// @notice Assert that `asset` is a local ERC-1155 for `collection` and return it unchanged.
154
+ /// Reverts if `asset` is not a local ERC-1155 asset or if its collection address differs.
155
+ /// @param asset ERC-1155 asset identifier.
156
+ /// @param collection Expected ERC-1155 collection address.
157
+ /// @return The same `asset` value if valid.
158
+ function matchErc1155(bytes32 asset, address collection) internal view returns (bytes32) {
159
+ if (erc1155Collection(asset) != collection) revert InvalidAsset();
160
+ return asset;
161
+ }
107
162
  }
108
163
 
109
164
  /// @title Amounts
package/utils/Ids.sol CHANGED
@@ -95,12 +95,12 @@ library Ids {
95
95
  return bytes4(uint32(id >> 160));
96
96
  }
97
97
 
98
- /// @notice Assert that `id` is the host ID of `target` on the current chain.
98
+ /// @notice Assert that `id` is the host ID of `addr` on the current chain.
99
99
  /// @param id Node ID to validate.
100
- /// @param target Expected host contract address.
101
- /// @return hid The same `id` value if it matches `target`.
102
- function host(uint id, address target) internal view returns (uint hid) {
103
- if (id != toHost(target)) revert InvalidId();
100
+ /// @param addr Expected host contract address.
101
+ /// @return hid The same `id` value if it matches `addr`.
102
+ function matchHost(uint id, address addr) internal view returns (uint hid) {
103
+ if (id != toHost(addr)) revert InvalidId();
104
104
  return id;
105
105
  }
106
106
 
package/utils/Layout.sol CHANGED
@@ -17,7 +17,7 @@ library Layout {
17
17
  uint16 constant Opaque32 = 0x2000;
18
18
  /// @dev 32-byte EVM-compatible value; lower 20 bytes hold an address.
19
19
  uint16 constant Evm32 = 0x2001;
20
- /// @dev 64-byte EVM-compatible value (reserved for extended IDs).
20
+ /// @dev 64-byte EVM-compatible value; used when a paired metadata word completes the identity.
21
21
  uint16 constant Evm64 = 0x4001;
22
22
 
23
23
  // -------------------------------------------------------------------------
@@ -63,6 +63,8 @@ library Layout {
63
63
  uint8 constant Value = 0x01;
64
64
  /// @dev ERC-20 fungible token; lower 20 bytes of the ID hold the contract address.
65
65
  uint8 constant Erc20 = 0x02;
66
- /// @dev ERC-721 non-fungible token; lower 20 bytes of the ID hold the issuer address.
66
+ /// @dev ERC-721 non-fungible token; lower 20 bytes of the ID hold the collection address.
67
67
  uint8 constant Erc721 = 0x03;
68
+ /// @dev ERC-1155 multi-token asset; lower 20 bytes of the ID hold the collection address.
69
+ uint8 constant Erc1155 = 0x04;
68
70
  }