@cofhe/abi 0.2.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @cofhe/abi Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [8fda09a]
8
+ - Updated dependencies [4057a76]
9
+ - Updated dependencies [dba2759]
10
+ - Updated dependencies [e0caeca]
11
+ - Updated dependencies [7c861af]
12
+ - Updated dependencies [2a9d6c5]
13
+ - @cofhe/sdk@0.2.0
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # @cofhe/abi
2
+
3
+ Type-safe ABI utilities for Fhenix fully homomorphic encryption (FHE) smart contracts. Provides type-safe transformations between encrypted input/output types and their underlying primitive values.
4
+
5
+ ## Overview
6
+
7
+ Fhenix contracts use encrypted types (e.g., `euint32`, `ebool`) that are represented as structs in ABIs. This package bridges the gap between:
8
+
9
+ - **Encrypted types** (`struct InEuint32`, `euint32`) - the ABI representation
10
+ - **Primitive types** (`bigint`, `boolean`, `string`) - developer-friendly values
11
+ - **Encryptable types** (`EncryptableItem`) - intermediate encryption format
12
+
13
+ The package provides compile-time type safety through TypeScript generics, ensuring encrypted values are correctly extracted, transformed, and inserted based on ABI definitions.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @cofhe/abi
19
+ # or
20
+ pnpm add @cofhe/abi
21
+ # or
22
+ yarn add @cofhe/abi
23
+ ```
24
+
25
+ **Peer Dependencies:**
26
+
27
+ - `@cofhe/sdk` - Core encryption types and utilities
28
+ - `abitype` - ABI type utilities
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import { extractEncryptableValues, insertEncryptedValues, transformEncryptedReturnTypes } from '@cofhe/abi';
34
+ import type { CofheInputArgs, CofheInputArgsPreTransform, CofheReturnType } from '@cofhe/abi';
35
+ import { encrypt } from '@cofhe/sdk';
36
+
37
+ const abi = [
38
+ {
39
+ type: 'function',
40
+ name: 'add',
41
+ inputs: [
42
+ { name: 'a', type: 'uint256', internalType: 'uint256' },
43
+ { name: 'b', type: 'tuple', internalType: 'struct InEuint32', components: [...] }
44
+ ],
45
+ outputs: [{ name: '', type: 'uint256', internalType: 'euint32' }],
46
+ stateMutability: 'nonpayable'
47
+ }
48
+ ] as const;
49
+
50
+ // 1. Prepare arguments with primitive values
51
+ const args: CofheInputArgsPreTransform<typeof abi, 'add'> = [100n, 200n];
52
+
53
+ // 2. Extract encryptable values
54
+ const encryptables = extractEncryptableValues(abi, 'add', args);
55
+ // ^? [Encryptable.uint32(200n)]
56
+
57
+ // 3. Encrypt the values
58
+ const encrypted = await encrypt(encryptables);
59
+ // ^? [EncryptedUint32Input]
60
+
61
+ // 4. Insert encrypted values back into arguments
62
+ const encryptedArgs: CofheInputArgs<typeof abi, 'add'> = insertEncryptedValues(abi, 'add', args, encrypted);
63
+ // ^? [100n, EncryptedUint32Input]
64
+
65
+ // 5. Call contract and transform return value
66
+ const result = await contract.add(...encryptedArgs);
67
+ // ^? bigint
68
+
69
+ // 6. Transform raw return value into correct encrypted value type
70
+ const transformed: CofheReturnType<typeof abi, 'add'> = transformEncryptedReturnTypes(abi, 'add', result);
71
+ // ^? Euint32
72
+ ```
73
+
74
+ ## Public API Reference
75
+
76
+ ### Return Types
77
+
78
+ #### `CofheReturnType<abi, functionName, args?>`
79
+
80
+ Type-level utility that infers the return type of a function, transforming encrypted return types to their typed representations.
81
+
82
+ **Type Parameters:**
83
+
84
+ - `abi` - Contract ABI (must be `const`-asserted for type inference)
85
+ - `functionName` - Function name (string literal)
86
+ - `args` - Optional function arguments for overload disambiguation
87
+
88
+ **Returns:**
89
+
90
+ - Transformed return type where encrypted types (`euint32`, `ebool`, etc.) are converted to typed objects (`{ ctHash: bigint, utype: FheTypes }`)
91
+ - Non-encrypted types remain unchanged
92
+ - Supports single values, tuples, arrays, and nested structures
93
+
94
+ **Supported Encrypted Return Types:**
95
+
96
+ - `ebool` → `{ ctHash: bigint; utype: FheTypes.Bool }`
97
+ - `euint8` → `{ ctHash: bigint; utype: FheTypes.Uint8 }`
98
+ - `euint16` → `{ ctHash: bigint; utype: FheTypes.Uint16 }`
99
+ - `euint32` → `{ ctHash: bigint; utype: FheTypes.Uint32 }`
100
+ - `euint64` → `{ ctHash: bigint; utype: FheTypes.Uint64 }`
101
+ - `euint128` → `{ ctHash: bigint; utype: FheTypes.Uint128 }`
102
+ - `eaddress` → `{ ctHash: bigint; utype: FheTypes.Uint160 }`
103
+
104
+ #### `transformEncryptedReturnTypes(abi, functionName, data)`
105
+
106
+ Runtime function that transforms contract return values from `bigint` ciphertext hashes to typed encrypted return objects.
107
+
108
+ **Parameters:**
109
+
110
+ - `abi` - Contract ABI
111
+ - `functionName` - Function name
112
+ - `data` - Raw return value(s) from contract call (single value or array)
113
+
114
+ **Returns:**
115
+ Transformed return value matching `CofheReturnType<abi, functionName>`
116
+ Works with multiple return values, nested structures and arrays.
117
+
118
+ ### Encrypted Inputs
119
+
120
+ #### `CofheInputArgs<abi, functionName>`
121
+
122
+ Type-level utility that infers function input arguments with encrypted types represented as encrypted input structs.
123
+
124
+ **Type Parameters:**
125
+
126
+ - `abi` - Contract ABI (must be `const`-asserted for type inference)
127
+ - `functionName` - Function name (string literal)
128
+
129
+ **Returns:**
130
+
131
+ - Tuple type where encrypted inputs are represented as encrypted input structs (`EncryptedUint32Input`, `EncryptedBoolInput`, etc.)
132
+ - Non-encrypted types use their primitive representations
133
+
134
+ #### `CofheInputArgsPreTransform<abi, functionName>`
135
+
136
+ Type-level utility that infers function input arguments with encrypted types represented as their underlying primitive values (before encryption).
137
+
138
+ **Type Parameters:**
139
+
140
+ - `abi` - Contract ABI (must be `const`-asserted for type inference)
141
+ - `functionName` - Function name (string literal)
142
+
143
+ **Returns:**
144
+
145
+ - Tuple type where encrypted inputs use primitive types (`bigint` for uints, `boolean` for bool, `string` for address)
146
+ - This is the format you provide to `extractEncryptableValues`
147
+
148
+ **Supported Encrypted Input Types:**
149
+
150
+ - `struct InEbool` → `boolean` (pre-transform) → `EncryptedBoolInput` (post-transform)
151
+ - `struct InEuint8` → `bigint | string` → `EncryptedUint8Input`
152
+ - `struct InEuint16` → `bigint | string` → `EncryptedUint16Input`
153
+ - `struct InEuint32` → `bigint | string` → `EncryptedUint32Input`
154
+ - `struct InEuint64` → `bigint | string` → `EncryptedUint64Input`
155
+ - `struct InEuint128` → `bigint | string` → `EncryptedUint128Input`
156
+ - `struct InEaddress` → `string | bigint` → `EncryptedAddressInput`
157
+
158
+ #### `extractEncryptableValues(abi, functionName, args)`
159
+
160
+ Extracts encryptable values from function arguments, converting primitive values to `EncryptableItem` objects ready for encryption.
161
+
162
+ **Parameters:**
163
+
164
+ - `abi` - Contract ABI
165
+ - `functionName` - Function name
166
+ - `args` - Function arguments in `CofheInputArgsPreTransform` format (primitives)
167
+
168
+ **Returns:**
169
+ Flat array of `EncryptableItem` objects in the order they appear in the ABI (depth-first traversal).
170
+ Works with arrays (fixed length or unbounded) and nested structures.
171
+
172
+ #### `insertEncryptedValues(abi, functionName, args, encryptedValues)`
173
+
174
+ Re-inserts encrypted values back into function arguments, replacing primitive values with encrypted input structs.
175
+
176
+ **Parameters:**
177
+
178
+ - `abi` - Contract ABI
179
+ - `functionName` - Function name
180
+ - `args` - Original function arguments in `CofheInputArgsPreTransform` format
181
+ - `encryptedValues` - Array of encrypted values in the same order as returned by `extractEncryptableValues`
182
+
183
+ **Returns:**
184
+ Function arguments in `CofheInputArgs` format (ready for contract calls)
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@cofhe/abi",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "description": "ABI utilities for Fhenix FHE contracts",
6
+ "types": "./dist/index.d.ts",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "sideEffects": false,
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/FhenixProtocol/cofhesdk.git",
21
+ "directory": "packages/abi"
22
+ },
23
+ "files": [
24
+ "dist/**",
25
+ "src/**",
26
+ "CHANGELOG.md"
27
+ ],
28
+ "dependencies": {
29
+ "abitype": "^1.2.3",
30
+ "@cofhe/sdk": "0.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.0.0",
34
+ "eslint": "^8.57.0",
35
+ "tsup": "^8.0.2",
36
+ "typescript": "5.5.4",
37
+ "vitest": "^3.0.0",
38
+ "@cofhe/tsconfig": "0.1.1",
39
+ "@cofhe/eslint-config": "0.2.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "dev": "tsup --watch",
47
+ "lint": "eslint \"**/*.ts*\"",
48
+ "type-check": "tsc --noEmit",
49
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest",
52
+ "test:coverage": "vitest run --coverage"
53
+ }
54
+ }
@@ -0,0 +1,451 @@
1
+ import {
2
+ Encryptable,
3
+ type EncryptableAddress,
4
+ type EncryptableBool,
5
+ type EncryptableItem,
6
+ type EncryptableUint128,
7
+ type EncryptableUint16,
8
+ type EncryptableUint32,
9
+ type EncryptableUint64,
10
+ type EncryptableUint8,
11
+ type EncryptedAddressInput,
12
+ type EncryptedBoolInput,
13
+ type EncryptedItemInput,
14
+ type EncryptedUint128Input,
15
+ type EncryptedUint16Input,
16
+ type EncryptedUint32Input,
17
+ type EncryptedUint64Input,
18
+ type EncryptedUint8Input,
19
+ type LiteralToPrimitive,
20
+ type Primitive,
21
+ } from '@cofhe/sdk';
22
+ import type { Abi, AbiFunction, AbiParameter, ExtractAbiFunction } from 'abitype';
23
+ import type { CofheAbiParametersToPrimitiveTypes } from './fhenixMap';
24
+ import { extractArrayParameterType, getAbiFunction, type MaybePartialBy, type ReadonlyWiden } from './utils';
25
+
26
+ export type CofheInputArgs<abi extends Abi | readonly unknown[] = Abi, functionName extends string = string> = GetArgs<
27
+ abi,
28
+ functionName
29
+ >['args'];
30
+
31
+ type EncryptedInputToInputMap<E extends EncryptedItemInput> = E extends EncryptedBoolInput
32
+ ? EncryptableBool['data']
33
+ : E extends EncryptedUint8Input
34
+ ? EncryptableUint8['data']
35
+ : E extends EncryptedUint16Input
36
+ ? EncryptableUint16['data']
37
+ : E extends EncryptedUint32Input
38
+ ? EncryptableUint32['data']
39
+ : E extends EncryptedUint64Input
40
+ ? EncryptableUint64['data']
41
+ : E extends EncryptedUint128Input
42
+ ? EncryptableUint128['data']
43
+ : E extends EncryptedAddressInput
44
+ ? EncryptableAddress['data']
45
+ : never;
46
+
47
+ type EncryptedInputsToInputs<T> = T extends Primitive
48
+ ? LiteralToPrimitive<T>
49
+ : T extends EncryptedItemInput
50
+ ? EncryptedInputToInputMap<T>
51
+ : {
52
+ [K in keyof T]: EncryptedInputsToInputs<T[K]>;
53
+ };
54
+
55
+ export type CofheInputArgsPreTransform<
56
+ abi extends Abi | readonly unknown[] = Abi,
57
+ functionName extends string = string,
58
+ > = EncryptedInputsToInputs<CofheInputArgs<abi, functionName>>;
59
+
60
+ /// GetArgs from abitype (not exported from abitype)
61
+ type GetArgs<
62
+ abi extends Abi | readonly unknown[] = Abi, // `readonly unknown[]` allows for non-const asserted types
63
+ functionName extends string = string,
64
+ args extends readonly unknown[] | undefined = readonly [],
65
+ ///
66
+ abiFunction extends AbiFunction = abi extends Abi ? ExtractAbiFunction<abi, functionName> : AbiFunction,
67
+ primitiveTypes = CofheAbiParametersToPrimitiveTypes<abiFunction['inputs']>,
68
+ args_ =
69
+ | primitiveTypes // show all values
70
+ | (abi extends Abi
71
+ ? args extends primitiveTypes // infer value (if valid)
72
+ ? primitiveTypes extends args // make sure `args` exactly matches `primitiveTypes` (e.g. avoid `args: readonly [{ foo: string; bar: number; }] | readonly [{ foo: string; }]`)
73
+ ? // make inferred value of `args` match `primitiveTypes` (e.g. avoid union `args: readonly [123n] | readonly [bigint]`)
74
+ ReadonlyWiden<args>
75
+ : never
76
+ : never
77
+ : never)
78
+ | (Abi extends abi ? readonly unknown[] : never), // fallback if `abi` is declared as `Abi`
79
+ > = MaybePartialBy<{ args: args_ }, readonly [] extends primitiveTypes ? 'args' : Abi extends abi ? 'args' : string>;
80
+
81
+ // Runtime helper to check if an internalType is an encrypted input type
82
+ const ENCRYPTED_INPUT_INTERNAL_TYPES = [
83
+ 'struct InEbool',
84
+ 'struct InEuint8',
85
+ 'struct InEuint16',
86
+ 'struct InEuint32',
87
+ 'struct InEuint64',
88
+ 'struct InEuint128',
89
+ 'struct InEaddress',
90
+ ] as const;
91
+ type EncryptedInputInternalType = (typeof ENCRYPTED_INPUT_INTERNAL_TYPES)[number];
92
+
93
+ function transformSingleEncryptedInputToEncryptable(
94
+ internalType: EncryptedInputInternalType,
95
+ data: unknown
96
+ ): EncryptableItem {
97
+ switch (internalType) {
98
+ case 'struct InEbool':
99
+ return Encryptable.bool(data as boolean);
100
+ case 'struct InEuint8':
101
+ return Encryptable.uint8(data as string | bigint);
102
+ case 'struct InEuint16':
103
+ return Encryptable.uint16(data as string | bigint);
104
+ case 'struct InEuint32':
105
+ return Encryptable.uint32(data as string | bigint);
106
+ case 'struct InEuint64':
107
+ return Encryptable.uint64(data as string | bigint);
108
+ case 'struct InEuint128':
109
+ return Encryptable.uint128(data as string | bigint);
110
+ case 'struct InEaddress':
111
+ return Encryptable.address(data as string | bigint);
112
+ default:
113
+ throw new Error(`Unknown encrypted input type: ${internalType}`);
114
+ }
115
+ }
116
+
117
+ function transformArrayOfEncryptedInputsToEncryptables(
118
+ internalType: EncryptedInputInternalType,
119
+ size: string | undefined,
120
+ data: unknown
121
+ ): EncryptableItem[] {
122
+ // Ensure data is an array
123
+ if (!Array.isArray(data)) {
124
+ throw new Error('Data must be an array');
125
+ }
126
+
127
+ // Ensure length of fixed length tuple matches the length of the data array
128
+ if (size != null && size !== '' && parseInt(size) !== data.length) {
129
+ throw new Error(`Array size mismatch: ${size} !== ${data.length}`);
130
+ }
131
+
132
+ // Transform each item in the data array into an encryptable item
133
+ return data.map((item) => transformSingleEncryptedInputToEncryptable(internalType, item));
134
+ }
135
+
136
+ // Pathways
137
+ // - type is a tuple && internalType is exactly an encrypted input type (struct InEbool)
138
+ // - - extract the base encrypted type (InEbool) and convert into an encryptable item
139
+ // - type is a tuple && internalType is a tuple of encrypted input types (struct InEuint32[2] / struct InEuint32[])
140
+ // - - extract the base encrypted type and length, and convert into an array of encryptable items
141
+ // - type is a tuple && internalType is not an encrypted input type
142
+ // - - iterate components
143
+ // - type is not a tuple
144
+ // - - throw away
145
+
146
+ function internalTypeIsEncryptedInput(internalType: string): internalType is EncryptedInputInternalType {
147
+ return ENCRYPTED_INPUT_INTERNAL_TYPES.includes(internalType as any);
148
+ }
149
+
150
+ /**
151
+ * Extracts encryptable values from function arguments based on the ABI.
152
+ * Transforms raw data values into EncryptableItem objects that can be passed to the encrypt function.
153
+ *
154
+ * @param abi - The ABI containing the function definition
155
+ * @param functionName - Name of the function
156
+ * @param args - Function arguments in the format of CofheInputArgsPreTransform (raw data values)
157
+ * @returns Array of EncryptableItem objects ready for encryption
158
+ */
159
+ export function extractEncryptableValues<TAbi extends Abi, TFunctionName extends string>(
160
+ abi: TAbi,
161
+ functionName: TFunctionName,
162
+ args: CofheInputArgsPreTransform<TAbi, TFunctionName>
163
+ ): EncryptableItem[] {
164
+ const abiFunction = getAbiFunction(abi, functionName);
165
+ const inputs = abiFunction?.inputs;
166
+ if (abiFunction == null || inputs == null) {
167
+ throw new Error(`Function ${functionName} not found in ABI`);
168
+ }
169
+
170
+ if (!Array.isArray(args)) {
171
+ throw new Error('Arguments must be an array');
172
+ }
173
+
174
+ // Collect encrypted values as EncryptableItem objects in order (flat array)
175
+ const encryptableItems: EncryptableItem[] = [];
176
+
177
+ // Process each parameter - transforms raw values into EncryptableItem objects
178
+ function processParameter(param: AbiParameter, value: unknown): void {
179
+ const [typeHead, typeSize] = extractArrayParameterType(param.type);
180
+ const [internalTypeHead, internalTypeSize] = extractArrayParameterType(param.internalType);
181
+
182
+ /**
183
+ {
184
+ name: 'inNumber',
185
+ type: 'tuple',
186
+ internalType: 'struct InEuint32',
187
+ components: [
188
+ {
189
+ name: 'ctHash',
190
+ type: 'uint256',
191
+ internalType: 'uint256',
192
+ },
193
+ {
194
+ name: 'securityZone',
195
+ type: 'uint8',
196
+ internalType: 'uint8',
197
+ },
198
+ {
199
+ name: 'utype',
200
+ type: 'uint8',
201
+ internalType: 'uint8',
202
+ },
203
+ {
204
+ name: 'signature',
205
+ type: 'bytes',
206
+ internalType: 'bytes',
207
+ },
208
+ ],
209
+ }
210
+ */
211
+ if (
212
+ typeHead === 'tuple' &&
213
+ typeSize == null &&
214
+ internalTypeHead != null &&
215
+ internalTypeIsEncryptedInput(internalTypeHead)
216
+ ) {
217
+ const encryptable = transformSingleEncryptedInputToEncryptable(internalTypeHead, value);
218
+ encryptableItems.push(encryptable);
219
+ return;
220
+ }
221
+
222
+ /**
223
+ {
224
+ name: 'inEuint32Array',
225
+ type: 'tuple[2]',
226
+ internalType: 'struct InEuint32[2]',
227
+ components: [
228
+ {
229
+ name: 'ctHash',
230
+ type: 'uint256',
231
+ internalType: 'uint256',
232
+ },
233
+ {
234
+ name: 'securityZone',
235
+ type: 'uint8',
236
+ internalType: 'uint8',
237
+ },
238
+ {
239
+ name: 'utype',
240
+ type: 'uint8',
241
+ internalType: 'uint8',
242
+ },
243
+ {
244
+ name: 'signature',
245
+ type: 'bytes',
246
+ internalType: 'bytes',
247
+ },
248
+ ],
249
+ }
250
+ */
251
+ if (
252
+ typeHead === 'tuple' &&
253
+ typeSize != null &&
254
+ internalTypeHead != null &&
255
+ internalTypeIsEncryptedInput(internalTypeHead)
256
+ ) {
257
+ const encryptables = transformArrayOfEncryptedInputsToEncryptables(internalTypeHead, typeSize, value);
258
+ encryptableItems.push(...encryptables);
259
+ return;
260
+ }
261
+
262
+ /**
263
+ {
264
+ name: 'containsEncryptedInput',
265
+ type: 'tuple',
266
+ internalType: 'struct ABITest.ContainsEncryptedInput',
267
+ components: [
268
+ {
269
+ name: 'value',
270
+ type: 'uint256',
271
+ internalType: 'uint256',
272
+ },
273
+ {
274
+ name: 'encryptedInput',
275
+ type: 'tuple',
276
+ internalType: 'struct InEuint32',
277
+ components: [
278
+ {
279
+ name: 'ctHash',
280
+ type: 'uint256',
281
+ internalType: 'uint256',
282
+ },
283
+ {
284
+ name: 'securityZone',
285
+ type: 'uint8',
286
+ internalType: 'uint8',
287
+ },
288
+ {
289
+ name: 'utype',
290
+ type: 'uint8',
291
+ internalType: 'uint8',
292
+ },
293
+ {
294
+ name: 'signature',
295
+ type: 'bytes',
296
+ internalType: 'bytes',
297
+ },
298
+ ],
299
+ },
300
+ ],
301
+ }
302
+ */
303
+ if (typeHead === 'tuple' && (internalTypeHead == null || !internalTypeIsEncryptedInput(internalTypeHead))) {
304
+ if ('components' in param && Array.isArray(param.components)) {
305
+ param.components.forEach((component) => {
306
+ processParameter(component, (value as Record<string, unknown>)[component.name]);
307
+ });
308
+ }
309
+ return;
310
+ }
311
+
312
+ // param.type is not a tuple, so cannot be an encrypted input or contain encrypted inputs
313
+ return;
314
+ }
315
+
316
+ // Process all inputs
317
+ inputs.forEach((input, index) => {
318
+ const arg = args[index];
319
+ if (arg == null) {
320
+ throw new Error(`Argument ${index} is undefined`);
321
+ }
322
+ processParameter(input, arg);
323
+ });
324
+
325
+ return encryptableItems;
326
+ }
327
+
328
+ /**
329
+ * Re-inserts encrypted values back into function arguments based on the ABI.
330
+ * Takes the extracted values (now encrypted) and the original args structure, and replaces
331
+ * the encryptable values with their encrypted counterparts.
332
+ *
333
+ * This function mirrors the extraction logic in extractEncryptableValues, ensuring values
334
+ * are inserted in the exact same order and locations where they were extracted.
335
+ *
336
+ * @param abi - The ABI containing the function definition
337
+ * @param functionName - Name of the function
338
+ * @param args - Original function arguments in the format of CofheInputArgsPreTransform
339
+ * @param encryptedValues - Encrypted values in the same order as returned by extractEncryptableValues
340
+ * @returns Function arguments with encrypted values inserted (format of CofheInputArgs)
341
+ */
342
+ export function insertEncryptedValues<TAbi extends Abi, TFunctionName extends string>(
343
+ abi: TAbi,
344
+ functionName: TFunctionName,
345
+ args: CofheInputArgsPreTransform<TAbi, TFunctionName>,
346
+ encryptedValues: readonly EncryptedItemInput[]
347
+ ): CofheInputArgs<TAbi, TFunctionName> {
348
+ const abiFunction = getAbiFunction(abi, functionName);
349
+ const inputs = abiFunction?.inputs;
350
+ if (abiFunction == null || inputs == null) {
351
+ throw new Error(`Function ${functionName} not found in ABI`);
352
+ }
353
+
354
+ if (!Array.isArray(args)) {
355
+ throw new Error('Arguments must be an array');
356
+ }
357
+
358
+ // Track position in encrypted values array
359
+ let encryptedIndex = 0;
360
+
361
+ // Process each parameter - replaces raw values with encrypted values
362
+ function processParameter(param: AbiParameter, value: unknown): unknown {
363
+ const [typeHead, typeSize] = extractArrayParameterType(param.type);
364
+ const [internalTypeHead, internalTypeSize] = extractArrayParameterType(param.internalType);
365
+
366
+ // Single encrypted input (tuple with encrypted input type)
367
+ if (
368
+ typeHead === 'tuple' &&
369
+ typeSize == null &&
370
+ internalTypeHead != null &&
371
+ internalTypeIsEncryptedInput(internalTypeHead)
372
+ ) {
373
+ if (encryptedIndex >= encryptedValues.length) {
374
+ throw new Error(
375
+ `Not enough encrypted values: expected at least ${encryptedIndex + 1}, got ${encryptedValues.length}`
376
+ );
377
+ }
378
+ const encryptedValue = encryptedValues[encryptedIndex];
379
+ encryptedIndex++;
380
+ return encryptedValue;
381
+ }
382
+
383
+ // Array of encrypted inputs (tuple[2] or tuple[] with encrypted input type)
384
+ if (
385
+ typeHead === 'tuple' &&
386
+ typeSize != null &&
387
+ internalTypeHead != null &&
388
+ internalTypeIsEncryptedInput(internalTypeHead)
389
+ ) {
390
+ // Determine how many encrypted values we need
391
+ const arrayLength = Array.isArray(value) ? value.length : 0;
392
+ if (arrayLength === 0) {
393
+ return [];
394
+ }
395
+
396
+ if (encryptedIndex + arrayLength > encryptedValues.length) {
397
+ throw new Error(
398
+ `Not enough encrypted values: expected at least ${encryptedIndex + arrayLength}, got ${encryptedValues.length}`
399
+ );
400
+ }
401
+
402
+ // Extract the required number of encrypted values
403
+ const encryptedArray = encryptedValues.slice(encryptedIndex, encryptedIndex + arrayLength);
404
+ encryptedIndex += arrayLength;
405
+ return encryptedArray;
406
+ }
407
+
408
+ // Tuple recursive case (not encrypted input)
409
+ if (typeHead === 'tuple' && (internalTypeHead == null || !internalTypeIsEncryptedInput(internalTypeHead))) {
410
+ if ('components' in param && Array.isArray(param.components)) {
411
+ const valueObj = value as Record<string, unknown>;
412
+ const result: Record<string, unknown> = {};
413
+
414
+ // Process each component in order
415
+ param.components.forEach((component) => {
416
+ const componentName = component.name;
417
+ if (componentName) {
418
+ const componentValue = valueObj[componentName];
419
+ if (componentValue !== undefined) {
420
+ result[componentName] = processParameter(component, componentValue);
421
+ }
422
+ }
423
+ });
424
+
425
+ return result;
426
+ }
427
+ return value;
428
+ }
429
+
430
+ // Not an encrypted input, return original value
431
+ return value;
432
+ }
433
+
434
+ // Process all inputs in order
435
+ const result = inputs.map((input, index) => {
436
+ const arg = args[index];
437
+ if (arg == null) {
438
+ throw new Error(`Argument ${index} is undefined`);
439
+ }
440
+ return processParameter(input, arg);
441
+ });
442
+
443
+ // Verify we used all encrypted values
444
+ if (encryptedIndex !== encryptedValues.length) {
445
+ throw new Error(
446
+ `Mismatch in encrypted values count: used ${encryptedIndex}, but provided ${encryptedValues.length}`
447
+ );
448
+ }
449
+
450
+ return result as CofheInputArgs<TAbi, TFunctionName>;
451
+ }