@inco/lightning-preview 0.8.0-devnet-3 → 0.8.0-devnet-5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inco/lightning-preview",
3
- "version": "0.8.0-devnet-3",
3
+ "version": "0.8.0-devnet-5",
4
4
  "repository": "https://github.com/Inco-fhevm/inco-monorepo",
5
5
  "files": [
6
6
  "src/",
@@ -14,12 +14,31 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own
14
14
  import {EList} from "./lightning-parts/EList.sol";
15
15
  import {VerifierAddressGetter} from "@inco/lightning/src/lightning-parts/primitives/VerifierAddressGetter.sol";
16
16
 
17
+ /// @title IncoLightningPreview
18
+ /// @notice Extended version of IncoLightning with preview/experimental features
19
+ /// @dev This contract extends the core IncoLightning functionality with additional features
20
+ /// that are still in preview/experimental stage. It uses a diamond-like delegation pattern:
21
+ ///
22
+ /// Architecture:
23
+ /// - Inherits EList for encrypted list operations (preview feature)
24
+ /// - Delegates all other calls to the core IncoLightning contract via fallback
25
+ /// - Shares the same version and salt as the base IncoLightning for consistency
26
+ ///
27
+ /// Call flow:
28
+ /// 1. If the function exists in IncoLightningPreview or EList, it's executed directly
29
+ /// 2. Otherwise, the fallback delegatecalls to IncoLightning for core functionality
30
+ ///
31
+ /// This pattern allows adding new features without modifying the audited core contract.
17
32
  contract IncoLightningPreview is EList, UUPSUpgradeable, Version, OwnableUpgradeable {
18
33
 
34
+ /// @notice Reference to the core IncoLightning contract for delegation
19
35
  IncoLightning private immutable LIGHTNING;
20
36
 
21
- // Note passing in address rather than IncoLightning directly since the contracts mismatch due to different
22
- // remappings
37
+ /// @notice Initializes the preview contract with references to core infrastructure
38
+ /// @dev Uses address type instead of IncoLightning to avoid remapping issues during deployment.
39
+ /// Inherits the same version/salt as the base contract with "Preview" suffix appended.
40
+ /// @param _lightningAddress The deployed IncoLightning contract address
41
+ /// @param _verifierAddress The IncoVerifier contract address for attestation validation
23
42
  constructor(address _lightningAddress, address _verifierAddress)
24
43
  VerifierAddressGetter(_verifierAddress)
25
44
  Version(
@@ -33,18 +52,30 @@ contract IncoLightningPreview is EList, UUPSUpgradeable, Version, OwnableUpgrade
33
52
  LIGHTNING = IncoLightning(_lightningAddress);
34
53
  }
35
54
 
55
+ /// @notice Indicates this contract includes experimental preview features
56
+ /// @dev Can be used by clients to detect if preview features are available
57
+ /// @return Always returns true for IncoLightningPreview
36
58
  function includesPreviewFeatures() public pure returns (bool) {
37
59
  return true;
38
60
  }
39
61
 
62
+ /// @notice Authorizes contract upgrades (restricted to owner only)
63
+ /// @dev Required by UUPSUpgradeable. Only the contract owner can authorize upgrades.
40
64
  function _authorizeUpgrade(address) internal view override {
41
65
  require(msg.sender == owner());
42
66
  }
43
67
 
68
+ /// @notice Initializes the proxy with an owner address
69
+ /// @dev Must be called immediately after proxy deployment. Can only be called once.
70
+ /// @param owner The address that will own this contract and can authorize upgrades
44
71
  function initialize(address owner) public initializer {
45
72
  __Ownable_init(owner);
46
73
  }
47
74
 
75
+ /// @notice Delegates unhandled calls to the core IncoLightning contract
76
+ /// @dev Uses assembly delegatecall for gas efficiency and to properly forward all data.
77
+ /// Any function not defined in IncoLightningPreview or its parents will be
78
+ /// executed on IncoLightning in the context of this contract's storage.
48
79
  fallback() external {
49
80
  // get facet from function selector
50
81
  address logic = address(LIGHTNING);
@@ -10,8 +10,25 @@ import {IIncoVerifier} from "@inco/lightning/src/interfaces/IIncoVerifier.sol";
10
10
  import {IQuoteVerifier} from "@inco/lightning/src/interfaces/automata-interfaces/IQuoteVerifier.sol";
11
11
  import {console} from "forge-std/console.sol";
12
12
 
13
+ /// @title PreviewDeployUtils
14
+ /// @notice Deployment utilities for IncoLightningPreview and its dependencies
15
+ /// @dev Extends DeployUtils to add preview-specific deployment functions.
16
+ /// Uses CreateX for deterministic cross-chain deployments with computed addresses.
17
+ ///
18
+ /// Deployment order:
19
+ /// 1. Deploy IncoLightning implementation (base contract)
20
+ /// 2. Deploy IncoLightningPreview implementation (extends base with preview features)
21
+ /// 3. Deploy proxy pointing to preview implementation
22
+ /// 4. Deploy IncoVerifier with reference to the proxy
13
23
  contract PreviewDeployUtils is DeployUtils {
14
24
 
25
+ /// @notice Deploys IncoLightningPreview with all required infrastructure
26
+ /// @dev Creates the implementation contracts and proxy in a single transaction.
27
+ /// The verifier address is computed from salt for use before deployment.
28
+ /// @param lightningSalt Salt for deterministic deployment of lightning contracts
29
+ /// @param verifierSalt Salt for deterministic deployment of the verifier
30
+ /// @param deployer Address that will own the deployed proxy
31
+ /// @return lightningPreview The deployed proxy contract cast to IIncoLightningPreview
15
32
  function deployLightningPreview(bytes32 lightningSalt, bytes32 verifierSalt, address deployer)
16
33
  internal
17
34
  returns (IIncoLightningPreview lightningPreview)
@@ -32,6 +49,14 @@ contract PreviewDeployUtils is DeployUtils {
32
49
  );
33
50
  }
34
51
 
52
+ /// @notice Full deployment of IncoLightningPreview using configuration-based salts
53
+ /// @dev Computes salts from deployer and pepper, then deploys both lightning preview
54
+ /// and verifier contracts. Logs the deployment configuration for verification.
55
+ /// @param deployer Address that will own both deployed proxies
56
+ /// @param pepper String used to generate unique deployment salts
57
+ /// @param quoteVerifier The Automata quote verifier for TEE attestation validation
58
+ /// @return lightningPreviewProxy The deployed IncoLightningPreview proxy
59
+ /// @return verifierProxy The deployed IncoVerifier proxy
35
60
  function deployIncoLightningPreviewUsingConfig(address deployer, string memory pepper, IQuoteVerifier quoteVerifier)
36
61
  internal
37
62
  returns (IIncoLightningPreview lightningPreviewProxy, IIncoVerifier verifierProxy)
@@ -10,6 +10,11 @@ import {EListHandleGeneration} from "./primitives/EListHandleGeneration.sol";
10
10
  import {IEList} from "./IEList.sol";
11
11
  import {elist, ListTypeMismatch, InvalidRange} from "../TypesPreview.sol";
12
12
 
13
+ /// @title EList
14
+ /// @notice Provides operations for encrypted lists (elist) - ordered collections of encrypted values.
15
+ /// @dev Encrypted lists are immutable; all operations return new list handles rather than modifying in place.
16
+ /// Lists are homogeneous - all elements must be of the same encrypted type. Operations are processed
17
+ /// by the covalidator off-chain. This is a preview feature and may change in future versions.
13
18
  abstract contract EList is
14
19
  AccessControlListStorage,
15
20
  EListHandleGeneration,
@@ -39,7 +44,17 @@ abstract contract EList is
39
44
  event EListShuffle(elist indexed list, uint256 indexed counter, elist indexed result, uint256 eventId);
40
45
  event EListReverse(elist indexed list, elist indexed result, uint256 eventId);
41
46
 
42
- function newEListFromInputs(bytes[] memory inputs, ETypes listType, address user) internal returns (elist newList) {
47
+ /// @notice Creates a new encrypted list from client-encrypted inputs.
48
+ /// @dev Internal function that processes multiple encrypted inputs without individual payment.
49
+ /// Payment should be handled by the caller for the batch.
50
+ /// @param inputs Array of encrypted inputs with prepended handles.
51
+ /// @param listType The type of elements in the list.
52
+ /// @param user The user address that encrypted the values.
53
+ /// @return newList The new encrypted list handle.
54
+ function newEListFromInputs(bytes[] calldata inputs, ETypes listType, address user)
55
+ internal
56
+ returns (elist newList)
57
+ {
43
58
  require(isTypeSupported(listType), UnsupportedType(listType));
44
59
 
45
60
  // TODO: Add a new event to create new elist from inputs, can be done as an upgrade to optimize for gas and castore.
@@ -51,6 +66,11 @@ abstract contract EList is
51
66
  return newEListFromHandles(handles, listType);
52
67
  }
53
68
 
69
+ /// @notice Creates a new encrypted list from existing encrypted handles.
70
+ /// @dev Validates that all handles are of the expected type and caller has access.
71
+ /// @param handles Array of encrypted value handles to include in the list.
72
+ /// @param listType The type of elements in the list (must match handle types).
73
+ /// @return newList The new encrypted list handle.
54
74
  function newEListFromHandles(bytes32[] memory handles, ETypes listType) internal returns (elist newList) {
55
75
  require(isTypeSupported(listType), UnsupportedType(listType));
56
76
  for (uint256 i = 0; i < handles.length; i++) {
@@ -66,11 +86,22 @@ abstract contract EList is
66
86
  return elist.wrap(newHandle);
67
87
  }
68
88
 
89
+ /// @notice Creates a new encrypted list from existing encrypted handles.
90
+ /// @dev External wrapper for newEListFromHandles.
91
+ /// @param handles Array of encrypted value handles to include in the list.
92
+ /// @param listType The type of elements in the list.
93
+ /// @return newList The new encrypted list handle.
69
94
  function newEList(bytes32[] memory handles, ETypes listType) external returns (elist newList) {
70
95
  return newEListFromHandles(handles, listType);
71
96
  }
72
97
 
73
- function newEList(bytes[] memory inputs, ETypes listType, address user)
98
+ /// @notice Creates a new encrypted list from client-encrypted inputs.
99
+ /// @dev This is a paid operation. Payment scales with the number of inputs.
100
+ /// @param inputs Array of encrypted inputs with prepended handles.
101
+ /// @param listType The type of elements in the list.
102
+ /// @param user The user address that encrypted the values.
103
+ /// @return newList The new encrypted list handle.
104
+ function newEList(bytes[] calldata inputs, ETypes listType, address user)
74
105
  external
75
106
  payable
76
107
  payingMultiple(inputs.length)
@@ -79,10 +110,19 @@ abstract contract EList is
79
110
  return newEListFromInputs(inputs, listType, user);
80
111
  }
81
112
 
113
+ /// @notice Returns the element type of an encrypted list handle.
114
+ /// @dev Extracts the type from the handle's metadata bytes.
115
+ /// @param handle The list handle to inspect.
116
+ /// @return The ETypes enum value representing the element type.
82
117
  function elementTypeOf(bytes32 handle) internal pure returns (ETypes) {
83
118
  return ETypes(uint8(uint256(handle) >> 16));
84
119
  }
85
120
 
121
+ /// @notice Appends an encrypted value to the end of an encrypted list.
122
+ /// @dev Returns a new list with the value appended; original list is unchanged.
123
+ /// @param list The encrypted list to append to.
124
+ /// @param value The encrypted value to append (must match list element type).
125
+ /// @return result A new encrypted list with the value appended.
86
126
  function listAppend(elist list, bytes32 value) external returns (elist result) {
87
127
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
88
128
  checkInput(value, typeToBitMask(listTypeOf(elist.unwrap(list))));
@@ -99,6 +139,11 @@ abstract contract EList is
99
139
  emit EListAppend(list, value, result, getNewEventId());
100
140
  }
101
141
 
142
+ /// @notice Retrieves an encrypted element at a specific index.
143
+ /// @dev Reverts if the index is out of range. For safe access with a default, use listGetOr.
144
+ /// @param list The encrypted list to access.
145
+ /// @param i The index to retrieve (0-based).
146
+ /// @return result The encrypted element at the specified index.
102
147
  function listGet(elist list, uint16 i) external returns (bytes32 result) {
103
148
  require(i < lengthOf(elist.unwrap(list)), IndexOutOfRange(i, lengthOf(elist.unwrap(list))));
104
149
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
@@ -108,6 +153,12 @@ abstract contract EList is
108
153
  emit EListGet(list, i, result, getNewEventId());
109
154
  }
110
155
 
156
+ /// @notice Retrieves an encrypted element at an encrypted index, with a default value for out-of-range access.
157
+ /// @dev Returns the default value if the index is out of range. Index must be euint256.
158
+ /// @param list The encrypted list to access.
159
+ /// @param index The encrypted index to retrieve.
160
+ /// @param defaultValue The encrypted value to return if index is out of range.
161
+ /// @return result The encrypted element at the index, or defaultValue if out of range.
111
162
  function listGetOr(elist list, bytes32 index, bytes32 defaultValue) external returns (bytes32 result) {
112
163
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
113
164
  checkInput(defaultValue, typeToBitMask(listTypeOf(elist.unwrap(list))));
@@ -119,6 +170,13 @@ abstract contract EList is
119
170
  emit EListGetOr(list, index, defaultValue, result, getNewEventId());
120
171
  }
121
172
 
173
+ /// @notice Sets an encrypted element at an encrypted index.
174
+ /// @dev Returns a new list with the element replaced; original list is unchanged.
175
+ /// Index must be euint256. Out-of-range behavior is handled by the covalidator.
176
+ /// @param list The encrypted list to modify.
177
+ /// @param index The encrypted index to set.
178
+ /// @param value The new encrypted value (must match list element type).
179
+ /// @return result A new encrypted list with the element replaced.
122
180
  function listSet(elist list, bytes32 index, bytes32 value) external returns (elist result) {
123
181
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
124
182
  checkInput(index, typeToBitMask(ETypes.Uint256)); //Currently we only support euint256 for index
@@ -136,6 +194,12 @@ abstract contract EList is
136
194
  emit EListSet(list, index, value, result, getNewEventId());
137
195
  }
138
196
 
197
+ /// @notice Inserts an encrypted element at an encrypted index, shifting subsequent elements.
198
+ /// @dev Returns a new list with one additional element. Index must be euint256.
199
+ /// @param list The encrypted list to modify.
200
+ /// @param index The encrypted index at which to insert.
201
+ /// @param value The encrypted value to insert (must match list element type).
202
+ /// @return result A new encrypted list with the element inserted.
139
203
  function listInsert(elist list, bytes32 index, bytes32 value) external returns (elist result) {
140
204
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
141
205
  checkInput(index, typeToBitMask(ETypes.Uint256)); //Currently we only support euint256 for index
@@ -153,6 +217,11 @@ abstract contract EList is
153
217
  emit EListInsert(list, index, value, result, getNewEventId());
154
218
  }
155
219
 
220
+ /// @notice Concatenates two encrypted lists into a new list.
221
+ /// @dev Both lists must have the same element type. Returns a new list with all elements.
222
+ /// @param lhs The first encrypted list.
223
+ /// @param rhs The second encrypted list (must have same element type as lhs).
224
+ /// @return result A new encrypted list containing all elements from both lists.
156
225
  function listConcat(elist lhs, elist rhs) external returns (elist result) {
157
226
  checkInput(elist.unwrap(lhs), typeToBitMask(ETypes.List));
158
227
  checkInput(elist.unwrap(rhs), typeToBitMask(ETypes.List));
@@ -172,6 +241,13 @@ abstract contract EList is
172
241
  emit EListConcat(lhs, rhs, result, getNewEventId());
173
242
  }
174
243
 
244
+ /// @notice Extracts a slice from an encrypted list starting at an encrypted index.
245
+ /// @dev Returns a new list of the specified length. Uses defaultValue for out-of-range positions.
246
+ /// @param list The encrypted list to slice.
247
+ /// @param start The encrypted starting index.
248
+ /// @param len The number of elements to include in the slice.
249
+ /// @param defaultValue The encrypted value to use for out-of-range positions.
250
+ /// @return result A new encrypted list containing the slice.
175
251
  function listSlice(elist list, bytes32 start, uint16 len, bytes32 defaultValue) external returns (elist result) {
176
252
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
177
253
  checkInput(defaultValue, typeToBitMask(listTypeOf(elist.unwrap(list))));
@@ -189,6 +265,11 @@ abstract contract EList is
189
265
  emit EListSlice(list, start, len, defaultValue, result, getNewEventId());
190
266
  }
191
267
 
268
+ /// @notice Creates an encrypted list containing a range of encrypted integers.
269
+ /// @dev Creates a list of euint256 values from start (inclusive) to end (exclusive).
270
+ /// @param start The starting value (inclusive).
271
+ /// @param end The ending value (exclusive). Must be >= start.
272
+ /// @return result A new encrypted list containing the range [start, end).
192
273
  function listRange(uint16 start, uint16 end) external returns (elist result) {
193
274
  require(start <= end, InvalidRange(start, end));
194
275
 
@@ -199,6 +280,11 @@ abstract contract EList is
199
280
  emit EListRange(start, end, result, getNewEventId());
200
281
  }
201
282
 
283
+ /// @notice Randomly shuffles the elements of an encrypted list.
284
+ /// @dev This is a paid operation. Returns a new list with elements in random order.
285
+ /// The shuffle is cryptographically secure, computed by the covalidator.
286
+ /// @param list The encrypted list to shuffle.
287
+ /// @return result A new encrypted list with elements in random order.
202
288
  function listShuffle(elist list) external payable paying returns (elist result) {
203
289
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
204
290
  randCounter++;
@@ -214,6 +300,10 @@ abstract contract EList is
214
300
  emit EListShuffle(list, randCounter, result, getNewEventId());
215
301
  }
216
302
 
303
+ /// @notice Reverses the order of elements in an encrypted list.
304
+ /// @dev Returns a new list with elements in reverse order; original list is unchanged.
305
+ /// @param list The encrypted list to reverse.
306
+ /// @return result A new encrypted list with elements in reverse order.
217
307
  function listReverse(elist list) external returns (elist result) {
218
308
  checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
219
309
 
@@ -8,7 +8,7 @@ interface IEList is IEListHandleMetadata {
8
8
 
9
9
  function newEList(bytes32[] memory handles, ETypes listType) external returns (elist newList);
10
10
 
11
- function newEList(bytes[] memory inputs, ETypes listType, address user) external payable returns (elist newList);
11
+ function newEList(bytes[] calldata inputs, ETypes listType, address user) external payable returns (elist newList);
12
12
 
13
13
  function listAppend(elist list, bytes32 value) external returns (elist result);
14
14
 
@@ -5,8 +5,27 @@ import {ETypes, EOps} from "@inco/lightning/src/Types.sol";
5
5
  import {HandleGeneration} from "@inco/lightning/src/lightning-parts/primitives/HandleGeneration.sol";
6
6
  import {EListHandleMetadata} from "./EListHandleMetadata.sol";
7
7
 
8
+ /// @title EListHandleGeneration
9
+ /// @notice Generates deterministic handles for encrypted list operations
10
+ /// @dev Extends the base HandleGeneration with list-specific handle creation.
11
+ /// List handles incorporate additional metadata:
12
+ /// - List length (number of elements)
13
+ /// - Element type (encrypted type of individual elements)
14
+ /// - List marker (ETypes.List to identify as a list)
15
+ ///
16
+ /// The handle is derived from:
17
+ /// - The operation type (EOps.NewEList for list creation)
18
+ /// - The packed input handles (for lists created from existing handles)
8
19
  contract EListHandleGeneration is HandleGeneration, EListHandleMetadata {
9
20
 
21
+ /// @notice Creates a handle for a list operation result
22
+ /// @dev Generates a deterministic handle by hashing the operation and inputs,
23
+ /// then embedding list metadata (length, element type, list marker, version).
24
+ /// @param op The operation that produced this list (e.g., NewEList, Slice)
25
+ /// @param listType The encrypted type of individual list elements (euint8, euint64, etc.)
26
+ /// @param len The number of elements in the resulting list
27
+ /// @param packedInputs ABI-packed representation of the input handles
28
+ /// @return result The deterministic handle for this list
10
29
  function createListResultHandle(EOps op, ETypes listType, uint16 len, bytes memory packedInputs)
11
30
  internal
12
31
  pure
@@ -18,6 +37,13 @@ contract EListHandleGeneration is HandleGeneration, EListHandleMetadata {
18
37
  result = embedTypeVersion(baseHandle, ETypes.List);
19
38
  }
20
39
 
40
+ /// @notice Creates a handle for a new list composed from individual encrypted handles
41
+ /// @dev This is the primary entry point for creating lists from existing encrypted values.
42
+ /// The handle is deterministically derived from the input handles, ensuring the same
43
+ /// inputs always produce the same list handle.
44
+ /// @param handles Array of encrypted value handles to combine into a list
45
+ /// @param listType The encrypted type of all elements (must be uniform)
46
+ /// @return newHandle The deterministic handle for the new list
21
47
  function createListInputHandle(bytes32[] memory handles, ETypes listType)
22
48
  internal
23
49
  pure
@@ -4,23 +4,55 @@ pragma solidity ^0.8;
4
4
  import {ETypes} from "@inco/lightning/src/Types.sol";
5
5
  import {IEListHandleMetadata} from "./interfaces/IEListHandleMetadata.sol";
6
6
 
7
+ /// @title EListHandleMetadata
8
+ /// @notice Utilities for embedding and extracting metadata from encrypted list handles
9
+ /// @dev Encrypted lists have additional metadata compared to scalar handles:
10
+ /// Handle structure (32 bytes / 256 bits):
11
+ /// - Bytes 0-26: Handle-specific data (hash, counters, etc.)
12
+ /// - Bytes 27-28: List length (uint16, max 65535 elements)
13
+ /// - Byte 29: Element type (ETypes enum value for individual elements)
14
+ /// - Byte 30: List type marker (identifies this as a list handle)
15
+ /// - Byte 31: Handle version
16
+ ///
17
+ /// This allows efficient extraction of list metadata without external calls.
7
18
  contract EListHandleMetadata is IEListHandleMetadata {
8
19
 
20
+ /// @notice Embeds the list length into a list handle
21
+ /// @dev Sets bytes 27-28 of the handle to the list length.
22
+ /// Clears existing length bits before setting new value.
23
+ /// @param prehandle The 32-byte handle before length embedding
24
+ /// @param len The number of elements in the list (max 65535)
25
+ /// @return result The handle with embedded list length
9
26
  function embedListLength(bytes32 prehandle, uint16 len) internal pure returns (bytes32 result) {
10
27
  // 27 and 28 bits are used for the list length
11
28
  result = prehandle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff;
12
29
  result = bytes32(uint256(result) | (uint256(len) << 24)); // append length
13
30
  }
14
31
 
32
+ /// @notice Extracts the list length from a list handle
33
+ /// @dev Reads bytes 27-28 of the handle as a uint16
34
+ /// @param handle The encrypted list handle to inspect
35
+ /// @return The number of elements in the list
15
36
  function lengthOf(bytes32 handle) public pure returns (uint16) {
16
37
  return uint16(uint256(handle) >> 24);
17
38
  }
18
39
 
40
+ /// @notice Embeds the element type into a list handle
41
+ /// @dev Sets byte 29 of the handle to the element type.
42
+ /// This indicates the encrypted type of individual elements (euint8, euint64, etc.).
43
+ /// @param prehandle The 32-byte handle before type embedding
44
+ /// @param listType The encrypted type of list elements
45
+ /// @return result The handle with embedded element type
19
46
  function embedListType(bytes32 prehandle, ETypes listType) internal pure returns (bytes32 result) {
20
47
  result = prehandle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff;
21
48
  result = bytes32(uint256(result) | (uint256(listType) << 16)); // append element type
22
49
  }
23
50
 
51
+ /// @notice Extracts the element type from a list handle
52
+ /// @dev Reads byte 29 of the handle and casts to ETypes enum.
53
+ /// This is the type of individual elements, not the list container type.
54
+ /// @param handle The encrypted list handle to inspect
55
+ /// @return The encrypted type of list elements (euint8, euint64, etc.)
24
56
  function listTypeOf(bytes32 handle) internal pure returns (ETypes) {
25
57
  return ETypes(uint8(uint256(handle) >> 16));
26
58
  }